From 9615927da308944770915cbc0c909fed3a2a3cfe Mon Sep 17 00:00:00 2001 From: Benoit Dumas Date: Wed, 15 Oct 2025 19:52:59 +0200 Subject: [PATCH 1/4] Use cmake 'EVAL CODE' to forward arguments passed to 'module' cmake function Use cmake 'EVAL CODE' to handle any number of arguments. This change mandates use of cmake >= 3.18. The solution comes from the following blog post: https://crascit.com/2019/01/29/forwarding-command-arguments-in-cmake/ Fixes #419 Signed-off-by: Benoit Dumas --- tcl/envmngt.tcl.in | 42 +++++++--------- testsuite/modules.70-maint/120-autoinit.exp | 53 +++++++-------------- 2 files changed, 35 insertions(+), 60 deletions(-) diff --git a/tcl/envmngt.tcl.in b/tcl/envmngt.tcl.in index 80cab3527..76a4b4378 100644 --- a/tcl/envmngt.tcl.in +++ b/tcl/envmngt.tcl.in @@ -1178,13 +1178,10 @@ end} } cmake { if {[getConf quarantine_support]} { - set pre_exec "\n execute_process(COMMAND \${_mlre} $tclshbin\ - \"$::argv0\" cmake " + set pre_exec "COMMAND \${_mlre} $tclshbin \\\"$::argv0\\\" cmake " } else { - set pre_exec "\n execute_process(COMMAND $tclshbin\ - \"$::argv0\" cmake " + set pre_exec "COMMAND $tclshbin \\\"$::argv0\\\" cmake " } - set post_exec "\n OUTPUT_FILE \${tempfile_name})\n" set fdef {function(module) cmake_policy(SET CMP0007 NEW)} if {[getConf quarantine_support]} { @@ -1195,14 +1192,15 @@ end} foreach(_mlv ${_mlv_list}) if(${_mlv} MATCHES "^[A-Za-z_][A-Za-z0-9_]*$") if(DEFINED ENV{${_mlv}}) - set(_mlre "${_mlre}__MODULES_QUAR_${_mlv}=$ENV{${_mlv}};") + string(APPEND _mlre + " [===[__MODULES_QUAR_${_mlv}=$ENV{${_mlv}}]===]") endif() set(_mlrv "MODULES_RUNENV_${_mlv}") - set(_mlre "${_mlre}${_mlv}=$ENV{${_mlrv}};") + string(APPEND _mlre " [===[${_mlv}=$ENV{${_mlrv}}]===]") endif() endforeach() if (NOT "${_mlre}" STREQUAL "") - set(_mlre "env;${_mlre}__MODULES_QUARANTINE_SET=1;") + set(_mlre "env ${_mlre} __MODULES_QUARANTINE_SET=1") endif() endif()} } @@ -1211,23 +1209,17 @@ end} execute_process(COMMAND mktemp -t moduleinit.cmake.XXXXXXXXXXXX OUTPUT_VARIABLE tempfile_name OUTPUT_STRIP_TRAILING_WHITESPACE) - if(${ARGC} EQUAL 1)} - # adapt command definition depending on the number of args to be - # able to pass to some extend (<5 args) empty string element to - # modulecmd (no other way as empty element in ${ARGV} are skipped - append fdef "$pre_exec\"\${ARGV0}\"$post_exec" - append fdef { elseif(${ARGC} EQUAL 2)} - append fdef "$pre_exec\"\${ARGV0}\" \"\${ARGV1}\"$post_exec" - append fdef { elseif(${ARGC} EQUAL 3)} - append fdef "$pre_exec\"\${ARGV0}\" \"\${ARGV1}\"\ - \"\${ARGV2}\"$post_exec" - append fdef { elseif(${ARGC} EQUAL 4)} - append fdef "$pre_exec\"\${ARGV0}\" \"\${ARGV1}\"\ - \"\${ARGV2}\" \"\${ARGV3}\"$post_exec" - append fdef { else()} - append fdef "$pre_exec\${ARGV}$post_exec" - append fdef { endif() - if(EXISTS ${tempfile_name}) +} + append fdef " set(quotedArgs \"$pre_exec\")" + append fdef { + cmake_parse_arguments(PARSE_ARGV 0 FWD "" "" "") + foreach(arg IN LISTS FWD_UNPARSED_ARGUMENTS) + string(APPEND quotedArgs " [===[${arg}]===]") + endforeach() + string(APPEND quotedArgs " OUTPUT_FILE ${tempfile_name}") + cmake_language(EVAL CODE "execute_process(${quotedArgs})") +} + append fdef { if(EXISTS ${tempfile_name}) include(${tempfile_name}) file(REMOVE ${tempfile_name}) endif() diff --git a/testsuite/modules.70-maint/120-autoinit.exp b/testsuite/modules.70-maint/120-autoinit.exp index b91337299..40910f5b4 100644 --- a/testsuite/modules.70-maint/120-autoinit.exp +++ b/testsuite/modules.70-maint/120-autoinit.exp @@ -413,36 +413,28 @@ set func_cmake "function\\\(module\\\) \\s*foreach\\\(_mlv \\\${_mlv_list}\\\) \\s*if\\\(\\\${_mlv} MATCHES \"\\\^\\\[A-Za-z_\\\]\\\[A-Za-z0-9_\\\]\\\*\\\$\"\\\) \\s*if\\\(DEFINED ENV{\\\${_mlv}}\\\) -\\s*set\\\(_mlre \"\\\${_mlre}__MODULES_QUAR_\\\${_mlv}=\\\$ENV{\\\${_mlv}};\"\\\) +\\s*string\\\(APPEND _mlre +\\s*\" \\\[===\\\[__MODULES_QUAR_\\\${_mlv}=\\\$ENV{\\\${_mlv}}\\\]===\\\]\"\\\) \\s*endif\\\(\\\) \\s*set\\\(_mlrv \"MODULES_RUNENV_\\\${_mlv}\"\\\) -\\s*set\\\(_mlre \"\\\${_mlre}\\\${_mlv}=\\\$ENV{\\\${_mlrv}};\"\\\) +\\s*string\\\(APPEND _mlre \" \\\[===\\\[\\\${_mlv}=\\\$ENV{\\\${_mlrv}}\\\]===\\\]\"\\\) \\s*endif\\\(\\\) \\s*endforeach\\\(\\\) \\s*if \\\(NOT \"\\\${_mlre}\" STREQUAL \"\"\\\) -\\s*set\\\(_mlre \"env;\\\${_mlre}__MODULES_QUARANTINE_SET=1;\"\\\) +\\s*set\\\(_mlre \"env \\\${_mlre} __MODULES_QUARANTINE_SET=1\"\\\) \\s*endif\\\(\\\) \\s*endif\\\(\\\) \\s*set\\\(_mlstatus TRUE\\\) \\s*execute_process\\\(COMMAND mktemp -t moduleinit.cmake.XXXXXXXXXXXX \\s*OUTPUT_VARIABLE tempfile_name \\s*OUTPUT_STRIP_TRAILING_WHITESPACE\\\) -\\s*if\\\(\\\${ARGC} EQUAL 1\\\) -\\s*execute_process\\\(COMMAND \\\${_mlre} $tclshlocre \"$modulecmd_pathre\" cmake \"\\\${ARGV0}\" -\\s*OUTPUT_FILE \\\${tempfile_name}\\\) -\\s*elseif\\\(\\\${ARGC} EQUAL 2\\\) -\\s*execute_process\\\(COMMAND \\\${_mlre} $tclshlocre \"$modulecmd_pathre\" cmake \"\\\${ARGV0}\" \"\\\${ARGV1}\" -\\s*OUTPUT_FILE \\\${tempfile_name}\\\) -\\s*elseif\\\(\\\${ARGC} EQUAL 3\\\) -\\s*execute_process\\\(COMMAND \\\${_mlre} $tclshlocre \"$modulecmd_pathre\" cmake \"\\\${ARGV0}\" \"\\\${ARGV1}\" \"\\\${ARGV2}\" -\\s*OUTPUT_FILE \\\${tempfile_name}\\\) -\\s*elseif\\\(\\\${ARGC} EQUAL 4\\\) -\\s*execute_process\\\(COMMAND \\\${_mlre} $tclshlocre \"$modulecmd_pathre\" cmake \"\\\${ARGV0}\" \"\\\${ARGV1}\" \"\\\${ARGV2}\" \"\\\${ARGV3}\" -\\s*OUTPUT_FILE \\\${tempfile_name}\\\) -\\s*else\\\(\\\) -\\s*execute_process\\\(COMMAND \\\${_mlre} $tclshlocre \"$modulecmd_pathre\" cmake \\\${ARGV} -\\s*OUTPUT_FILE \\\${tempfile_name}\\\) -\\s*endif\\\(\\\) +\\s*set\\\(quotedArgs \"COMMAND \\\${_mlre} $tclshlocre \\\\\"$modulecmd_pathre\\\\\" cmake \"\\\) +\\s*cmake_parse_arguments\\\(PARSE_ARGV 0 FWD \"\" \"\" \"\"\\\) +\\s*foreach\\\(arg IN LISTS FWD_UNPARSED_ARGUMENTS\\\) +\\s*string\\\(APPEND quotedArgs \" \\\[===\\\[\\\${arg}\\\]===\\\]\"\\\) +\\s*endforeach\\\(\\\) +\\s*string\\\(APPEND quotedArgs \" OUTPUT_FILE \\\${tempfile_name}\"\\\) +\\s*cmake_language\\\(EVAL CODE \"execute_process\\\(\\\${quotedArgs}\\\)\"\\\) \\s*if\\\(EXISTS \\\${tempfile_name}\\\) \\s*include\\\(\\\${tempfile_name}\\\) \\s*file\\\(REMOVE \\\${tempfile_name}\\\) @@ -619,22 +611,13 @@ set func_cmake "function\\\(module\\\) \\s*execute_process\\\(COMMAND mktemp -t moduleinit.cmake.XXXXXXXXXXXX \\s*OUTPUT_VARIABLE tempfile_name \\s*OUTPUT_STRIP_TRAILING_WHITESPACE\\\) -\\s*if\\\(\\\${ARGC} EQUAL 1\\\) -\\s*execute_process\\\(COMMAND $tclshlocre \"$modulecmd_pathre\" cmake \"\\\${ARGV0}\" -\\s*OUTPUT_FILE \\\${tempfile_name}\\\) -\\s*elseif\\\(\\\${ARGC} EQUAL 2\\\) -\\s*execute_process\\\(COMMAND $tclshlocre \"$modulecmd_pathre\" cmake \"\\\${ARGV0}\" \"\\\${ARGV1}\" -\\s*OUTPUT_FILE \\\${tempfile_name}\\\) -\\s*elseif\\\(\\\${ARGC} EQUAL 3\\\) -\\s*execute_process\\\(COMMAND $tclshlocre \"$modulecmd_pathre\" cmake \"\\\${ARGV0}\" \"\\\${ARGV1}\" \"\\\${ARGV2}\" -\\s*OUTPUT_FILE \\\${tempfile_name}\\\) -\\s*elseif\\\(\\\${ARGC} EQUAL 4\\\) -\\s*execute_process\\\(COMMAND $tclshlocre \"$modulecmd_pathre\" cmake \"\\\${ARGV0}\" \"\\\${ARGV1}\" \"\\\${ARGV2}\" \"\\\${ARGV3}\" -\\s*OUTPUT_FILE \\\${tempfile_name}\\\) -\\s*else\\\(\\\) -\\s*execute_process\\\(COMMAND $tclshlocre \"$modulecmd_pathre\" cmake \\\${ARGV} -\\s*OUTPUT_FILE \\\${tempfile_name}\\\) -\\s*endif\\\(\\\) +\\s*set\\\(quotedArgs \"COMMAND $tclshlocre \\\\\"$modulecmd_pathre\\\\\" cmake \"\\\) +\\s*cmake_parse_arguments\\\(PARSE_ARGV 0 FWD \"\" \"\" \"\"\\\) +\\s*foreach\\\(arg IN LISTS FWD_UNPARSED_ARGUMENTS\\\) +\\s*string\\\(APPEND quotedArgs \" \\\[===\\\[\\\${arg}\\\]===\\\]\"\\\) +\\s*endforeach\\\(\\\) +\\s*string\\\(APPEND quotedArgs \" OUTPUT_FILE \\\${tempfile_name}\"\\\) +\\s*cmake_language\\\(EVAL CODE \"execute_process\\\(\\\${quotedArgs}\\\)\"\\\) \\s*if\\\(EXISTS \\\${tempfile_name}\\\) \\s*include\\\(\\\${tempfile_name}\\\) \\s*file\\\(REMOVE \\\${tempfile_name}\\\) From c31693a8d07598cac34a6201673ecda5ddbb6d94 Mon Sep 17 00:00:00 2001 From: Xavier Delaruelle Date: Sun, 26 Oct 2025 18:01:55 +0100 Subject: [PATCH 2/4] Remove useless extra space in cmake module procedure Signed-off-by: Xavier Delaruelle --- tcl/envmngt.tcl.in | 4 ++-- testsuite/modules.70-maint/120-autoinit.exp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tcl/envmngt.tcl.in b/tcl/envmngt.tcl.in index 76a4b4378..30b8b5eb3 100644 --- a/tcl/envmngt.tcl.in +++ b/tcl/envmngt.tcl.in @@ -1178,9 +1178,9 @@ end} } cmake { if {[getConf quarantine_support]} { - set pre_exec "COMMAND \${_mlre} $tclshbin \\\"$::argv0\\\" cmake " + set pre_exec "COMMAND \${_mlre} $tclshbin \\\"$::argv0\\\" cmake" } else { - set pre_exec "COMMAND $tclshbin \\\"$::argv0\\\" cmake " + set pre_exec "COMMAND $tclshbin \\\"$::argv0\\\" cmake" } set fdef {function(module) cmake_policy(SET CMP0007 NEW)} diff --git a/testsuite/modules.70-maint/120-autoinit.exp b/testsuite/modules.70-maint/120-autoinit.exp index 40910f5b4..2e9cf1e2c 100644 --- a/testsuite/modules.70-maint/120-autoinit.exp +++ b/testsuite/modules.70-maint/120-autoinit.exp @@ -428,7 +428,7 @@ set func_cmake "function\\\(module\\\) \\s*execute_process\\\(COMMAND mktemp -t moduleinit.cmake.XXXXXXXXXXXX \\s*OUTPUT_VARIABLE tempfile_name \\s*OUTPUT_STRIP_TRAILING_WHITESPACE\\\) -\\s*set\\\(quotedArgs \"COMMAND \\\${_mlre} $tclshlocre \\\\\"$modulecmd_pathre\\\\\" cmake \"\\\) +\\s*set\\\(quotedArgs \"COMMAND \\\${_mlre} $tclshlocre \\\\\"$modulecmd_pathre\\\\\" cmake\"\\\) \\s*cmake_parse_arguments\\\(PARSE_ARGV 0 FWD \"\" \"\" \"\"\\\) \\s*foreach\\\(arg IN LISTS FWD_UNPARSED_ARGUMENTS\\\) \\s*string\\\(APPEND quotedArgs \" \\\[===\\\[\\\${arg}\\\]===\\\]\"\\\) @@ -611,7 +611,7 @@ set func_cmake "function\\\(module\\\) \\s*execute_process\\\(COMMAND mktemp -t moduleinit.cmake.XXXXXXXXXXXX \\s*OUTPUT_VARIABLE tempfile_name \\s*OUTPUT_STRIP_TRAILING_WHITESPACE\\\) -\\s*set\\\(quotedArgs \"COMMAND $tclshlocre \\\\\"$modulecmd_pathre\\\\\" cmake \"\\\) +\\s*set\\\(quotedArgs \"COMMAND $tclshlocre \\\\\"$modulecmd_pathre\\\\\" cmake\"\\\) \\s*cmake_parse_arguments\\\(PARSE_ARGV 0 FWD \"\" \"\" \"\"\\\) \\s*foreach\\\(arg IN LISTS FWD_UNPARSED_ARGUMENTS\\\) \\s*string\\\(APPEND quotedArgs \" \\\[===\\\[\\\${arg}\\\]===\\\]\"\\\) From 02b5f1867a33df3539d0c0affec02e8097fe6990 Mon Sep 17 00:00:00 2001 From: Xavier Delaruelle Date: Sun, 26 Oct 2025 19:38:00 +0100 Subject: [PATCH 3/4] ts: use cmake EVAL CODE in install_test_cmake Signed-off-by: Xavier Delaruelle --- testsuite/bin/install_test_cmake | 76 ++++++-------------------- testsuite/install.00-init/080-args.exp | 2 +- 2 files changed, 17 insertions(+), 61 deletions(-) diff --git a/testsuite/bin/install_test_cmake b/testsuite/bin/install_test_cmake index d2b8faf21..5b4fa4112 100755 --- a/testsuite/bin/install_test_cmake +++ b/testsuite/bin/install_test_cmake @@ -28,7 +28,7 @@ include(${initfile}) # execute command list set(code 0) -set(runml 0) +set(func "module") string(REPLACE ":" ";" cmdsplit "${cmdlist}") # empty command list, means one empty command list (LENGTH cmdlist cmdcnt) @@ -38,70 +38,26 @@ else() foreach(cmd ${cmdsplit}) # call ml procedure instead of module if("${cmd}" STREQUAL "ml") - set(runml 1) - # if command equals to NOARG string, means call with no arg passed - elseif("${cmd}" STREQUAL "NOARG") - if (${runml} EQUAL 0) - module() - else() - ml() - endif() + set(func "ml") else() - string(REPLACE "," ";" cmdelt "${cmd}") - list (LENGTH cmdelt eltcnt) - if(${eltcnt} GREATER 4) - list(GET cmdelt 0 elt0) - list(GET cmdelt 1 elt1) - list(GET cmdelt 2 elt2) - list(GET cmdelt 3 elt3) - list(GET cmdelt 4 elt4) - if (${runml} EQUAL 0) - module("${elt0}" "${elt1}" "${elt2}" "${elt3}" "${elt4}") - else() - ml("${elt0}" "${elt1}" "${elt2}" "${elt3}" "${elt4}") - endif() - elseif(${eltcnt} GREATER 3) - list(GET cmdelt 0 elt0) - list(GET cmdelt 1 elt1) - list(GET cmdelt 2 elt2) - list(GET cmdelt 3 elt3) - if (${runml} EQUAL 0) - module("${elt0}" "${elt1}" "${elt2}" "${elt3}") - else() - ml("${elt0}" "${elt1}" "${elt2}" "${elt3}") - endif() - elseif(${eltcnt} GREATER 2) - list(GET cmdelt 0 elt0) - list(GET cmdelt 1 elt1) - list(GET cmdelt 2 elt2) - if (${runml} EQUAL 0) - module("${elt0}" "${elt1}" "${elt2}") - else() - ml("${elt0}" "${elt1}" "${elt2}") - endif() - elseif(${eltcnt} GREATER 1) - list(GET cmdelt 0 elt0) - list(GET cmdelt 1 elt1) - if (${runml} EQUAL 0) - module("${elt0}" "${elt1}") - else() - ml("${elt0}" "${elt1}") - endif() - elseif(${eltcnt} GREATER 0) - list(GET cmdelt 0 elt0) - if (${runml} EQUAL 0) - module("${elt0}") - else() - ml("${elt0}") - endif() - else() + set(quotedArgs "") + # if command equals to NOARG string, means call with no arg passed + if(NOT "${cmd}" STREQUAL "NOARG") + string(REPLACE "," ";" cmdelt "${cmd}") + list (LENGTH cmdelt eltcnt) # empty arg list, means one empty arg - if (${runml} EQUAL 0) - module("") + if (${eltcnt} EQUAL 0) + string(APPEND quotedArgs "\"\"") else() - ml("") + foreach(elt IN LISTS cmdelt) + if(NOT "${quotedArgs}" STREQUAL "") + string(APPEND quotedArgs " ") + endif() + string(APPEND quotedArgs "[===[${elt}]===]") + endforeach() endif() endif() + cmake_language(EVAL CODE "${func}(${quotedArgs})") endif() if(NOT module_result AND NOT ${module_result} STREQUAL "") set(code 1) diff --git a/testsuite/install.00-init/080-args.exp b/testsuite/install.00-init/080-args.exp index 1758caac8..894b5bb6f 100644 --- a/testsuite/install.00-init/080-args.exp +++ b/testsuite/install.00-init/080-args.exp @@ -103,7 +103,7 @@ foreach shell $othlang_list { set postmsg "\nError: \nExecution halted" } cmake { - set postmsg "\nCMake Error at testsuite/bin/install_test_cmake:115 \\\(message\\\):\n\n" + set postmsg "\nCMake Error at testsuite/bin/install_test_cmake:71 \\\(message\\\):\n\n" } default { set postmsg {} From a8014a9adedd568efe75bac2622dde53c8038ef8 Mon Sep 17 00:00:00 2001 From: Xavier Delaruelle Date: Mon, 27 Oct 2025 07:46:14 +0100 Subject: [PATCH 4/4] init: use 'EVAL CODE' in cmake init script Apply same kind of code in init script than used on autoinit. Signed-off-by: Xavier Delaruelle --- init/cmake.in | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/init/cmake.in b/init/cmake.in index 1d9fa0df4..8e7866410 100644 --- a/init/cmake.in +++ b/init/cmake.in @@ -10,14 +10,15 @@ if(DEFINED ENV{MODULES_RUN_QUARANTINE}) foreach(_mlv ${_mlv_list}) if(${_mlv} MATCHES "^[A-Za-z_][A-Za-z0-9_]*$") if(DEFINED ENV{${_mlv}}) - set(_mlre "${_mlre}__MODULES_QUAR_${_mlv}=$ENV{${_mlv}};") + string(APPEND _mlre + " [===[__MODULES_QUAR_${_mlv}=$ENV{${_mlv}}]===]") endif() set(_mlrv "MODULES_RUNENV_${_mlv}") - set(_mlre "${_mlre}${_mlv}=$ENV{${_mlrv}};") + string(APPEND _mlre " [===[${_mlv}=$ENV{${_mlrv}}]===]") endif() endforeach() if (NOT "${_mlre}" STREQUAL "") - set(_mlre "env;${_mlre}__MODULES_QUARANTINE_SET=1;") + set(_mlre "env ${_mlre} __MODULES_QUARANTINE_SET=1") endif() endif() @@ -26,8 +27,9 @@ endif() execute_process(COMMAND mktemp -t moduleinit.cmake.XXXXXXXXXXXX OUTPUT_VARIABLE tempfile_name OUTPUT_STRIP_TRAILING_WHITESPACE) -execute_process(COMMAND ${_mlre} @TCLSH@ "@libexecdir@/modulecmd.tcl" cmake autoinit - OUTPUT_FILE ${tempfile_name}) +set(quotedArgs "COMMAND ${_mlre} @TCLSH@ \"@libexecdir@/modulecmd.tcl\" cmake") +string(APPEND quotedArgs " autoinit OUTPUT_FILE ${tempfile_name}") +cmake_language(EVAL CODE "execute_process(${quotedArgs})") if(EXISTS ${tempfile_name}) include(${tempfile_name}) file(REMOVE ${tempfile_name})