mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-02 03:39:43 -06:00
FindEnvModules: Provide a CMake interface to environment modules
This commit is contained in:
@@ -125,6 +125,7 @@ They are normally called through the :command:`find_package` command.
|
||||
/module/FindDCMTK
|
||||
/module/FindDevIL
|
||||
/module/FindDoxygen
|
||||
/module/FindEnvModules
|
||||
/module/FindEXPAT
|
||||
/module/FindFLEX
|
||||
/module/FindFLTK2
|
||||
|
||||
1
Help/module/FindEnvModules.rst
Normal file
1
Help/module/FindEnvModules.rst
Normal file
@@ -0,0 +1 @@
|
||||
.. cmake-module:: ../../Modules/FindEnvModules.cmake
|
||||
5
Help/release/dev/environment-modules.rst
Normal file
5
Help/release/dev/environment-modules.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
environment-modules
|
||||
-------------------
|
||||
|
||||
* The :module:`FindEnvModules` module was added to use Lua- and TCL-based
|
||||
environment modules in :ref:`CTest Scripts <CTest Script>`.
|
||||
333
Modules/FindEnvModules.cmake
Normal file
333
Modules/FindEnvModules.cmake
Normal file
@@ -0,0 +1,333 @@
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
FindEnvModules
|
||||
--------------
|
||||
|
||||
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``
|
||||
Found the a compatible environment modules framework
|
||||
|
||||
Cache Variables
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
The following cache variable will be set:
|
||||
|
||||
``EnvModules_COMMAND``
|
||||
The low level module command to use. Currently supported are
|
||||
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)
|
||||
@@ -1421,6 +1421,10 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
|
||||
add_subdirectory(FindDoxygen)
|
||||
endif()
|
||||
|
||||
if(CMake_TEST_FindEnvModules)
|
||||
add_subdirectory(FindEnvModules)
|
||||
endif()
|
||||
|
||||
if(CMake_TEST_FindEXPAT)
|
||||
add_subdirectory(FindEXPAT)
|
||||
endif()
|
||||
|
||||
3
Tests/FindEnvModules/CMakeLists.txt
Normal file
3
Tests/FindEnvModules/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
add_test(FindEnvModules.Test ${CMAKE_CMAKE_COMMAND}
|
||||
-P ${CMAKE_CURRENT_LIST_DIR}/EnvModules.cmake
|
||||
)
|
||||
35
Tests/FindEnvModules/EnvModules.cmake
Normal file
35
Tests/FindEnvModules/EnvModules.cmake
Normal file
@@ -0,0 +1,35 @@
|
||||
find_package(EnvModules REQUIRED)
|
||||
message("module purge")
|
||||
env_module(COMMAND purge RESULT_VARIABLE ret_var)
|
||||
if(NOT ret_var EQUAL 0)
|
||||
message(FATAL_ERROR "module(purge) returned ${ret_var}")
|
||||
endif()
|
||||
|
||||
message("module avail")
|
||||
env_module_avail(avail_mods)
|
||||
foreach(mod IN LISTS avail_mods)
|
||||
message(" ${mod}")
|
||||
endforeach()
|
||||
|
||||
if(avail_mods)
|
||||
list(GET avail_mods 0 mod0)
|
||||
message("module load ${mod0}")
|
||||
env_module(load ${mod0})
|
||||
|
||||
message("module list")
|
||||
env_module_list(loaded_mods)
|
||||
foreach(mod IN LISTS loaded_mods)
|
||||
message(" ${mod}")
|
||||
endforeach()
|
||||
|
||||
list(LENGTH loaded_mods num_loaded_mods)
|
||||
message("Number of modules loaded: ${num_loaded_mods}")
|
||||
if(NOT num_loaded_mods EQUAL 1)
|
||||
message(FATAL_ERROR "Exactly 1 module should be loaded. Found ${num_loaded_mods}")
|
||||
endif()
|
||||
|
||||
list(GET loaded_mods 0 mod0_actual)
|
||||
if(NOT (mod0_actual MATCHES "^${mod0}"))
|
||||
message(FATAL_ERROR "Loaded module does not match ${mod0}. Actual: ${mod0_actual}")
|
||||
endif()
|
||||
endif()
|
||||
Reference in New Issue
Block a user