1# Distributed under the OSI-approved BSD 3-Clause License. See accompanying 2# file Copyright.txt or https://cmake.org/licensing for details. 3 4#[=======================================================================[.rst: 5FindEnvModules 6-------------- 7 8.. versionadded:: 3.15 9 10Locate an environment module implementation and make commands available to 11CMake scripts to use them. This is compatible with both Lua-based Lmod 12and TCL-based EnvironmentModules. 13 14This module is intended for the use case of setting up the compiler and library 15environment within a :ref:`CTest Script <CTest Script>` (``ctest -S``). It can 16also be used in a :ref:`CMake Script <Script Processing Mode>` (``cmake -P``). 17 18.. note:: 19 20 The loaded environment will not survive past the end of the calling process. 21 Do not use this module in project code (``CMakeLists.txt`` files) to load 22 a compiler environment; it will not be available during the build. Instead 23 load the environment manually before running CMake or using the generated 24 build system. 25 26Example Usage 27^^^^^^^^^^^^^ 28 29.. code-block:: cmake 30 31 set(CTEST_BUILD_NAME "CrayLinux-CrayPE-Cray-dynamic") 32 set(CTEST_BUILD_CONFIGURATION Release) 33 set(CTEST_BUILD_FLAGS "-k -j8") 34 set(CTEST_CMAKE_GENERATOR "Unix Makefiles") 35 36 ... 37 38 find_package(EnvModules REQUIRED) 39 40 env_module(purge) 41 env_module(load modules) 42 env_module(load craype) 43 env_module(load PrgEnv-cray) 44 env_module(load craype-knl) 45 env_module(load cray-mpich) 46 env_module(load cray-libsci) 47 48 set(ENV{CRAYPE_LINK_TYPE} dynamic) 49 50 ... 51 52Result Variables 53^^^^^^^^^^^^^^^^ 54 55This module will set the following variables in your project: 56 57``EnvModules_FOUND`` 58 True if a compatible environment modules framework was found. 59 60Cache Variables 61^^^^^^^^^^^^^^^ 62 63The following cache variable will be set: 64 65``EnvModules_COMMAND`` 66 The low level module command to use. Currently supported 67 implementations are the Lua based Lmod and TCL based EnvironmentModules. 68 69Environment Variables 70^^^^^^^^^^^^^^^^^^^^^ 71 72``ENV{MODULESHOME}`` 73 Usually set by the module environment implementation, used as a hint to 74 locate the module command to execute. 75 76Provided Functions 77^^^^^^^^^^^^^^^^^^ 78 79This defines the following CMake functions for interacting with environment 80modules: 81 82.. command:: env_module 83 84 Execute an aribitrary module command: 85 86 .. code-block:: cmake 87 88 env_module(cmd arg1 ... argN) 89 env_module( 90 COMMAND cmd arg1 ... argN 91 [OUTPUT_VARIABLE <out-var>] 92 [RESULT_VARIABLE <ret-var>] 93 ) 94 95 The options are: 96 97 ``cmd arg1 ... argN`` 98 The module sub-command and arguments to execute as if they were 99 passed directly to the module command in your shell environment. 100 101 ``OUTPUT_VARIABLE <out-var>`` 102 The standard output from executing the module command. 103 104 ``RESULT_VARIABLE <ret-var>`` 105 The return code from executing the module command. 106 107.. command:: env_module_swap 108 109 Swap one module for another: 110 111 .. code-block:: cmake 112 113 env_module_swap(out_mod in_mod 114 [OUTPUT_VARIABLE <out-var>] 115 [RESULT_VARIABLE <ret-var>] 116 ) 117 118 This is functionally equivalent to the ``module swap out_mod in_mod`` shell 119 command. The options are: 120 121 ``OUTPUT_VARIABLE <out-var>`` 122 The standard output from executing the module command. 123 124 ``RESULT_VARIABLE <ret-var>`` 125 The return code from executing the module command. 126 127.. command:: env_module_list 128 129 Retrieve the list of currently loaded modules: 130 131 .. code-block:: cmake 132 133 env_module_list(<out-var>) 134 135 This is functionally equivalent to the ``module list`` shell command. 136 The result is stored in ``<out-var>`` as a properly formatted CMake 137 :ref:`semicolon-separated list <CMake Language Lists>` variable. 138 139.. command:: env_module_avail 140 141 Retrieve the list of available modules: 142 143 .. code-block:: cmake 144 145 env_module_avail([<mod-prefix>] <out-var>) 146 147 This is functionally equivalent to the ``module avail <mod-prefix>`` shell 148 command. The result is stored in ``<out-var>`` as a properly formatted 149 CMake :ref:`semicolon-separated list <CMake Language Lists>` variable. 150 151#]=======================================================================] 152 153function(env_module) 154 if(NOT EnvModules_COMMAND) 155 message(FATAL_ERROR "Failed to process module command. EnvModules_COMMAND not found") 156 return() 157 endif() 158 159 set(options) 160 set(oneValueArgs OUTPUT_VARIABLE RESULT_VARIABLE) 161 set(multiValueArgs COMMAND) 162 cmake_parse_arguments(MOD_ARGS 163 "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGV} 164 ) 165 if(NOT MOD_ARGS_COMMAND) 166 # If no explicit command argument was given, then treat the calling syntax 167 # as: module(cmd args...) 168 set(exec_cmd ${ARGV}) 169 else() 170 set(exec_cmd ${MOD_ARGS_COMMAND}) 171 endif() 172 173 if(MOD_ARGS_OUTPUT_VARIABLE) 174 set(err_var_args ERROR_VARIABLE err_var) 175 endif() 176 177 execute_process( 178 COMMAND mktemp -t module.cmake.XXXXXXXXXXXX 179 OUTPUT_VARIABLE tempfile_name 180 ) 181 string(STRIP "${tempfile_name}" tempfile_name) 182 183 # If the $MODULESHOME/init/cmake file exists then assume that the CMake 184 # "shell" functionality exits 185 if(EXISTS "$ENV{MODULESHOME}/init/cmake") 186 execute_process( 187 COMMAND ${EnvModules_COMMAND} cmake ${exec_cmd} 188 OUTPUT_FILE ${tempfile_name} 189 ${err_var_args} 190 RESULT_VARIABLE ret_var 191 ) 192 193 else() # fallback to the sh shell and manually convert to CMake 194 execute_process( 195 COMMAND ${EnvModules_COMMAND} sh ${exec_cmd} 196 OUTPUT_VARIABLE out_var 197 ${err_var_args} 198 RESULT_VARIABLE ret_var 199 ) 200 endif() 201 202 # If we executed successfully then process and cleanup the temp file 203 if(ret_var EQUAL 0) 204 # No CMake shell so we need to process the sh output into CMake code 205 if(NOT EXISTS "$ENV{MODULESHOME}/init/cmake") 206 file(WRITE ${tempfile_name} "") 207 string(REPLACE "\n" ";" out_var "${out_var}") 208 foreach(sh_cmd IN LISTS out_var) 209 if(sh_cmd MATCHES "^ *unset *([^ ]*)") 210 set(cmake_cmd "unset(ENV{${CMAKE_MATCH_1}})") 211 elseif(sh_cmd MATCHES "^ *export *([^ ]*)") 212 set(cmake_cmd "set(ENV{${CMAKE_MATCH_1}} \"\${${CMAKE_MATCH_1}}\")") 213 elseif(sh_cmd MATCHES " *([^ =]*) *= *(.*)") 214 set(var_name "${CMAKE_MATCH_1}") 215 set(var_value "${CMAKE_MATCH_2}") 216 if(var_value MATCHES "^\"(.*[^\\])\"") 217 # If it's in quotes, take the value as is 218 set(var_value "${CMAKE_MATCH_1}") 219 else() 220 # Otherwise, strip trailing spaces 221 string(REGEX REPLACE "([^\\])? +$" "\\1" var_value "${var_value}") 222 endif() 223 string(REPLACE "\\ " " " var_value "${var_value}") 224 set(cmake_cmd "set(${var_name} \"${var_value}\")") 225 else() 226 continue() 227 endif() 228 file(APPEND ${tempfile_name} "${cmake_cmd}\n") 229 endforeach() 230 endif() 231 232 # Process the change in environment variables 233 include(${tempfile_name}) 234 file(REMOVE ${tempfile_name}) 235 endif() 236 237 # Push the output back out to the calling scope 238 if(MOD_ARGS_OUTPUT_VARIABLE) 239 set(${MOD_ARGS_OUTPUT_VARIABLE} "${err_var}" PARENT_SCOPE) 240 endif() 241 if(MOD_ARGS_RESULT_VARIABLE) 242 set(${MOD_ARGS_RESULT_VARIABLE} ${ret_var} PARENT_SCOPE) 243 endif() 244endfunction(env_module) 245 246#------------------------------------------------------------------------------ 247function(env_module_swap out_mod in_mod) 248 set(options) 249 set(oneValueArgs OUTPUT_VARIABLE RESULT_VARIABLE) 250 set(multiValueArgs) 251 252 cmake_parse_arguments(MOD_ARGS 253 "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGV} 254 ) 255 256 env_module(COMMAND -t swap ${out_mod} ${in_mod} 257 OUTPUT_VARIABLE tmp_out 258 RETURN_VARIABLE tmp_ret 259 ) 260 261 if(MOD_ARGS_OUTPUT_VARIABLE) 262 set(${MOD_ARGS_OUTPUT_VARIABLE} "${err_var}" PARENT_SCOPE) 263 endif() 264 if(MOD_ARGS_RESULT_VARIABLE) 265 set(${MOD_ARGS_RESULT_VARIABLE} ${tmp_ret} PARENT_SCOPE) 266 endif() 267endfunction() 268 269#------------------------------------------------------------------------------ 270function(env_module_list out_var) 271 cmake_policy(SET CMP0007 NEW) 272 env_module(COMMAND -t list OUTPUT_VARIABLE tmp_out) 273 274 # Convert output into a CMake list 275 string(REPLACE "\n" ";" ${out_var} "${tmp_out}") 276 277 # Remove title headers and empty entries 278 list(REMOVE_ITEM ${out_var} "No modules loaded") 279 if(${out_var}) 280 list(FILTER ${out_var} EXCLUDE REGEX "^(.*:)?$") 281 endif() 282 list(FILTER ${out_var} EXCLUDE REGEX "^(.*:)?$") 283 284 set(${out_var} ${${out_var}} PARENT_SCOPE) 285endfunction() 286 287#------------------------------------------------------------------------------ 288function(env_module_avail) 289 cmake_policy(SET CMP0007 NEW) 290 291 if(ARGC EQUAL 1) 292 set(mod_prefix) 293 set(out_var ${ARGV0}) 294 elseif(ARGC EQUAL 2) 295 set(mod_prefix ${ARGV0}) 296 set(out_var ${ARGV1}) 297 else() 298 message(FATAL_ERROR "Usage: env_module_avail([mod_prefix] out_var)") 299 endif() 300 env_module(COMMAND -t avail ${mod_prefix} OUTPUT_VARIABLE tmp_out) 301 302 # Convert output into a CMake list 303 string(REPLACE "\n" ";" tmp_out "${tmp_out}") 304 305 set(${out_var}) 306 foreach(MOD IN LISTS tmp_out) 307 # Remove directory entries and empty values 308 if(MOD MATCHES "^(.*:)?$") 309 continue() 310 endif() 311 312 # Convert default modules 313 if(MOD MATCHES "^(.*)/$" ) # "foo/" 314 list(APPEND ${out_var} ${CMAKE_MATCH_1}) 315 elseif(MOD MATCHES "^((.*)/.*)\\(default\\)$") # "foo/1.2.3(default)" 316 list(APPEND ${out_var} ${CMAKE_MATCH_2}) 317 list(APPEND ${out_var} ${CMAKE_MATCH_1}) 318 else() 319 list(APPEND ${out_var} ${MOD}) 320 endif() 321 endforeach() 322 323 set(${out_var} ${${out_var}} PARENT_SCOPE) 324endfunction() 325 326#------------------------------------------------------------------------------ 327# Make sure we know where the underlying module command is 328find_program(EnvModules_COMMAND 329 NAMES lmod modulecmd 330 HINTS ENV MODULESHOME 331 PATH_SUFFIXES libexec 332) 333 334include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) 335find_package_handle_standard_args(EnvModules DEFAULT_MSG EnvModules_COMMAND) 336