#!/bin/sh # Compile a C# program. # Copyright (C) 2003-2024 Free Software Foundation, Inc. # Written by Bruno Haible , 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 . # 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