| # Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| # file Copyright.txt or https://cmake.org/licensing for details. | |
| #[=======================================================================[.rst: | |
| FindEnvModules | |
| -------------- | |
| .. versionadded:: 3.15 | |
| Locate an environment module implementation and make commands available to | |
| CMake scripts to use them. This is compatible with both Lua-based Lmod | |
| and TCL-based EnvironmentModules. | |
| This module is intended for the use case of setting up the compiler and library | |
| environment within a :ref:`CTest Script <CTest Script>` (``ctest -S``). It can | |
| also be used in a :ref:`CMake Script <Script Processing Mode>` (``cmake -P``). | |
| .. note:: | |
| The loaded environment will not survive past the end of the calling process. | |
| Do not use this module in project code (``CMakeLists.txt`` files) to load | |
| a compiler environment; it will not be available during the build. Instead | |
| load the environment manually before running CMake or using the generated | |
| build system. | |
| Example Usage | |
| ^^^^^^^^^^^^^ | |
| .. code-block:: 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) | |
| env_module(purge) | |
| env_module(load modules) | |
| env_module(load craype) | |
| env_module(load PrgEnv-cray) | |
| env_module(load craype-knl) | |
| env_module(load cray-mpich) | |
| env_module(load cray-libsci) | |
| set(ENV{CRAYPE_LINK_TYPE} dynamic) | |
| ... | |
| Result Variables | |
| ^^^^^^^^^^^^^^^^ | |
| This module will set the following variables in your project: | |
| ``EnvModules_FOUND`` | |
| True if a compatible environment modules framework was found. | |
| Cache Variables | |
| ^^^^^^^^^^^^^^^ | |
| The following cache variable will be set: | |
| ``EnvModules_COMMAND`` | |
| The low level module command to use. Currently supported | |
| implementations are the Lua based Lmod and TCL based EnvironmentModules. | |
| Environment Variables | |
| ^^^^^^^^^^^^^^^^^^^^^ | |
| ``ENV{MODULESHOME}`` | |
| Usually set by the module environment implementation, used as a hint to | |
| locate the module command to execute. | |
| Provided Functions | |
| ^^^^^^^^^^^^^^^^^^ | |
| This defines the following CMake functions for interacting with environment | |
| modules: | |
| .. command:: env_module | |
| Execute an aribitrary module command: | |
| .. code-block:: cmake | |
| env_module(cmd arg1 ... argN) | |
| env_module( | |
| COMMAND cmd arg1 ... argN | |
| [OUTPUT_VARIABLE <out-var>] | |
| [RESULT_VARIABLE <ret-var>] | |
| ) | |
| The options are: | |
| ``cmd arg1 ... argN`` | |
| The module sub-command and arguments to execute as if they were | |
| passed directly to the module command in your shell environment. | |
| ``OUTPUT_VARIABLE <out-var>`` | |
| The standard output from executing the module command. | |
| ``RESULT_VARIABLE <ret-var>`` | |
| The return code from executing the module command. | |
| .. command:: env_module_swap | |
| Swap 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>`` | |
| The standard output from executing the module command. | |
| ``RESULT_VARIABLE <ret-var>`` | |
| The return code from executing the module command. | |
| .. command:: env_module_list | |
| Retrieve 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 | |
| Retrieve 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. | |
| #]=======================================================================] | |
| 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(env_module) | |
| #------------------------------------------------------------------------------ | |
| 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} "${err_var}" 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) | |
| cmake_policy(SET CMP0007 NEW) | |
| 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) | |
| cmake_policy(SET CMP0007 NEW) | |
| 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(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) | |
| find_package_handle_standard_args(EnvModules DEFAULT_MSG EnvModules_COMMAND) |