| #!/bin/sh | |
| # Compile a C# program. | |
| # Copyright (C) 2003-2026 Free Software Foundation, Inc. | |
| # Written by Bruno Haible <bruno@clisp.org>, 2003. | |
| # | |
| # This program is free software: you can redistribute it and/or modify | |
| # it under the terms of the GNU General Public License as published by | |
| # the Free Software Foundation, either version 3 of the License, or | |
| # (at your option) any later version. | |
| # | |
| # This program is distributed in the hope that it will be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| # GNU General Public License for more details. | |
| # | |
| # You should have received a copy of the GNU General Public License | |
| # along with this program. If not, see <https://www.gnu.org/licenses/>. | |
| # This uses the same choices as csharpcomp.c, but instead of relying on the | |
| # environment settings at run time, it uses the environment variables | |
| # present at configuration time. | |
| # | |
| # This is a separate shell script, because the various C# compilers have | |
| # different command line options. | |
| # | |
| # Usage: /bin/sh csharpcomp.sh [OPTION] SOURCE.cs ... RES.resource ... | |
| # Options: | |
| # -o PROGRAM.exe or -o LIBRARY.dll | |
| # set the output assembly name | |
| # -L DIRECTORY search for C# libraries also in DIRECTORY | |
| # -l LIBRARY reference the C# library LIBRARY.dll | |
| # -O optimize | |
| # -g generate debugging information | |
| # func_tmpdir | |
| # creates a temporary directory. | |
| # Sets variable | |
| # - tmp pathname of freshly created temporary directory | |
| func_tmpdir () | |
| { | |
| # Use the environment variable TMPDIR, falling back to /tmp. This allows | |
| # users to specify a different temporary directory, for example, if their | |
| # /tmp is filled up or too small. | |
| : "${TMPDIR=/tmp}" | |
| { | |
| # Use the mktemp program if available. If not available, hide the error | |
| # message. | |
| tmp=`(umask 077 && mktemp -d -q "$TMPDIR/gtXXXXXX") 2>/dev/null` && | |
| test -n "$tmp" && test -d "$tmp" | |
| } || | |
| { | |
| # Use a simple mkdir command. It is guaranteed to fail if the directory | |
| # already exists. $RANDOM is bash specific and expands to empty in shells | |
| # other than bash, ksh and zsh. Its use does not increase security; | |
| # rather, it minimizes the probability of failure in a very cluttered /tmp | |
| # directory. | |
| tmp=$TMPDIR/gt$$-$RANDOM | |
| (umask 077 && mkdir "$tmp") | |
| } || | |
| { | |
| echo "$0: cannot create a temporary directory in $TMPDIR" >&2 | |
| { (exit 1); exit 1; } | |
| } | |
| } | |
| # 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= | |
| sources= | |
| 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" | |
| func_add_word_to_options_csc "-target:library" | |
| ;; | |
| *.exe) | |
| func_add_word_to_options_csc "-target:exe" | |
| ;; | |
| esac | |
| options_mcs="$options_mcs -out:"`echo "$2" | sed -e "$sed_quote_subst"` | |
| # On Windows, assume that 'dotnet' and 'csc' are native Windows programs, | |
| # not Cygwin programs. | |
| arg="$2" | |
| case "@build_os@" in | |
| cygwin*) | |
| arg=`cygpath -w "$arg"` | |
| ;; | |
| esac | |
| func_add_word_to_options_csc "-out:$arg" | |
| shift | |
| ;; | |
| -L) | |
| options_mcs="$options_mcs -lib:"`echo "$2" | sed -e "$sed_quote_subst"` | |
| # On Windows, assume that 'dotnet' and 'csc' are native Windows programs, | |
| # not Cygwin programs. | |
| arg="$2" | |
| case "@build_os@" in | |
| cygwin*) | |
| arg=`cygpath -w "$arg"` | |
| ;; | |
| esac | |
| func_add_word_to_options_csc "-lib:$arg" | |
| shift | |
| ;; | |
| -l) | |
| options_mcs="$options_mcs -reference:"`echo "$2" | sed -e "$sed_quote_subst"` | |
| func_add_word_to_options_csc "-reference:$2.dll" | |
| shift | |
| ;; | |
| -O) | |
| func_add_word_to_options_csc "-optimize+" | |
| ;; | |
| -g) | |
| options_mcs="$options_mcs -debug" | |
| func_add_word_to_options_csc "-debug+" | |
| ;; | |
| -*) | |
| echo "csharpcomp: unknown option '$1'" 1>&2 | |
| exit 1 | |
| ;; | |
| *.resources) | |
| options_mcs="$options_mcs -resource:"`echo "$1" | sed -e "$sed_quote_subst"` | |
| # On Windows, assume that 'dotnet' and 'csc' are native Windows programs, | |
| # not Cygwin programs. | |
| arg="$1" | |
| case "@build_os@" in | |
| cygwin*) | |
| arg=`cygpath -w "$arg"` | |
| ;; | |
| esac | |
| func_add_word_to_options_csc "-resource:$arg" | |
| ;; | |
| *.cs) | |
| sources="$sources "`echo "$1" | sed -e "$sed_quote_subst"` | |
| # On Windows, assume that 'dotnet' and 'csc' are native Windows programs, | |
| # not Cygwin programs. | |
| arg="$1" | |
| case "@build_os@" in | |
| cygwin*) | |
| arg=`cygpath -w "$arg"` | |
| ;; | |
| esac | |
| func_add_word_to_sources_csc "$arg" | |
| ;; | |
| *) | |
| echo "csharpcomp: unknown type of argument '$1'" 1>&2 | |
| exit 1 | |
| ;; | |
| esac | |
| shift | |
| done | |
| if test -n "@HAVE_MCS@"; then | |
| # mcs prints it errors and warnings to stdout, not stderr. Furthermore it | |
| # adds a useless line "Compilation succeeded..." at the end. Correct both. | |
| sed_drop_success_line='${ | |
| /^Compilation succeeded/d | |
| }' | |
| func_tmpdir | |
| trap 'rm -rf "$tmp"' HUP INT QUIT TERM | |
| 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 | |
| rm -rf "$tmp" | |
| exit $result | |
| else | |
| if test -n "@HAVE_DOTNET_SDK@"; then | |
| dotnet_runtime_dir=`dotnet --list-runtimes | sed -n -e 's/Microsoft.NETCore.App \([^ ]*\) \[\(.*\)\].*/\2\/\1/p' | sed -e 1q` | |
| dotnet_sdk_dir=`dotnet --list-sdks | sed -e 's/\([^ ]*\) \[\(.*\)\].*/\2\/\1/p' | sed -e 1q` | |
| # Add -lib and -reference options, so that the compiler finds Object, Console, String, etc. | |
| arg="$dotnet_runtime_dir" | |
| case "@build_os@" in | |
| cygwin*) | |
| arg=`cygpath -w "$arg"` | |
| ;; | |
| esac | |
| func_add_word_to_options_csc "-lib:$arg" | |
| for file in `cd "$dotnet_runtime_dir" && echo [ABCDEFGHIJKLMNOPQRSTUVWXYZ]*.dll`; do | |
| case "$file" in | |
| *.Native.*) ;; | |
| *) 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 | |
| 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_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 | |
| fi | |
| fi | |
| fi |