1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
|
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file LICENSE.rst or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
FindEnvModules
--------------
.. versionadded:: 3.15
Finds an Environment Modules implementation and provides commands for use in
CMake scripts:
.. code-block:: cmake
find_package(EnvModules [...])
The Environment Modules system is a command-line tool that manages Unix-like
shell environments by dynamically modifying environment variables.
It is commonly used in High-Performance Computing (HPC) environments to
support multiple software versions or configurations.
This module is compatible with the two most common implementations:
* Lua-based Lmod
* TCL-based Environment Modules
This module is primarily intended for setting up compiler and library
environments within a :ref:`CTest Script <CTest Script>` (``ctest -S``).
It may also be used in a :ref:`CMake Script <Script Processing Mode>`
(``cmake -P``).
.. note::
The loaded environment will not persist beyond the end of the calling
process. Do not use this module in CMake project code (such as
``CMakeLists.txt``) to load compiler environments, as the environment
changes will not be available during the build phase. In such a case, load
the desired environment before invoking CMake or the generated build system.
Result Variables
^^^^^^^^^^^^^^^^
This module defines the following variables:
``EnvModules_FOUND``
Boolean indicating whether a compatible Environment Modules framework is
found.
Cache Variables
^^^^^^^^^^^^^^^
The following cache variables may also be set:
``EnvModules_COMMAND``
The path to a low level module command to use.
Hints
^^^^^
This module accepts the following variables before calling the
``find_package(EnvModules)``:
``ENV{MODULESHOME}``
This environment variable is usually set by the Environment Modules
implementation, and can be used as a hint to locate the module command to
execute.
Commands
^^^^^^^^
This module provides the following commands for interacting with the
Environment Modules system, if found:
.. command:: env_module
Executes an arbitrary module command:
.. code-block:: cmake
env_module(<command> <args>...)
env_module(
COMMAND <command> <args>...
[OUTPUT_VARIABLE <out-var>]
[RESULT_VARIABLE <ret-var>]
)
The options are:
``COMMAND <command> <args>...``
The module sub-command and arguments to execute as if passed directly to
the module command in the shell environment.
``OUTPUT_VARIABLE <out-var>``
Stores the standard output of the executed module command in the specified
variable.
``RESULT_VARIABLE <ret-var>``
Stores the return code of the executed module command in the specified
variable.
.. command:: env_module_swap
Swaps one module for another:
.. code-block:: cmake
env_module_swap(
<out-mod>
<in-mod>
[OUTPUT_VARIABLE <out-var>]
[RESULT_VARIABLE <ret-var>]
)
This is functionally equivalent to the ``module swap <out-mod> <in-mod>``
shell command.
The options are:
``OUTPUT_VARIABLE <out-var>``
Stores the standard output of the executed module command in the specified
variable.
``RESULT_VARIABLE <ret-var>``
Stores the return code of the executed module command in the specified
variable.
.. command:: env_module_list
Retrieves the list of currently loaded modules:
.. code-block:: cmake
env_module_list(<out-var>)
This is functionally equivalent to the ``module list`` shell command.
The result is stored in ``<out-var>`` as a properly formatted CMake
:ref:`semicolon-separated list <CMake Language Lists>` variable.
.. command:: env_module_avail
Retrieves the list of available modules:
.. code-block:: cmake
env_module_avail([<mod-prefix>] <out-var>)
This is functionally equivalent to the ``module avail <mod-prefix>`` shell
command. The result is stored in ``<out-var>`` as a properly formatted
CMake :ref:`semicolon-separated list <CMake Language Lists>` variable.
Examples
^^^^^^^^
In the following example, this module is used in a CTest script to configure
the compiler and libraries for a Cray Programming Environment.
After the Environment Modules system is found, the ``env_module()`` command is
used to load the necessary compiler, MPI, and scientific libraries to set up
the build environment. The ``CRAYPE_LINK_TYPE`` environment variable is set
to ``dynamic`` to specify dynamic linking. This instructs the Cray Linux
Environment compiler driver to link against dynamic libraries at runtime,
rather than linking static libraries at compile time. As a result, the
compiler produces dynamically linked executable files.
.. code-block:: cmake
:caption: ``example-script.cmake``
set(CTEST_BUILD_NAME "CrayLinux-CrayPE-Cray-dynamic")
set(CTEST_BUILD_CONFIGURATION Release)
set(CTEST_BUILD_FLAGS "-k -j8")
set(CTEST_CMAKE_GENERATOR "Unix Makefiles")
# ...
find_package(EnvModules REQUIRED)
# Clear all currently loaded Environment Modules to start with a clean state
env_module(purge)
# Load the base module-handling system to use other modules
env_module(load modules)
# Load Cray Programming Environment (Cray PE) support, which manages
# platform-specific optimizations and architecture selection
env_module(load craype)
# Load the Cray programming environment
env_module(load PrgEnv-cray)
# Load settings targeting the Intel Knights Landing (KNL) CPU architecture
env_module(load craype-knl)
# Load the Cray MPI (Message Passing Interface) library, needed for
# distributed computing
env_module(load cray-mpich)
# Load Cray's scientific library package, which includes optimized math
# libraries (like BLAS, LAPACK)
env_module(load cray-libsci)
set(ENV{CRAYPE_LINK_TYPE} dynamic)
# ...
#]=======================================================================]
function(env_module)
if(NOT EnvModules_COMMAND)
message(FATAL_ERROR "Failed to process module command. EnvModules_COMMAND not found")
return()
endif()
set(options)
set(oneValueArgs OUTPUT_VARIABLE RESULT_VARIABLE)
set(multiValueArgs COMMAND)
cmake_parse_arguments(MOD_ARGS
"${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGV}
)
if(NOT MOD_ARGS_COMMAND)
# If no explicit command argument was given, then treat the calling syntax
# as: module(cmd args...)
set(exec_cmd ${ARGV})
else()
set(exec_cmd ${MOD_ARGS_COMMAND})
endif()
if(MOD_ARGS_OUTPUT_VARIABLE)
set(err_var_args ERROR_VARIABLE err_var)
endif()
execute_process(
COMMAND mktemp -t module.cmake.XXXXXXXXXXXX
OUTPUT_VARIABLE tempfile_name
)
string(STRIP "${tempfile_name}" tempfile_name)
# If the $MODULESHOME/init/cmake file exists then assume that the CMake
# "shell" functionality exits
if(EXISTS "$ENV{MODULESHOME}/init/cmake")
execute_process(
COMMAND ${EnvModules_COMMAND} cmake ${exec_cmd}
OUTPUT_FILE ${tempfile_name}
${err_var_args}
RESULT_VARIABLE ret_var
)
else() # fallback to the sh shell and manually convert to CMake
execute_process(
COMMAND ${EnvModules_COMMAND} sh ${exec_cmd}
OUTPUT_VARIABLE out_var
${err_var_args}
RESULT_VARIABLE ret_var
)
endif()
# If we executed successfully then process and cleanup the temp file
if(ret_var EQUAL 0)
# No CMake shell so we need to process the sh output into CMake code
if(NOT EXISTS "$ENV{MODULESHOME}/init/cmake")
file(WRITE ${tempfile_name} "")
string(REPLACE "\n" ";" out_var "${out_var}")
foreach(sh_cmd IN LISTS out_var)
if(sh_cmd MATCHES "^ *unset *([^ ]*)")
set(cmake_cmd "unset(ENV{${CMAKE_MATCH_1}})")
elseif(sh_cmd MATCHES "^ *export *([^ ]*)")
set(cmake_cmd "set(ENV{${CMAKE_MATCH_1}} \"\${${CMAKE_MATCH_1}}\")")
elseif(sh_cmd MATCHES " *([^ =]*) *= *(.*)")
set(var_name "${CMAKE_MATCH_1}")
set(var_value "${CMAKE_MATCH_2}")
if(var_value MATCHES "^\"(.*[^\\])\"")
# If it's in quotes, take the value as is
set(var_value "${CMAKE_MATCH_1}")
else()
# Otherwise, strip trailing spaces
string(REGEX REPLACE "([^\\])? +$" "\\1" var_value "${var_value}")
endif()
string(REPLACE "\\ " " " var_value "${var_value}")
set(cmake_cmd "set(${var_name} \"${var_value}\")")
else()
continue()
endif()
file(APPEND ${tempfile_name} "${cmake_cmd}\n")
endforeach()
endif()
# Process the change in environment variables
include(${tempfile_name})
file(REMOVE ${tempfile_name})
endif()
# Push the output back out to the calling scope
if(MOD_ARGS_OUTPUT_VARIABLE)
set(${MOD_ARGS_OUTPUT_VARIABLE} "${err_var}" PARENT_SCOPE)
endif()
if(MOD_ARGS_RESULT_VARIABLE)
set(${MOD_ARGS_RESULT_VARIABLE} ${ret_var} PARENT_SCOPE)
endif()
endfunction()
#------------------------------------------------------------------------------
function(env_module_swap out_mod in_mod)
set(options)
set(oneValueArgs OUTPUT_VARIABLE RESULT_VARIABLE)
set(multiValueArgs)
cmake_parse_arguments(MOD_ARGS
"${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGV}
)
env_module(COMMAND -t swap ${out_mod} ${in_mod}
OUTPUT_VARIABLE tmp_out
RETURN_VARIABLE tmp_ret
)
if(MOD_ARGS_OUTPUT_VARIABLE)
set(${MOD_ARGS_OUTPUT_VARIABLE} "${tmp_out}" PARENT_SCOPE)
endif()
if(MOD_ARGS_RESULT_VARIABLE)
set(${MOD_ARGS_RESULT_VARIABLE} ${tmp_ret} PARENT_SCOPE)
endif()
endfunction()
#------------------------------------------------------------------------------
function(env_module_list out_var)
env_module(COMMAND -t list OUTPUT_VARIABLE tmp_out)
# Convert output into a CMake list
string(REPLACE "\n" ";" ${out_var} "${tmp_out}")
# Remove title headers and empty entries
list(REMOVE_ITEM ${out_var} "No modules loaded")
if(${out_var})
list(FILTER ${out_var} EXCLUDE REGEX "^(.*:)?$")
endif()
list(FILTER ${out_var} EXCLUDE REGEX "^(.*:)?$")
set(${out_var} ${${out_var}} PARENT_SCOPE)
endfunction()
#------------------------------------------------------------------------------
function(env_module_avail)
if(ARGC EQUAL 1)
set(mod_prefix)
set(out_var ${ARGV0})
elseif(ARGC EQUAL 2)
set(mod_prefix ${ARGV0})
set(out_var ${ARGV1})
else()
message(FATAL_ERROR "Usage: env_module_avail([mod_prefix] out_var)")
endif()
env_module(COMMAND -t avail ${mod_prefix} OUTPUT_VARIABLE tmp_out)
# Convert output into a CMake list
string(REPLACE "\n" ";" tmp_out "${tmp_out}")
set(${out_var})
foreach(MOD IN LISTS tmp_out)
# Remove directory entries and empty values
if(MOD MATCHES "^(.*:)?$")
continue()
endif()
# Convert default modules
if(MOD MATCHES "^(.*)/$" ) # "foo/"
list(APPEND ${out_var} ${CMAKE_MATCH_1})
elseif(MOD MATCHES "^((.*)/.*)\\(default\\)$") # "foo/1.2.3(default)"
list(APPEND ${out_var} ${CMAKE_MATCH_2})
list(APPEND ${out_var} ${CMAKE_MATCH_1})
else()
list(APPEND ${out_var} ${MOD})
endif()
endforeach()
set(${out_var} ${${out_var}} PARENT_SCOPE)
endfunction()
#------------------------------------------------------------------------------
# Make sure we know where the underlying module command is
find_program(EnvModules_COMMAND
NAMES lmod modulecmd
HINTS ENV MODULESHOME
PATH_SUFFIXES libexec
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(EnvModules DEFAULT_MSG EnvModules_COMMAND)
|