summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn W. Parent <45471568+johnwparent@users.noreply.github.com>2023-03-28 19:50:37 -0400
committerGitHub <noreply@github.com>2023-03-28 16:50:37 -0700
commit6b03c9f2856c144e45ea39f24ef50158b9343818 (patch)
tree204323e17433744534c10cf8405243f9e5e6301d
parent7ffe2fadfedae1133da6a2dfa96b8c21828dbabc (diff)
downloadspack-6b03c9f2856c144e45ea39f24ef50158b9343818.tar.gz
spack-6b03c9f2856c144e45ea39f24ef50158b9343818.tar.bz2
spack-6b03c9f2856c144e45ea39f24ef50158b9343818.tar.xz
spack-6b03c9f2856c144e45ea39f24ef50158b9343818.zip
Windows: spack.bat CLI handling robustness (#36281)
* Current develop spack.bat file cannot handle any reserved characters being passed via the CLI, particularly '=' and '?'. To address this, re-do the CLI parsing for loop to use custom logic to allow for more granular handling of CLI args. * We take a less-than-ideal approach to escaping local scope and handling unset variables as well as the actual parsing of CL arguments. To address this, don't quote the args and then try to parse the quotes we just added (resulting in spack flags being undefined). Instead, leverage batch script features. Since we are not unnecessarily quoting things, we don't need to think about removing them, and in the case of paths with spaces, we should _not_ be removing the quotes as we currently do.
-rw-r--r--bin/spack.bat107
1 files changed, 71 insertions, 36 deletions
diff --git a/bin/spack.bat b/bin/spack.bat
index 310b59506c..2c2947ac0f 100644
--- a/bin/spack.bat
+++ b/bin/spack.bat
@@ -50,24 +50,69 @@ setlocal enabledelayedexpansion
:: flags will always start with '-', e.g. --help or -V
:: subcommands will never start with '-'
:: everything after the subcommand is an arg
-for %%x in (%*) do (
- set t="%%~x"
+
+:: we cannot allow batch "for" loop to directly process CL args
+:: a number of batch reserved characters are commonly passed to
+:: spack and allowing batch's "for" method to process the raw inputs
+:: results in a large number of formatting issues
+:: instead, treat the entire CLI as one string
+:: and split by space manually
+:: capture cl args in variable named cl_args
+set cl_args=%*
+:process_cl_args
+:: tokens=1* returns the first processed token produced
+:: by tokenizing the input string cl_args on spaces into
+:: the named variable %%g
+:: While this make look like a for loop, it only
+:: executes a single time for each of the cl args
+:: the actual iterative loop is performed by the
+:: goto process_cl_args stanza
+:: we are simply leveraging the "for" method's string
+:: tokenization
+for /f "tokens=1*" %%g in ("%cl_args%") do (
+ set t=%%~g
+ :: remainder of string is composed into %%h
+ :: these are the cl args yet to be processed
+ :: assign cl_args var to only the args to be processed
+ :: effectively discarding the current arg %%g
+ :: this will be nul when we have no further tokens to process
+ set cl_args=%%h
+ :: process the first space delineated cl arg
+ :: of this iteration
if "!t:~0,1!" == "-" (
if defined _sp_subcommand (
:: We already have a subcommand, processing args now
- set "_sp_args=!_sp_args! !t!"
+ if not defined _sp_args (
+ set "_sp_args=!t!"
+ ) else (
+ set "_sp_args=!_sp_args! !t!"
+ )
) else (
- set "_sp_flags=!_sp_flags! !t!"
- shift
+ if not defined _sp_flags (
+ set "_sp_flags=!t!"
+ shift
+ ) else (
+ set "_sp_flags=!_sp_flags! !t!"
+ shift
+ )
)
) else if not defined _sp_subcommand (
set "_sp_subcommand=!t!"
shift
) else (
- set "_sp_args=!_sp_args! !t!"
- shift
+ if not defined _sp_args (
+ set "_sp_args=!t!"
+ shift
+ ) else (
+ set "_sp_args=!_sp_args! !t!"
+ shift
+ )
)
)
+:: if this is not nil, we have more tokens to process
+:: start above process again with remaining unprocessed cl args
+if defined cl_args goto :process_cl_args
+
:: --help, -h and -V flags don't require further output parsing.
:: If we encounter, execute and exit
@@ -95,31 +140,21 @@ if not defined _sp_subcommand (
:: pass parsed variables outside of local scope. Need to do
:: this because delayedexpansion can only be set by setlocal
-echo %_sp_flags%>flags
-echo %_sp_args%>args
-echo %_sp_subcommand%>subcmd
-endlocal
-set /p _sp_subcommand=<subcmd
-set /p _sp_flags=<flags
-set /p _sp_args=<args
-if "%_sp_subcommand%"=="ECHO is off." (set "_sp_subcommand=")
-if "%_sp_subcommand%"=="ECHO is on." (set "_sp_subcommand=")
-if "%_sp_flags%"=="ECHO is off." (set "_sp_flags=")
-if "%_sp_flags%"=="ECHO is on." (set "_sp_flags=")
-if "%_sp_args%"=="ECHO is off." (set "_sp_args=")
-if "%_sp_args%"=="ECHO is on." (set "_sp_args=")
-del subcmd
-del flags
-del args
+endlocal & (
+ set "_sp_flags=%_sp_flags%"
+ set "_sp_args=%_sp_args%"
+ set "_sp_subcommand=%_sp_subcommand%"
+)
+
:: Filter out some commands. For any others, just run the command.
-if %_sp_subcommand% == "cd" (
+if "%_sp_subcommand%" == "cd" (
goto :case_cd
-) else if %_sp_subcommand% == "env" (
+) else if "%_sp_subcommand%" == "env" (
goto :case_env
-) else if %_sp_subcommand% == "load" (
+) else if "%_sp_subcommand%" == "load" (
goto :case_load
-) else if %_sp_subcommand% == "unload" (
+) else if "%_sp_subcommand%" == "unload" (
goto :case_load
) else (
goto :default_case
@@ -154,20 +189,20 @@ goto :end_switch
if NOT defined _sp_args (
goto :default_case
)
-set args_no_quote=%_sp_args:"=%
-if NOT "%args_no_quote%"=="%args_no_quote:--help=%" (
+
+if NOT "%_sp_args%"=="%_sp_args:--help=%" (
goto :default_case
-) else if NOT "%args_no_quote%"=="%args_no_quote: -h=%" (
+) else if NOT "%_sp_args%"=="%_sp_args: -h=%" (
goto :default_case
-) else if NOT "%args_no_quote%"=="%args_no_quote:--bat=%" (
+) else if NOT "%_sp_args%"=="%_sp_args:--bat=%" (
goto :default_case
-) else if NOT "%args_no_quote%"=="%args_no_quote:deactivate=%" (
+) else if NOT "%_sp_args%"=="%_sp_args:deactivate=%" (
for /f "tokens=* USEBACKQ" %%I in (
- `call python %spack% %_sp_flags% env deactivate --bat %args_no_quote:deactivate=%`
+ `call python %spack% %_sp_flags% env deactivate --bat %_sp_args:deactivate=%`
) do %%I
-) else if NOT "%args_no_quote%"=="%args_no_quote:activate=%" (
+) else if NOT "%_sp_args%"=="%_sp_args:activate=%" (
for /f "tokens=* USEBACKQ" %%I in (
- `python %spack% %_sp_flags% env activate --bat %args_no_quote:activate=%`
+ `python %spack% %_sp_flags% env activate --bat %_sp_args:activate=%`
) do %%I
) else (
goto :default_case
@@ -188,7 +223,7 @@ if defined _sp_args (
for /f "tokens=* USEBACKQ" %%I in (
`python "%spack%" %_sp_flags% %_sp_subcommand% --bat %_sp_args%`) do %%I
-)
+
goto :end_switch
:case_unload