#! /usr/bin/bash

#
# The master compiler wrapper that makes Microsoft Visual C++ 6.0
# look like a Unix-y cc to the configure script and the Makefile.
# Give this file as your compiler (CC=wricc) to configure, and all
# will be well.
#
# The options are the same as that used for pcc by David Korn,
# distributed with UWIN.

# Todo:
# Find CL and LINK.  For now, it's OK to use hardcoded paths, or
#   even to assume that they're already on the PATH.  (i.e. this
#   script only works if you installed MSVC with the "Build from
#   command line" option.)
#
# pcc distinguishes between 'ld' and 'ar', which latter seems to be
#   LINK with different arguments.
#
# How to handle DLLs is still unclear.  PCC doesn't actually seem to
#  do anything useful here.
#
# Environment-variable overrides for things in here: CL and AS,
#   op_cpu (the ability to force it to something, ignoring -mcpu)

# For the LINK stage, we will pass everything off to wrild.

# Names of tools we are using.
AS=as
CL=cl

# declare some arrays and integers
declare -i optshift
declare -i c_file_count
declare -i s_file_count
declare -i o_file_count
declare -i file_order_count
declare -i out_obj_count
declare c_out_obj_offset
declare s_out_obj_offset
declare o_out_obj_offset

declare -a c_files      # stores names of files ending in .c or .i
                        # in the order they appear on the command line.
declare -a s_files      # stores names of files ending in .s
                        # in the order they appear on the command line.
declare -a o_files      # stores names of files ending in .o, .obj, .lib,
                        # or any other that is passed directly to the linker,
                        # in the order they appear on the command line.
declare -a out_obj_files   # names of generated object files

declare -a file_order   # Stores the type of files (c, s, o) in the
                        # original order they appeared on the command line.
                        # From this, and the fact that the c_files, s_files,
                        # and o_files appear in order, it's possible to
                        # reconstruct the original filename list in order.

# Initialize variables used for storing and counting filename arguments.
let c_file_count=0
let s_file_count=0
let o_file_count=0
let file_order_count=0
let out_obj_count=0
let c_out_obj_offset=0  # start of .c/.i-based object files in out_obj_files
let s_out_obj_offset=0  # start of .s-based object files in out_obj_files
let o_out_obj_offset=0  # start of .o/.obj-based object files in out_obj_files

# Set defaults
op_nolink=0   # The -c option: do not link, just compile to a .o file.
op_static=1   # -Bstatic, build a static library, not a DLL
warnlevel=3   # warning level, set with -W
outfile=	# The -o option: "output file" (use dependent on other flags)
op_cflag=0	# -C option - do not strip comments - requires -E or -P
op_eflag=0	# -E option - preprocess source w/line #s - >stdout or outfile
op_pflag=0	# -P option - preprocess source w/o line#s - >file.i or outfile
op_objext=o	# -Xo option - extension to use for object files
op_debug=0	# -g option - produce version w/symbols & debug C run-time
op_verbose=0	# -V option - show trace information as we go
op_optimize=0   # -O option - turn on optimization
op_optimize_level=2   # -O# option - set optimization level (bare -O means -O2)
op_inlining_level=none  # -Ob option (-Ob0, -Ob1, -Ob2).  "none" if not set.
op_rtl="T"	# -M option - choose run-time library (default: Multithreaded)
		# if op_debug, then "d" will be appended (e.g. -MTd).
op_subsystem=           # -T option.  console or windows (gui) program.
op_subsystem_defined=   # subsystem implied by -D_CONSOLE, -D_WINDOWS, or -DPIC
op_Microsoft=0		# -q flag: turn on incompatible Microsoft extensions
preprocess_only=0   # -P or -E option specified; stop before compilation.
output_redirect=    # any "> foo" options to add to the command line

op_debugging_print=0	# have this script (& wrild) print debugging info

# The command line that follows CL.
ccline="/nologo /c /DWIN32 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE " 

ldargs=			# Arguments to pass to wrild.
ldfinals=		# Arguments to pass to wrild at the end of the line.
includeline=		# Include directives.  Passed to CL and AS.

# Some subfunctions.

# Append argument to ccline, the flags to be passed to CL.
function Append_To_CCLine
{
	ccline="$ccline$1 "
}

# Append argument to includeline, a bunch of -I directives passed to CL and AS.
function Append_To_Includeline
{
	includeline="$includeline-I\"$1\" "
}

# Append argument to ldargs, the flags to be passed to wrild.
function Append_To_LDArgs
{
	ldargs="$ldargs$1 "
}

# Append argument to ldfinals, the arguments that appear at the end of
# the wrild command-line invocation.  (In case order matters.)
function Append_To_LDFinals
{
	ldfinals="$ldfinals$1 "
}

# get_optarg is used to find the arguments to options.
# $optset is the remainder of the options in this command-line argument
# (e.g. a command-line argument might be -W2; in this case the
#  remainder after W is "2").
# $nextarg is the next command-line argument (e.g. for -o foo.o, the
#  next argument is foo.o.
# Returns argument in optarg.  Returns optshift=0 if argument immediately
# followed option (-W2), optshift=1 if there was an extra command-line
# argument (-o foo.o).
function get_optarg
{
	# If text immediately follows the option, then that's
	# the option's argument
	if [ -n "$optset" ]; then
		optarg="$optset"
		let optshift=0
		optset=
	# If no following text, then next command-line arg is the argument
	elif [ -n "$nextarg" ]; then
		optarg="$nextarg"
		let optshift=1
	# It's a fatal error if option is at the end of the line
	else
		echo "Missing required argument for option -$opt" >&2
		Fail_Usage
	fi
}

# Print the usage message and terminate.
function Fail_Usage
{
	local usage
	local ublanks

	usage="Usage: `basename $scriptname`"
	ublanks="${usage//?/ }"  # blanks the same length as usage
	echo   "$usage [-cghqCEPSVZ] [-e entry] [-l library]" >&2
	echo "$ublanks [-mcpu=cpu] [-o outfile] [-s sedcommand]" >&2
	echo "$ublanks [-u name] [-B binding] [-D name[=val]]" >&2
	echo "$ublanks [-G] [-Gexport-all-symbols] [-I dir]" >&2
	echo "$ublanks [-L dir] [-M runtime] [-O[#]] [-Ob#]" >&2
	echo "$ublanks [-T subsystem] [-U name] [-W level]" >&2
	echo "$ublanks [-Y [pass,]argument] [--export-all-symbols]" >&2
	echo "$ublanks [--output-def deffile] [--help]  [file ...]" >&2
	echo
	echo "Enter \"`basename $scriptname` -h\" for help."
	exit 1;
}

# Handle the -Y options.  These options let you pass arguments
# directly to CL and LINK, the underlying Microsoft tools.
# On entry, $1 contains the argument to the -Y option, that is,
# the portion following -Y.
function Parse_PassThrough_Options
{
	# Allowable syntax for the -Y option:
	#  -Yc,foo - add foo verbatim to the CL options
	#  -Yfoo   - same as -Yc,foo
	#  -Yl,foo - add foo verbatim to the LINK options
	#  -YL,foo - add foo verbatim to the *end* of the LINK line

	# Note that if foo contains spaces, it is necessary to quote
	# it twice: once for the invocation of wricc, and again (\")
	# for the invocation of cl.
	#    e.g. -Yc,"/I \"\Program Files\Wolfram Research\""
	# The outer quotes keep the argument of /Y together; the
	# inner ones make the /I path quoted on the cl command line.
	if [ x"${1#c,}" != x"$1" ]; then
		Append_To_CCLine "${1#c,}"	# snip off leading c,
	elif [ x"${1#l,}" != x"$1" ]; then
		Append_To_LDArgs "-Yl,\"${1#l,}\""	# snip off leading l,
	elif [ x"${1#L,}" != x"$1" ]; then
		Append_To_LDArgs "-YL,\"${1#L,}\""	# snip off leading L,
	else
		Append_To_CCLine "$1"		# -Yfoo is like -Yc,foo
	fi
}

# Print a long help message and quit.  This is triggered by the -h option.
function Print_Help_And_Exit
{
echo -n "`basename $scriptname` "
cat <<EOF
is a wrapper for the Microsoft Visual C compiler
CL that makes it act like a Unix-style compiler.  It is intended
for use in building Autoconf-based packages.  It runs under
Cygwin, and requires either that the Visual C tools be installed
with environment variables set up to allow them to run from the
command line, or that VCVARS32.BAT be run beforehand.  This
script handles only compilation and assembly; linking is done
by passing options and filenames to wrild.

EOF
echo "Usage: `basename $scriptname` [ options ] [file ...]"
cat <<EOF
OPTIONS
  -c              Suppress linking and produce a .o file for each
                  source file.
  -e entry        Passed to the linker to set the entry point for
                  the program or dynamically linked library.
  -g              Produce a debug build.  Optimization is turned
                  off (unless -O# is specified), a debug version of
                  the runtime library is linked, and the -g flag is
                  passed to the linker to produce symbol table
                  information (.PDB file.)
  -h              Prints this help message.
  -l library      Passed to the linker to link with object library
                  "library".  [Currently not working.]
    -link         If Microsoft extensions are enabled (-q), all
                  subsequent text on the command line is passed
                  directly to LINK.  (Libtool always tries to use
                  this syntax, and so -q must be set globally during
                  an Autoconf build. See WRICCFLAGS below.)
  -march=cpu      Set the CPU target (a/k/a architecture) to one of:
   -mcpu=cpu         i386, i486, pentium|i586, pentiumpro|ppro|686.
                       Not meaningful with Visual C.
  -o outfile      Use the pathname outfile for the output file
                  instead of the default.  If compiling and linking,
                  this is the executable or DLL name; if compiling
                  only (-c), this is the object file name; if only
                  preprocessing (-E, -P), this is the target text file.
  -q              Turn on Microsoft-compatible extensions.  This
                  activates functionality in the compiler and linker
                  that reduces generality.  Enables the following:
                      -link  -  compiler option
  -s sedcommand   Passed to the linker to modify the executable or DLL
                  output file name, by sed-editing it with "sedcommand"
                  before it's used.  (This can be useful when an
                  automatic build system like libtool is creating a DLL
                  with a bogus name, since you cannot rename a DLL once
                  it's created.)
  -u name         Passed to the linker to make symbol "name" an
                  unsatisfied external symbol so it will be searched
                  for in a library.
  -B binding      binding can be one of the following, optionally
                  preceded by no[-] to indicate the inverse:
                      dynamic - Build a dynamic-link library.
                      static  - Build a static library.
                   [This flag doesn't work.  I carried it over from
                    pcc but I don't understand what it does there.]
  -C              Do not strip comments while preprocessing.  This
                  option requires the -E or -P option be present.
  -D name[=val]   Define "name" as if by a C-language #define.
                  If no "val" is given, "name" is defined as 1.
  -E              Run the source file through the CL preprocessor.
                  Sends the output to stdout, unless an output file
                  is specified with the -o option.  Includes #line
                  number information.  (Contrast to -P option.)
  -G              Passed to the linker to build a dynamically-linked
                  library.
  -Gexport-all-symbols
                  Passed to the linker to indicate that all symbols
                  in a dynamically-linked library should be exported
                  (not just those marked __declspec(dllexport)).
                  Does nothing if not linking a DLL.  This is the
                  standard export behavior for Unix shared libraries.
  -I dir          Add dir to the list of directories in which to
                  search for #include files with relative filenames.
                  The preprocessor first searches for #include files
                  in the directory containing the source file, then
                  in directories named with the -I options, and then
                  in directories named in the INCLUDE environment
                  variable.
  -L dir          Passed to the linker to add "dir" to the list of
                  directories containing object-library files.
  -M runtime      Link with one of these C "runtime" libraries:
                      L  -  LIBC: Single-thread application only.
                      T  -  LIBCMT: Multithread application.
                      D  -  MSVCRT runtime DLL (multithread safe).
                  If -g flag is present, use debug versions of these
                  runtimes (LIBCd, LIBCMTd, MSVCRTd).  Default is T.
  -O[#]           Set optimization level #.  (If # is omitted, its
                  value is 2.)  # must immediately follow -O.
                      -O1  -  optimize for space
                      -O2  -  optimize for speed
                      -Ox  -  full optimization
  -Ob#            Control inlining of functions.
                      -Ob0 -  no functions will be inlined
                      -Ob1 -  functions marked __inline are inlined.
                      -Ob2 -  "auto-inlining":  compiler will inline
                              functions marked __inline, and any other
                              function it chooses.
                  Note that unless an __inline function is declared
                  __forceinline, the compiler may choose not to inline it.
  -P              Run the source file through the CL preprocessor.
                  Sends the output to an output file with the same
                  basename and a .i suffix, unless an output file
                  is specified with the -o option.  Does not include
                  #line number information.  (Contrast to -E option.)
  -S              Produce an assembly source file with suffix .asm
                  in Intel assembly format.  (Cannot be assembled by
                  this script, which uses the GNU assembler.)
  -T subsystem    Set the subsystem type for the executable.
                     console  -  a console-mode program
                     windows  -  a standard Windows program
                  The default is -Tconsole.
  -U name         Remove any initial definition of the C preprocessor
                  symbol "name".
  -V              Verbose mode.  Show the command lines given to the
                  underlying tools.
  -W level        Set the warning level to "level".  -W0 is lowest,
                  -W4 is highest.  -Wall means -W4, and -WX causes
                  warnings to be treated as errors.  Default is -W3.
  -X ext          Set the default object extension to .ext
                  The default is o, but obj is often desired.
  -Y [pass,]argument
                  Pass the given argument to the underlying tool
                  for one of these passes:
                     c  -  pass argument to CL
                     l  -  pass argument to LINK
                     L  -  pass argument to LINK at the end of the line
                  If no pass is specified, the default is c.
                  Note that if "argument" contains spaces, it is
                  necessary to quote it twice: once for the invocation
                  of this script, and again (\\") for the invocation of
                  CL.
  -Z              Passed to the linker to add a symbol table.
                   [This flag doesn't work.  I carried it over from
                    pcc but I don't understand what it does there.]
  --export-all-symbols    Same as -Gexport-all-symbols
  --help          Prints this help message.
  --output-def deffile
                  When constructing a DLL, produce a .DEF file with the
                  name "deffile"
		


FILES
  *.c, *.i        C files compiled with CL.

  *.s             GNU-format assembly language files assembled with as.

  *.obj, *.o, *.lib, *.exp, *.res, *.def
                  Files passed on to the linker.

  All source files are passed to the linker in the order given on the
  command line.

ENVIRONMENT VARIABLE
  The contents of the environment variable WRICCFLAGS, if exported,
  will be added to the beginning of the command line.  This can give
  you some control over the behavior of the compiler and linker in
  automated build script situations.

MACROS
  The default Microsoft Visual C automatic macros are defined.
  Specifically, _DEBUG is defined when -g is given (or a -M*d
  runtime is used), and _MT is defined when a multithread-safe
  runtime (-MT, -MD) is used.  _WIN32 and _MSC_VER are always defined.

  _CONSOLE is defined if this is a console-mode program (-Tconsole)
  _WINDOWS is defined if this is a windows-mode program (-Twindows)
      or if -DPIC is present.
  NDEBUG is defined if _DEBUG is not.
  _WRICC_INLINING is defined if the compiler is inlining __inline
      functions.
  _WRICC_NOINLINING is defined if the compiler is not inlining
      __inline functions.
    Inlining occurs by default if optimization is on (-O1 -O2 or -Ox).
    It does not occur by default if no optimization is on (no -O).
    But using the -Ob# option can explicitly turn it on and off.
EOF

exit 0;
}

# Process the command line arguments.  This function iterates through
# all the arguments on the command line, and sets lots of variables.
# Each of the command-line options sets corresponding flags and
# string variables, indicating its content.  Filenames are stored
# into the c_files, s_files, and o_files arrays, depending on whether
# they are .c files, .s files, or other files.
function Process_Command_Line_Arguments
{
while [ $# -ne 0 ]; do
    arg="$1"
    nextarg="$2"
    if [ "${arg:0:1}" = "-" ]; then
	# the argument begins with a -.  option or set of options.
	optset="${arg#-}"		# strip off the leading -

	# now iterate through all the letters in optset
	while [ -n "$optset" ]; do
	    opt="${optset:0:1}"		# pull letter off the front
	    optset="${optset:1}"	# drop that letter from optset
	    let optshift=0
	    case "$opt" in
		B ) get_optarg
		    case "$optarg" in
			static|nodynamic|no-dynamic ) op_static=1 ;;
			dynamic|nostatic|no-static ) op_static=0 ;;
			* ) echo "-B$optarg: must be static, dynamic,"\
			    "nostatic, nodynamic." >&2
		    	    Fail_Usage ;;
		    esac
		    Append_To_LDArgs "-B $optarg" ;;
		C ) op_cflag=1
		    Append_To_CCLine "/C" ;;
		D ) get_optarg
		    Append_To_CCLine "/D\"$optarg\""
		    case "$optarg" in
		        _CONSOLE|"_CONSOLE" )
			    if [ x"$op_subsystem_defined" = x"windows" ]; then
			     echo "Cannot have both -D_CONSOLE and -D_WINDOWS/PIC." >&2
			     Fail_Usage
			    fi
			    op_subsystem_defined="console" ;;
		        _WINDOWS|"_WINDOWS"|PIC|"PIC" )
			    if [ x"$op_subsystem_defined" = x"console" ]; then
			     echo "Cannot have both -D_CONSOLE and -D_WINDOWS/PIC." >&2
			     Fail_Usage
			    fi
			    if [ x"$optarg" = x"PIC" ] || [ x"$optarg" = x"\"PIC\"" ]; then
				Append_To_CCLine "/D_WINDOWS"
			    fi
			    op_subsystem_defined="windows" ;;
			*) ;;
		    esac ;;
		E ) op_eflag=1
		    preprocess_only=1 ;;
		G ) # -G is a linker option for creating DLLs.
		    # Like -O it might be followed by other text.
		    Append_To_LDArgs "-G$optset"
		    optset=
		    ;;
		I ) get_optarg
		    Append_To_Includeline "$optarg" ;;
		L ) get_optarg
		    Append_To_LDArgs "-L$optarg" ;;
		M ) get_optarg
		    op_rtl="$optarg" ;;

		    # The -O flag can be followed by an optimization
		    # level or b, not separated by a space.  (-O, -Ob1
		    # and -O2 are legal, but -O 2 is not.)
		O ) if [ -n "$optset" ]; then
			case "$optset" in
			    [1-4x] ) op_optimize_level=$optset
		                     op_optimize=1 ;;
		            b[0-2] ) op_inlining_level="${optset#b}" ;;
			    * ) echo "Invalid optimization setting: -O$optset" >&2
				Fail_Usage ;;
			esac
			optset=
		    else
		        op_optimize=1
		    fi ;;
		P ) op_pflag=1 
		    preprocess_only=1 ;;
		Q ) op_debugging_print=1
		    # -Q is the SOOPER SEKRIT DEBUGGING PRINT INFO flag
		    Append_To_LDArgs "-Q" ;;
		S ) Append_To_CCLine "/FAs /c"
		    op_nolink=1 ;;
		T ) get_optarg
		    case "$optarg" in
			console|CONSOLE ) op_subsystem="console" ;;
			windows|gui|WINDOWS|GUI ) op_subsystem="windows" ;;
			* ) echo "Invalid subsystem: -T$optarg: must be"\
			         "console or windows." >&2
			    Fail_Usage ;;
		    esac ;;
		U ) get_optarg
		    Append_To_CCLine "/U$optarg" ;;
		V ) op_verbose=1
		    Append_To_LDArgs "-V" ;;
		W ) get_optarg
		    warnlevel=$optarg
		    case "$warnlevel" in
			[01234X] ) : ;;
			all ) warnlevel=4 ;;
			* ) echo "-W$warnlevel: must be 0-4, X or all." >&2
			    Fail_Usage ;;
		    esac
		    if [ x"$warnlevel" = x"4" -o x"$warnlevel" = x"X" ]; then
		        linker_warnlevel=3
		    else
		        linker_warnlevel=$warnlevel
		    fi
		    Append_To_LDArgs "-W$linker_warnlevel" ;;
		X ) get_optarg
		    case "$optarg" in
			o=* ) op_objext="${optarg#o=}" ;;
			* ) echo "-X$optarg: Illegal -X option." >&2
			    Fail_Usage ;;
		    esac ;;
		Y ) get_optarg
		    Parse_PassThrough_Options "$optarg" ;;
		Z ) Append_To_LDArgs "-Z" ;;

		c ) op_nolink=1 ;;
		e ) get_optarg
		    Append_To_LDArgs "-e $optarg" ;;
		g ) op_debug=1 ;;
                h ) Print_Help_And_Exit ;;
		l ) get_optarg
		    if [ $optarg = "ink" -a $op_Microsoft = 1 ]; then
		        # Support the MS extension -link option, which
		        # means "all following text is passed to LINK."
			# This means you can't have a library called "ink"
		        shift $((optshift+1))
		        while [ $# -ne 0 ]; do
			    Append_To_LDArgs "-Yl,\"$1\""
		            shift
		        done
		        let optshift=-1	# We have shifted all arguments!
		    else
		        Append_To_LDFinals "-l$optarg"
		    fi ;;
		m ) get_optarg
		    case "$optarg" in
			arch=*|cpu=* ) op_cpu="${optarg#*=}" ;;
			* ) echo "-m$optarg: Illegal option." >&2
			    Fail_Usage ;;
		    esac ;;
		o ) get_optarg
		    outfile="$optarg"
		    Append_To_LDArgs "-o $optarg" ;;
		q ) op_Microsoft=1 ;;
		s ) get_optarg
		    Append_To_LDArgs "-s $optarg" ;;
		u ) get_optarg
		    Append_To_LDArgs "-u $optarg" ;;

		- ) case x"$optset" in
			x"export-all-symbols" ) 
			    Append_To_LDArgs "--export-all-symbols"
			    ;;
			x"output-def" )
			    opt="${opt}${optset}"
			    optset=
			    get_optarg
			    outfile="$optarg"
			    Append_To_LDArgs "--output-def $optarg"
			    ;;
			x"help" )
			    Print_Help_And_Exit ;;
			* ) echo "$scriptname: illegal option -$opt"
			    Fail_Usage ;;
			esac
			optset=
			;;
		* ) echo "$scriptname: illegal option -$opt"
		     Fail_Usage ;;
	    esac
	done	# with while loop handling multiple options in an argument

    else
	# The argument does not begin with a "-".  It's a filename.
 	# Filenames are stored into the c_files, s_files, and o_files
	# arrays, depending on whether they are .c files, .s files,
	# or other files.  The original command-line file order is
	# recorded using the file_order array; this is used to ensure
	# that wrild is passed object files in the same order as
	# wricc's source files.
	filename="$1"
	case "$filename" in
	    *.c|*.i|*.C|*.I ) c_files[$c_file_count]="$filename"
		  let c_file_count=$c_file_count+1
		  file_order[$file_order_count]=c ;;
	    *.s|*.S ) s_files[$s_file_count]="$filename"
		  let s_file_count=$s_file_count+1
		  file_order[$file_order_count]=s ;;
	    *.o|*.obj|*.lib|*.exp|*.res|*.def|*.OBJ|*.LIB|*.EXP|*.RES|*.DEF )
			o_files[$o_file_count]="$filename" 
			let o_file_count=$o_file_count+1
			file_order[$file_order_count]=o ;;
	    * ) echo "Bad filename: $filename" >&2
		Fail_Usage ;;
	esac
	let file_order_count=$file_order_count+1

	let optshift=0
    fi

    # skip to next command-line argument.  if an option used up a
    # command-line argument (e.g. "-o foo.o"), then optshift=1 and
    # we shift twice
    shift $((optshift+1))
done	# with while loop handling all command-line arguments.
}

# End of subfunctions.

# Begin the main portion of the wricc shell script.

scriptname="$0"

# Read all the command-line arguments and set lots of flags and
# variables based on them.  Everything past this point is just
# analyzing and translating the results.
#
# We invoke the function using eval in order to pass all
# arguments in WRICCFLAGS through the tokenizer, just as if
# they were entered on the command line.

cmdline="Process_Command_Line_Arguments $WRICCFLAGS $@"
eval "$cmdline"

# The -E and -P options run the C preprocessor only.
if [ $preprocess_only = 1 ]; then	# no compiling means no linking
	op_nolink=1
fi

Append_To_CCLine "/W$warnlevel"

# Analyze the subsystem for the executable implied by the -T option
# and the presence of any -D_CONSOLE or -D_WINDOWS macros.
# The default is "console".
# (When we handle building a DLL the default should be "windows".)

# First check for conflict between -T option and -D macros.

if [ -n "$op_subsystem" -a -n "$op_subsystem_defined" -a x"$op_subsystem" != "$op_subsystem_defined" ]; then
    echo -n "Subsystem implied by -D" >&2
    if [ x"$op_subsystem_defined" = x"console" ]; then
	echo -n "_CONSOLE" >&2
    else
	echo -n "_WINDOWS" >&2
    fi
    echo " conflicts with subsystem specified by -T$op_subsystem." >&2
    Fail_Usage
fi

# Let the subsystem be the one implied by the -D macros if it wasn't set
# by the -T option.
if [ -z "$op_subsystem" ]; then
    if [ -n "$op_subsystem_defined" ]; then
	op_subsystem="$op_subsystem_defined"
    else
	op_subsystem="console"
    fi
fi

# Add appropriate defines for subsystem (_CONSOLE or _WINDOWS) unless
# they are already there.
if [ -z "$op_subsystem_defined" ]; then
    case "$op_subsystem" in
	console ) Append_To_CCLine "/D_CONSOLE" ;;
	windows ) Append_To_CCLine "/D_WINDOWS" ;;
	* ) echo "INTERNAL ERROR: somehow op_subsystem is $op_subsystem,"\
		"not console or windows!" >&2
	    Fail_Usage ;;
    esac
fi

# Add appropriate flag to the linker for console vs. windows
Append_To_LDArgs "-T$op_subsystem"

# Analyze the combination of -O# optimization, debug non-optimization,
# and -Ob# inline level to determine whether inlining is happening or
# not.  Set the _WRICC_NOINLINING or _WRICC_INLINING define macros
# to indicate which one.
if [ $op_debug = 1 -o $op_optimize = 0 ]; then
    case "$op_inlining_level" in
        none|0 ) inlining_define=0 ;;
        *      ) inlining_define=1 ;;
    esac
else
    case "$op_inlining_level" in
        0 ) inlining_define=0 ;;
        * ) inlining_define=1 ;;
    esac
fi

if [ $inlining_define = 0 ]; then
    Append_To_CCLine "/D_WRICC_NO_INLINING"
else
    Append_To_CCLine "/D_WRICC_INLINING"
fi

# Analyze any preprocessing-only options.

if [ $op_eflag = 1 -a $op_pflag = 1 ]; then
	echo "Can't specify both -E and -P simultaneously." >&2
	Fail_Usage
fi
if [ $op_cflag = 1 -a $preprocess_only = 0 ]; then
	echo "Can't specify -C without either -E or -P." >&2
	Fail_Usage
fi
if [ $op_eflag = 1 ]; then		# -E => /E
	Append_To_CCLine "/E"
	if [ -n "$outfile" ]; then	# -E -o outfile => /E > outfile
		output_redirect="> \"$outfile\""
	fi
elif [ $op_pflag = 1 ]; then
	if [ -n "$outfile" ]; then	# -P -o outfile => /E /EP > outfile
		Append_To_CCLine "/E /EP"
		output_redirect="> \"$outfile\"" 
	else # no -o output file, send to .i file  (-P => /P /EP)
		Append_To_CCLine "/P /EP"
	fi
fi

# Analyze run-time library option.  Debug build means run-time library
# name ends in "d", and vice versa.
if [ x"${op_rtl: -1}" = x"d" ]; then
	op_debug=1
elif [ $op_debug = 1 ]; then
	op_rtl="$op_rtl""d"
fi

if [ $op_debug = 1 ]; then
	Append_To_LDArgs "-g"
fi

Append_To_CCLine "/M$op_rtl"
Append_To_LDArgs "-M$op_rtl"

# Analyze debugging and optimization options.
if [ $op_optimize = 1 ]; then
	Append_To_CCLine "/O$op_optimize_level"
elif [ $op_debug = 1 ]; then	# debug and no optimization
	Append_To_CCLine "/Od"
fi

if [ $op_debug = 1 ]; then
	Append_To_CCLine "/Zi"	# Program database (.PDB) debug info file
fi

if [ $op_inlining_level != "none" ]; then
	Append_To_CCLine "/Ob$op_inlining_level"
fi

# Add NDEBUG define if this is not a debug version.
# (_DEBUG is automatically defined by CL for debug versions.)
if [ $op_debug = 0 ]; then
	Append_To_CCLine "/DNDEBUG"
fi

let total_file_count=$c_file_count+s_file_count+$o_file_count

if [ $total_file_count -eq 0 ]; then
	echo "No files specified!  Nothing to do." >&2
	Fail_Usage
fi

if [ $preprocess_only = 1 ]; then

	# Can only preprocess non-assembler source files.

	if [ $((c_file_count)) -eq 0 ]; then
		echo "No .c or .i files to preprocess!  Nothing to do." >&2
		Fail_Usage
	fi

else  # not preprocess_only : full compile

	# If there is more than one source file, and the -o option
	# specified an object file name (i.e. no linking), then it
	# is impossible to continue: which source file corresponds
	# to the object file?

	if [ $((total_file_count-o_file_count)) -gt 1 -a $op_nolink = 1 -a -n "$outfile" ]; then
		echo "Cannot have -c (or -S) and -o with more than one source file." >&2
		Fail_Usage
	fi

	# Now walk through the various source file names supplied
	# on the command line, and work out their corresponding
	# object file names.  Basically this means "snip off the
	# path and extension and append .o" (except for the obj
	# files already supplied, which are copied unchanged).

	if [ $op_nolink = 1 -a -n "$outfile" ]; then
		# If -o file supplied to -c build, then it gives the name
		# of the one source file we're allowed to build.
		out_obj_files[0]="$outfile"
		let out_obj_count=1;
	else
		# Output object file name not specified, so work them out.
		# Strip off all but file base name (no path, no extension)
		# and append .o (or .obj).

		for fn in "${c_files[@]}"; do
			fn="${fn##*[/\\]}"
			out_obj_files[$out_obj_count]="${fn%.[ci]}.$op_objext"
			let out_obj_count=$out_obj_count+1
		done
		let s_out_obj_offset=$out_obj_count
		for fn in "${s_files[@]}"; do
			fn="${fn##*[/\\]}"
			out_obj_files[$out_obj_count]="${fn%.s}.$op_objext"
			let out_obj_count=$out_obj_count+1
		done
		let o_out_obj_offset=$out_obj_count
		for fn in "${o_files[@]}"; do
			out_obj_files[$out_obj_count]="$fn"
			let out_obj_count=$out_obj_count+1
		done
	fi

	# If there are any .s files, assemble them into object
	# files now.  (There is no preprocessing for .s files.)

	let i=0;
	while [ $i -lt $s_file_count ]; do
		outname="${out_obj_files[$((s_out_obj_offset+i))]}"
		cmdline="$AS $includeline -o \"$outname\" \"${s_files[i]}\""
		if [ $op_verbose = 1 ]; then
			echo "$cmdline"
		fi
		eval "$cmdline"
		es=$?
		if [ $es != 0 ]; then
			exit $es	# AS failed!
		fi
		let i=$i+1
	done
fi

# Time to invoke the compiler.  Compile (or preprocess) all the .c
# and .i files as C programs.
let i=0;
while [ $i -lt $c_file_count ]; do

	# Determine the corresponding .o file for this .c/.i file
	if [ $preprocess_only = 0 ]; then
		outname="${out_obj_files[$((c_out_obj_offset+i))]}"
		outpath="${outname%/*}"   # output file path (without filename)
		outopt="-Fo\"$outname\""

		# If output file goes into a subdirectory, so does the
		# debugging information file (VC60.PDB).
		if [ $op_debug = 1 -a "$outpath" != "$outname" ]; then
			outopt="$outopt /Fd\"$outpath\\\\\""
		fi
	else
		# If we are just preprocessing then there is no object file
		outopt=
	fi

	cmdline="$CL $ccline$includeline$outopt /Tc\"${c_files[i]}\" $output_redirect"
	if [ $op_verbose = 1 ]; then
		echo "$cmdline"
	fi
	eval "$cmdline"
	es=$?
	if [ $es != 0 ]; then
		exit $es		# cl failed!
	fi
	let i=$i+1
done

if [ $op_debugging_print = 1 ]; then
echo "------------------------------------------------------------------------"

echo "Here are the files."
echo "C files (c_file_count=$c_file_count) (c_out_obj_offset=$c_out_obj_offset):"
for i in "${c_files[@]}"; do
	echo $i
done
echo "Assembly files (s_file_count=$s_file_count) (s_out_obj_offset=$s_out_obj_offset):"
for i in "${s_files[@]}"; do
	echo $i
done	
echo "Object files (o_file_count=$o_file_count) (o_out_obj_offset=$o_out_obj_offset):"
for i in "${o_files[@]}"; do
	echo $i
done
echo "-- total_file_count=$total_file_count"
echo "File order indicators (file_order_count=$file_order_count):"
for i in "${file_order[@]}"; do
	echo $i
done
echo "Output OBJ files: (out_obj_count=$out_obj_count)"
for i in "${out_obj_files[@]}"; do
	echo $i
done
echo ""

echo "No complications, only some options:"
echo "op_nolink=$op_nolink"
echo "op_static=$op_static"
echo "warnlevel=$warnlevel"
echo "op_cflag=$op_cflag"
echo "op_eflag=$op_eflag"
echo "op_pflag=$op_pflag"
echo "op_debug=$op_debug"
echo "op_verbose=$op_verbose"
echo "op_optimize=$op_optimize"
echo "op_optimize_level=$op_optimize_level"
echo "op_inlining_level=$op_inlining_level"
echo "op_rtl=$op_rtl"
echo "op_objext=$op_objext"
echo "op_subsystem=$op_subsystem"
echo "op_subsystem_defined=$op_subsystem_defined"
echo "op_Microsoft=$op_Microsoft"
echo "preprocess_only=$preprocess_only"

echo "outfile=$outfile"

echo "ccline=$ccline"
echo "ldargs=$ldargs"
echo "output_redirect=$output_redirect"
echo "ldfinals=$ldfinals"
echo "------------------------------------------------------------------------"
fi  # $op_debugging_print = 1

if [ $op_nolink = 1 ]; then
	exit 0;		# We are done!
fi

# Time to pass off the rest of the work to wrild.

# wrild is in the same directory as wricc.  what directory is that?
script_dir=`dirname "${0}"`
script_dir=`cd "${script_dir}"; pwd`

cmdline="$script_dir/wrild $ldargs"

# Pass the object file names in the order that the corresponding source
# files appeared on the wricc command line.  This order can be reconstructed
# (with some effort) from the array of file *types* (c, s, o) for each
# filename, and the fact that the arrays of files (c_files, etc.) are
# each in order.
let c_file_count=0
let s_file_count=0
let o_file_count=0
for i in "${file_order[@]}"; do
    case "$i" in
	c ) fn="${out_obj_files[$((c_out_obj_offset+c_file_count))]}"
	    c_file_count=$c_file_count+1 ;;
	s ) fn="${out_obj_files[$((s_out_obj_offset+s_file_count))]}"
	    s_file_count=$s_file_count+1 ;;
	o ) fn="${out_obj_files[$((o_out_obj_offset+o_file_count))]}"
	    o_file_count=$o_file_count+1 ;;
    esac
    cmdline="$cmdline\"$fn\" "
done
cmdline="$cmdline$ldfinals"

if [ $op_verbose = 1 ]; then
	echo "$cmdline"
fi

eval "$cmdline"
es=$?

if [ $es != 0 ]; then
	exit $es	# wrild failed!
fi

exit 0









# Copyright 2010 Wolfram Research, Inc.
#
# This file is part of the build scripts for building the GNU MP Library
# for Mathematica on Windows (32-bit).
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser 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 Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see http://www.gnu.org/licenses/.
