On Windows, the spaces in directory names cause trouble: Michele Locati reports errors like
exec dotnet 'C:\Program Files\dotnet\sdk\6.0.300\Roslyn\bincore\csc.dll' -nologo -optimize+ -debug+ -target:library -out:GNU.Gettext.dll '-lib:C:\\Program\' 'Files\\dotnet\\shared\\Microsoft.NETCore.App\\6.0.5' ... ... warning CS1668: Invalid search path 'C:\\Program\' specified in '/LIB option' -- 'directory does not exist' error CS2001: Source file 'D:\gettext-2024-10-10\build\gettext-runtime\intl-csharp\Files\\dotnet\\shared\\Microsoft.NETCore.App\\6.0.5' could not be found. make[4]: *** [Makefile:2364: GNU.Gettext.dll] Error 1 This patch fixes it. Tested with dotnet 8 on Windows. Passing arguments with spaces, as part of an argument list with an arbitrary number of arguments, to subprocesses is one of the hardest things in shell scripts. The simpler cases are simple: - If there are only a limited number of arguments, just using a different variable for each, and avoiding word-expansion through use of double-quotes is sufficient. - If all arguments of the shell script need to be passed to a subprocess, possibly with some being filtered out, it can be done with a single loop and the use of 'set -- ...' or 'set x ...'. Examples: build-aux/compile (from Automake) build-aux/ar-lib (from Automake) build-aux/macos-compile But the general case (that involves reordering the arguments or adding other arguments) is hard. Stackoverflow [1] says that word-expansion must be avoided, because it does not support the spaces in arguments and is dangerous. But it doesn't give a solution, cf. [1][2]. Fortunately, the 'x-to-1.in' script that we already have in Gnulib shows how it can be done: With 'eval'. [1] https://stackoverflow.com/questions/3811345/ [2] https://stackoverflow.com/questions/4824590/ 2024-10-10 Bruno Haible <br...@clisp.org> csharpcomp-script: Handle directories with spaces correctly. Reported by Michele Locati <mich...@locati.it>. * build-aux/csharpcomp.sh.in (command_for_print, command_for_eval, options_csc_for_print, options_csc_for_eval, sources_csc_for_print, sources_csc_for_eval): New variables. (sed_protect_1, sed_protect_2a, sed_protect_2b, sed_protect_2c, sed_protect_3a, sed_protect_3b): New variables, copied from build-aux/x-to-1.in. (func_add_word_to_command): New function, copied from build-aux/x-to-1.in. (func_add_word_to_options_csc, func_add_word_to_sources_csc): New functions. (options_csc, sources_csc): Remove variables. Use func_add_word_to_options_csc, func_add_word_to_sources_csc instead of augmenting them. Use options_csc_for_print, options_csc_for_eval, sources_csc_for_print, sources_csc_for_eval when invoking csc. * build-aux/csharpexec.sh.in (sed_quote_subst): Remove unused variable. 2024-10-10 Bruno Haible <br...@clisp.org> java{comp,exec}-script, csharp{comp,exec}-script: Improve debugging. * build-aux/javaexec.sh.in: Send debugging output to stderr, not stdout. * build-aux/javacomp.sh.in: Likewise. * build-aux/csharpexec.sh.in: Likewise. * build-aux/csharpcomp.sh.in: Likewise.
>From cd962ee06c2dfa79a6f77fa2a81244bd9bc7c49a Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Thu, 10 Oct 2024 19:15:06 +0200 Subject: [PATCH 1/2] java{comp,exec}-script, csharp{comp,exec}-script: Improve debugging. * build-aux/javaexec.sh.in: Send debugging output to stderr, not stdout. * build-aux/javacomp.sh.in: Likewise. * build-aux/csharpexec.sh.in: Likewise. * build-aux/csharpcomp.sh.in: Likewise. --- ChangeLog | 8 ++++++++ build-aux/csharpcomp.sh.in | 6 +++--- build-aux/csharpexec.sh.in | 6 +++--- build-aux/javacomp.sh.in | 4 ++-- build-aux/javaexec.sh.in | 6 +++--- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index 04883497b5..b427c5033c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2024-10-10 Bruno Haible <br...@clisp.org> + + java{comp,exec}-script, csharp{comp,exec}-script: Improve debugging. + * build-aux/javaexec.sh.in: Send debugging output to stderr, not stdout. + * build-aux/javacomp.sh.in: Likewise. + * build-aux/csharpexec.sh.in: Likewise. + * build-aux/csharpcomp.sh.in: Likewise. + 2024-10-09 Bruno Haible <br...@clisp.org> csharpcomp: Avoid error on Windows. diff --git a/build-aux/csharpcomp.sh.in b/build-aux/csharpcomp.sh.in index fcfc32cd7f..58b53c6bff 100644 --- a/build-aux/csharpcomp.sh.in +++ b/build-aux/csharpcomp.sh.in @@ -162,7 +162,7 @@ if test -n "@HAVE_MCS@"; then }' func_tmpdir trap 'rm -rf "$tmp"' HUP INT QUIT TERM - test -z "$CSHARP_VERBOSE" || echo mcs $options_mcs $sources + test -z "$CSHARP_VERBOSE" || echo mcs $options_mcs $sources 1>&2 mcs $options_mcs $sources > "$tmp"/mcs.err result=$? sed -e "$sed_drop_success_line" < "$tmp"/mcs.err >&2 @@ -192,11 +192,11 @@ else csc=`cygpath -w "$csc"` ;; esac - test -z "$CSHARP_VERBOSE" || echo dotnet "$csc" $options_csc $sources_csc + test -z "$CSHARP_VERBOSE" || echo dotnet "$csc" $options_csc $sources_csc 1>&2 exec dotnet "$csc" $options_csc $sources_csc else if test -n "@HAVE_DOTNET_CSC@" || test -n "@HAVE_CSC@"; then - test -z "$CSHARP_VERBOSE" || echo csc $options_csc $sources_csc + test -z "$CSHARP_VERBOSE" || echo csc $options_csc $sources_csc 1>&2 exec csc $options_csc $sources_csc else echo 'C# compiler not found, try installing mono or dotnet, then reconfigure' 1>&2 diff --git a/build-aux/csharpexec.sh.in b/build-aux/csharpexec.sh.in index 45c95b4b5f..c2ccd3b645 100644 --- a/build-aux/csharpexec.sh.in +++ b/build-aux/csharpexec.sh.in @@ -101,7 +101,7 @@ if test -n "@HAVE_MONO@"; then MONO_PATH="$CONF_MONO_PATH" fi export MONO_PATH - test -z "$CSHARP_VERBOSE" || echo mono "$@" + test -z "$CSHARP_VERBOSE" || echo mono "$@" 1>&2 exec mono "$@" else if test -n "@HAVE_DOTNET@"; then @@ -180,7 +180,7 @@ else runtimeconfig_arg=`cygpath -w "$runtimeconfig"` ;; esac - test -z "$CSHARP_VERBOSE" || echo dotnet exec --runtimeconfig "$runtimeconfig_arg" "$prog_arg" "$@" + test -z "$CSHARP_VERBOSE" || echo dotnet exec --runtimeconfig "$runtimeconfig_arg" "$prog_arg" "$@" 1>&2 dotnet exec --runtimeconfig "$runtimeconfig_arg" "$prog_arg" "$@" result=$? rm -f "$runtimeconfig" @@ -204,7 +204,7 @@ else prog=`cygpath -w "$prog"` ;; esac - test -z "$CSHARP_VERBOSE" || echo clix "$prog" "$@" + test -z "$CSHARP_VERBOSE" || echo clix "$prog" "$@" 1>&2 exec clix "$prog" "$@" else echo 'C# virtual machine not found, try installing mono or dotnet, then reconfigure' 1>&2 diff --git a/build-aux/javacomp.sh.in b/build-aux/javacomp.sh.in index 65c4008fe7..a24fa1d410 100644 --- a/build-aux/javacomp.sh.in +++ b/build-aux/javacomp.sh.in @@ -37,7 +37,7 @@ if test -n "@HAVE_JAVAC_ENVVAR@"; then CLASSPATH="$CONF_CLASSPATH" fi export CLASSPATH - test -z "$JAVA_VERBOSE" || echo "$CONF_JAVAC $@" + test -z "$JAVA_VERBOSE" || echo "$CONF_JAVAC $@" 1>&2 exec $CONF_JAVAC "$@" else unset JAVA_HOME @@ -45,7 +45,7 @@ else # In this case, $CONF_JAVAC starts with "javac". CLASSPATH="$CLASSPATH" export CLASSPATH - test -z "$JAVA_VERBOSE" || echo "$CONF_JAVAC $@" + test -z "$JAVA_VERBOSE" || echo "$CONF_JAVAC $@" 1>&2 exec $CONF_JAVAC "$@" else echo 'Java compiler not found, try setting $JAVAC, then reconfigure' 1>&2 diff --git a/build-aux/javaexec.sh.in b/build-aux/javaexec.sh.in index cedb28d75d..01d5a6fadc 100644 --- a/build-aux/javaexec.sh.in +++ b/build-aux/javaexec.sh.in @@ -36,19 +36,19 @@ if test -n "@HAVE_JAVA_ENVVAR@"; then CLASSPATH="$CONF_CLASSPATH" fi export CLASSPATH - test -z "$JAVA_VERBOSE" || echo "$CONF_JAVA $@" + test -z "$JAVA_VERBOSE" || echo "$CONF_JAVA $@" 1>&2 exec $CONF_JAVA "$@" else unset JAVA_HOME export CLASSPATH if test -n "@HAVE_JAVA@"; then # In this case, $CONF_JAVA is "java". - test -z "$JAVA_VERBOSE" || echo "$CONF_JAVA $@" + test -z "$JAVA_VERBOSE" || echo "$CONF_JAVA $@" 1>&2 exec $CONF_JAVA "$@" else if test -n "@HAVE_JRE@"; then # In this case, $CONF_JAVA is "jre". - test -z "$JAVA_VERBOSE" || echo "$CONF_JAVA $@" + test -z "$JAVA_VERBOSE" || echo "$CONF_JAVA $@" 1>&2 exec $CONF_JAVA "$@" else echo 'Java virtual machine not found, try setting $JAVA, then reconfigure' 1>&2 -- 2.34.1
>From b8f013422581af67cdfcddd3beaece2c86150579 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Thu, 10 Oct 2024 19:39:36 +0200 Subject: [PATCH 2/2] csharpcomp-script: Handle directories with spaces correctly. Reported by Michele Locati <mich...@locati.it>. * build-aux/csharpcomp.sh.in (command_for_print, command_for_eval, options_csc_for_print, options_csc_for_eval, sources_csc_for_print, sources_csc_for_eval): New variables. (sed_protect_1, sed_protect_2a, sed_protect_2b, sed_protect_2c, sed_protect_3a, sed_protect_3b): New variables, copied from build-aux/x-to-1.in. (func_add_word_to_command): New function, copied from build-aux/x-to-1.in. (func_add_word_to_options_csc, func_add_word_to_sources_csc): New functions. (options_csc, sources_csc): Remove variables. Use func_add_word_to_options_csc, func_add_word_to_sources_csc instead of augmenting them. Use options_csc_for_print, options_csc_for_eval, sources_csc_for_print, sources_csc_for_eval when invoking csc. * build-aux/csharpexec.sh.in (sed_quote_subst): Remove unused variable. --- ChangeLog | 21 +++++++++++ build-aux/csharpcomp.sh.in | 75 +++++++++++++++++++++++++++++--------- build-aux/csharpexec.sh.in | 1 - 3 files changed, 79 insertions(+), 18 deletions(-) diff --git a/ChangeLog b/ChangeLog index b427c5033c..0b47332948 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2024-10-10 Bruno Haible <br...@clisp.org> + + csharpcomp-script: Handle directories with spaces correctly. + Reported by Michele Locati <mich...@locati.it>. + * build-aux/csharpcomp.sh.in (command_for_print, command_for_eval, + options_csc_for_print, options_csc_for_eval, sources_csc_for_print, + sources_csc_for_eval): New variables. + (sed_protect_1, sed_protect_2a, sed_protect_2b, sed_protect_2c, + sed_protect_3a, sed_protect_3b): New variables, copied from + build-aux/x-to-1.in. + (func_add_word_to_command): New function, copied from + build-aux/x-to-1.in. + (func_add_word_to_options_csc, func_add_word_to_sources_csc): New + functions. + (options_csc, sources_csc): Remove variables. Use + func_add_word_to_options_csc, func_add_word_to_sources_csc instead of + augmenting them. + Use options_csc_for_print, options_csc_for_eval, sources_csc_for_print, + sources_csc_for_eval when invoking csc. + * build-aux/csharpexec.sh.in (sed_quote_subst): Remove unused variable. + 2024-10-10 Bruno Haible <br...@clisp.org> java{comp,exec}-script, csharp{comp,exec}-script: Improve debugging. diff --git a/build-aux/csharpcomp.sh.in b/build-aux/csharpcomp.sh.in index 58b53c6bff..2dec12af7e 100644 --- a/build-aux/csharpcomp.sh.in +++ b/build-aux/csharpcomp.sh.in @@ -64,21 +64,58 @@ func_tmpdir () } } +# In order to construct a command that invokes csc, we need 'eval', because +# some of the arguments may contain spaces. +command_for_print= +command_for_eval= +options_csc_for_print= +options_csc_for_eval= +sources_csc_for_print= +sources_csc_for_eval= +# Protecting special characters, hiding them from 'eval': +# Double each backslash. +sed_protect_1='s/\\/\\\\/g' +# Escape each dollar, backquote, double-quote. +sed_protect_2a='s/\$/\\$/g' +sed_protect_2b='s/`/\\`/g' +sed_protect_2c='s/"/\\"/g' +# Add double-quotes at the beginning and end of the word. +sed_protect_3a='1s/^/"/' +sed_protect_3b='$s/$/"/' +func_add_word_to_command () +{ + command_for_print="${command_for_print:+$command_for_print }$1" + word_protected=`echo "$1" | sed -e "$sed_protect_1" -e "$sed_protect_2a" -e "$sed_protect_2b" -e "$sed_protect_2c" -e "$sed_protect_3a" -e "$sed_protect_3b"` + command_for_eval="${command_for_eval:+$command_for_eval }$word_protected" +} +func_add_word_to_options_csc () +{ + options_csc_for_print="${options_csc_for_print:+$options_csc_for_print }$1" + word_protected=`echo "$1" | sed -e "$sed_protect_1" -e "$sed_protect_2a" -e "$sed_protect_2b" -e "$sed_protect_2c" -e "$sed_protect_3a" -e "$sed_protect_3b"` + options_csc_for_eval="${options_csc_for_eval:+$options_csc_for_eval }$word_protected" +} +func_add_word_to_sources_csc () +{ + sources_csc_for_print="${sources_csc_for_print:+$sources_csc_for_print }$1" + word_protected=`echo "$1" | sed -e "$sed_protect_1" -e "$sed_protect_2a" -e "$sed_protect_2b" -e "$sed_protect_2c" -e "$sed_protect_3a" -e "$sed_protect_3b"` + sources_csc_for_eval="${sources_csc_for_eval:+$sources_csc_for_eval }$word_protected" +} + sed_quote_subst='s/\([|&;<>()$`"'"'"'*?[#~=% \\]\)/\\\1/g' + options_mcs= -options_csc="-nologo" sources= -sources_csc= +func_add_word_to_options_csc "-nologo" while test $# != 0; do case "$1" in -o) case "$2" in *.dll) options_mcs="$options_mcs -target:library" - options_csc="$options_csc -target:library" + func_add_word_to_options_csc "-target:library" ;; *.exe) - options_csc="$options_csc -target:exe" + func_add_word_to_options_csc "-target:exe" ;; esac options_mcs="$options_mcs -out:"`echo "$2" | sed -e "$sed_quote_subst"` @@ -90,7 +127,7 @@ while test $# != 0; do arg=`cygpath -w "$arg"` ;; esac - options_csc="$options_csc -out:"`echo "$arg" | sed -e "$sed_quote_subst"` + func_add_word_to_options_csc "-out:$arg" shift ;; -L) @@ -103,20 +140,20 @@ while test $# != 0; do arg=`cygpath -w "$arg"` ;; esac - options_csc="$options_csc -lib:"`echo "$arg" | sed -e "$sed_quote_subst"` + func_add_word_to_options_csc "-lib:$arg" shift ;; -l) options_mcs="$options_mcs -reference:"`echo "$2" | sed -e "$sed_quote_subst"` - options_csc="$options_csc -reference:"`echo "$2" | sed -e "$sed_quote_subst"`".dll" + func_add_word_to_options_csc "-reference:$2.dll" shift ;; -O) - options_csc="$options_csc -optimize+" + func_add_word_to_options_csc "-optimize+" ;; -g) options_mcs="$options_mcs -debug" - options_csc="$options_csc -debug+" + func_add_word_to_options_csc "-debug+" ;; -*) echo "csharpcomp: unknown option '$1'" 1>&2 @@ -132,7 +169,7 @@ while test $# != 0; do arg=`cygpath -w "$arg"` ;; esac - options_csc="$options_csc -resource:"`echo "$arg" | sed -e "$sed_quote_subst"` + func_add_word_to_options_csc "-resource:$arg" ;; *.cs) sources="$sources "`echo "$1" | sed -e "$sed_quote_subst"` @@ -144,7 +181,7 @@ while test $# != 0; do arg=`cygpath -w "$arg"` ;; esac - sources_csc="$sources_csc "`echo "$arg" | sed -e "$sed_quote_subst"` + func_add_word_to_sources_csc "$arg" ;; *) echo "csharpcomp: unknown type of argument '$1'" 1>&2 @@ -179,25 +216,29 @@ else arg=`cygpath -w "$arg"` ;; esac - options_csc="$options_csc -lib:"`echo "$arg" | sed -e "$sed_quote_subst"` + func_add_word_to_options_csc "-lib:$arg" for file in `cd "$dotnet_runtime_dir" && echo [ABCDEFGHIJKLMNOPQRSTUVWXYZ]*.dll`; do case "$file" in *.Native.*) ;; - *) options_csc="$options_csc -reference:"`echo "$file" | sed -e "$sed_quote_subst"` ;; + *) func_add_word_to_options_csc "-reference:$file" ;; esac done + func_add_word_to_command dotnet csc="$dotnet_sdk_dir/Roslyn/bincore/csc.dll" case "@build_os@" in cygwin*) csc=`cygpath -w "$csc"` ;; esac - test -z "$CSHARP_VERBOSE" || echo dotnet "$csc" $options_csc $sources_csc 1>&2 - exec dotnet "$csc" $options_csc $sources_csc + func_add_word_to_command "$csc" + test -z "$CSHARP_VERBOSE" || echo "$command_for_print $options_csc_for_print $sources_csc_for_print" 1>&2 + eval "$command_for_eval $options_csc_for_eval $sources_csc_for_eval" + exit $? else if test -n "@HAVE_DOTNET_CSC@" || test -n "@HAVE_CSC@"; then - test -z "$CSHARP_VERBOSE" || echo csc $options_csc $sources_csc 1>&2 - exec csc $options_csc $sources_csc + test -z "$CSHARP_VERBOSE" || echo "csc $options_csc_for_print $sources_csc_for_print" 1>&2 + eval "csc $options_csc_for_eval $sources_csc_for_eval" + exit $? else echo 'C# compiler not found, try installing mono or dotnet, then reconfigure' 1>&2 exit 1 diff --git a/build-aux/csharpexec.sh.in b/build-aux/csharpexec.sh.in index c2ccd3b645..a7a5a277c0 100644 --- a/build-aux/csharpexec.sh.in +++ b/build-aux/csharpexec.sh.in @@ -59,7 +59,6 @@ func_tmpdir () } } -sed_quote_subst='s/\([|&;<>()$`"'"'"'*?[#~=% \\]\)/\\\1/g' libdirs_mono= libdirs_dotnet= prog= -- 2.34.1