| #!/usr/bin/env bash |
| # ################################################################################################ # |
| # MetaStack Solutions Ltd. # |
| # ################################################################################################ # |
| # Microsoft C Compiler Environment Detection Script # |
| # ################################################################################################ # |
| # Copyright (c) 2016, 2017, 2018, 2019, 2020 MetaStack Solutions Ltd. # |
| # ################################################################################################ # |
| # Author: David Allsopp # |
| # 16-Feb-2016 # |
| # ################################################################################################ # |
| # Redistribution and use in source and binary forms, with or without modification, are permitted # |
| # provided that the following two conditions are met: # |
| # 1. Redistributions of source code must retain the above copyright notice, this list of # |
| # conditions and the following disclaimer. # |
| # 2. Neither the name of MetaStack Solutions Ltd. nor the names of its contributors may be # |
| # used to endorse or promote products derived from this software without specific prior # |
| # written permission. # |
| # # |
| # This software is provided by the Copyright Holder 'as is' and any express or implied warranties # |
| # including, but not limited to, the implied warranties of merchantability and fitness for a # |
| # particular purpose are disclaimed. In no event shall the Copyright Holder be liable for any # |
| # direct, indirect, incidental, special, exemplary, or consequential damages (including, but not # |
| # limited to, procurement of substitute goods or services; loss of use, data, or profits; or # |
| # business interruption) however caused and on any theory of liability, whether in contract, # |
| # strict liability, or tort (including negligence or otherwise) arising in any way out of the use # |
| # of this software, even if advised of the possibility of such damage. # |
| # ################################################################################################ # |
| |
| VERSION=0.4.1 |
| |
| # debug [level=2] message |
| debug () |
| { |
| if [[ -z ${2+x} ]] ; then |
| DEBUG_LEVEL=2 |
| else |
| DEBUG_LEVEL=$1 |
| shift |
| fi |
| |
| if [[ $DEBUG -ge $DEBUG_LEVEL ]] ; then |
| echo "$1">&2 |
| fi |
| } |
| |
| # warning message |
| warning () |
| { |
| if [[ $DEBUG -gt 0 ]] ; then |
| echo "Warning: $1">&2 |
| fi |
| } |
| |
| # reg_string key value |
| # Retrieves a REG_SZ value from the registry (redirected on WOW64) |
| reg_string () |
| { |
| reg query "$1" /v "$2" 2>/dev/null | tr -d '\r' | sed -ne "s/ *$2 *REG_SZ *//p" |
| } |
| |
| # reg64_string key value |
| # As reg_string, but without WOW64 redirection (i.e. guaranteed access to 64-bit registry) |
| reg64_string () |
| { |
| $REG64 query "$1" /v "$2" 2>/dev/null | tr -d '\r' | sed -ne "s/ *$2 *REG_SZ *//p" |
| } |
| |
| # find_in list file |
| # Increments $RET if file does not exist in any of the directories in the *-separated list |
| find_in () |
| { |
| debug 4 "Looking for $2 in $1" |
| if [[ -z $1 ]] ; then |
| STATUS=1 |
| else |
| IFS=* |
| STATUS=1 |
| for f in $1; do |
| if [[ -e "$f/$2" ]] ; then |
| STATUS=0 |
| break |
| fi |
| done |
| unset IFS |
| fi |
| if [[ $STATUS -eq 1 ]] ; then |
| debug 4 "$2 not found" |
| fi |
| ((RET+=STATUS)) |
| } |
| |
| # check_environment PATH INC LIB name arch |
| # By checking for the presence of various files, verifies that PATH, INC and LIB provide a complete |
| # compiler and indicates this in its return status. RET is assumed to be zero on entry. $ASSEMBLER |
| # will contain the name of assembler for this compiler series (ml.exe or ml64.exe). |
| # The following files are checked: |
| # cl.exe PATH Microsoft C compiler |
| # kernel32.lib LIB Implies Windows SDK present |
| # link.exe PATH Microsoft Linker |
| # ml[64].exe PATH Microsoft Assembler (ml.exe or ml64.exe) |
| # msvcrt.lib LIB Implies C Runtime Libraries present |
| # mt.exe PATH Microsoft Manifest Tool |
| # oldnames.lib LIB Implies C Runtime Libraries present |
| # rc.exe PATH Microsoft Resource Compiler (implies tools present) |
| # stdlib.h INC Implies Microsoft C Runtime Libraries present |
| # windows.h INC Implies Windows SDK present |
| # oldnames.lib is included, because certain SDKs and older versions don't correctly install the |
| # entire runtime if only some options (e.g. Dynamic Runtime and not Static) are selected. |
| check_environment () |
| { |
| debug 4 "Checking $4 ($5)" |
| for tool in cl rc link ; do |
| find_in "$1" $tool.exe |
| done |
| |
| if [[ $RET -gt 0 ]] ; then |
| warning "Microsoft C Compiler tools not all found - $4 ($5) excluded" |
| return 1 |
| fi |
| |
| RET=0 |
| find_in "$2" windows.h |
| find_in "$3" kernel32.lib |
| if [[ $RET -gt 0 ]] ; then |
| warning "Windows SDK not all found - $4 ($5) excluded" |
| return 1 |
| fi |
| |
| RET=0 |
| find_in "$2" stdlib.h |
| find_in "$3" msvcrt.lib |
| find_in "$3" oldnames.lib |
| if [[ $RET -gt 0 ]] ; then |
| warning "Microsoft C runtime library not all found - $4 ($5) excluded" |
| return 1 |
| fi |
| |
| ASSEMBLER=ml${5#x} |
| ASSEMBLER=${ASSEMBLER%86}.exe |
| if [[ $ML_REQUIRED -eq 1 ]] ; then |
| RET=0 |
| find_in "$1" $ASSEMBLER |
| if [[ $RET -gt 0 ]] ; then |
| warning "Microsoft Assembler ($ASSEMBLER) not found - $4 ($5)" |
| return 1 |
| fi |
| fi |
| |
| if [[ $MT_REQUIRED -eq 1 ]] ; then |
| RET=0 |
| find_in "$1" mt.exe |
| if [[ $RET -gt 0 ]] ; then |
| warning "Microsoft Manifest Tool not found - $4 ($5)" |
| return 1 |
| fi |
| fi |
| |
| return 0 |
| } |
| |
| # output VAR value arch |
| # Outputs a command for setting VAR to value based on $OUTPUT. If $ENV_ARCH is arch, then an empty |
| # value (i.e. no change) is output. |
| output () |
| { |
| if [[ $3 = $ENV_ARCH ]] ; then |
| VALUE= |
| else |
| VALUE=$2 |
| fi |
| case "$OUTPUT" in |
| 0) |
| echo "$1='${VALUE//\'/\'\"\'\"\'}'";; |
| 1) |
| VALUE=${VALUE//#/\\\#} |
| echo "$1=${VALUE//\$/\$\$}";; |
| esac |
| } |
| |
| # DEBUG Debugging level |
| # MODE Operation mode |
| # 0 - Normal |
| # 1 - --all |
| # 2 - --help |
| # 3 - --version |
| # OUTPUT --output option |
| # 0 - =shell |
| # 1 - =make |
| # MT_REQUIRED --with-mt |
| # ML_REQUIRED --with-assembler |
| # TARGET_ARCH Normalised --arch (x86, x64 or blank for both) |
| # LEFT_ARCH \ If $TARGET_ARCH is blank, these will be x86 and x64 respectively, otherwise they |
| # RIGHT_ARCH / equal $TARGET_ARCH |
| # SCAN_ENV Controls from parsing whether the environment should be queried for a compiler |
| DEBUG=0 |
| MODE=0 |
| OUTPUT=0 |
| MT_REQUIRED=0 |
| ML_REQUIRED=0 |
| TARGET_ARCH= |
| SCAN_ENV=0 |
| |
| # Various PATH messing around means it's sensible to know where tools are now |
| WHICH=$(which which) |
| |
| if [[ $(uname --operating-system 2>/dev/null) = "Msys" ]] ; then |
| # Prevent MSYS from translating command line switches to paths |
| SWITCH_PREFIX='//' |
| else |
| SWITCH_PREFIX='/' |
| fi |
| |
| # Parse command-line. At the moment, the short option which usefully combines with anything is -d, |
| # so for the time being, combining short options is not permitted, as the loop becomes even less |
| # clear with getopts. GNU getopt isn't installed by default on Cygwin... |
| if [[ $@ != "" ]] ; then |
| while true ; do |
| case "$1" in |
| # Mode settings ($MODE) |
| -a|--all) |
| MODE=1 |
| shift 1;; |
| -h|--help) |
| MODE=2 |
| shift;; |
| -v|--version) |
| MODE=3 |
| shift;; |
| |
| # Simple flags ($MT_REQUIRED and $ML_REQUIRED) |
| --with-mt) |
| MT_REQUIRED=1 |
| shift;; |
| --with-assembler) |
| ML_REQUIRED=1 |
| shift;; |
| |
| # -o, --output ($OUTPUT) |
| -o|--output) |
| case "$2" in |
| shell) |
| ;; |
| make) |
| OUTPUT=1;; |
| *) |
| echo "$0: unrecognised option for $1: '$2'">&2 |
| exit 2;; |
| esac |
| shift 2;; |
| -oshell|--output=shell) |
| shift;; |
| -omake|--output=make) |
| OUTPUT=1 |
| shift;; |
| -o*) |
| echo "$0: unrecognised option for -o: '${1#-o}'">&2 |
| exit 2;; |
| --output=*) |
| echo "$0: unrecognised option for --output: '${1#--output=}'">&2 |
| exit 2;; |
| |
| # -x, --arch ($TARGET_ARCH) |
| -x|--arch) |
| case "$2" in |
| 86|x86) |
| TARGET_ARCH=x86;; |
| 64|x64) |
| TARGET_ARCH=x64;; |
| *) |
| echo "$0: unrecognised option for $1: '$2'">&2 |
| exit 2 |
| esac |
| shift 2;; |
| -x86|-xx86|--arch=x86|--arch=86) |
| TARGET_ARCH=x86 |
| shift;; |
| -x64|-xx64|--arch=x64|--arch=64) |
| TARGET_ARCH=x64 |
| shift;; |
| -x*) |
| echo "$0: unrecognised option for -x: '${1#-x}'">&2 |
| exit 2;; |
| --arch=*) |
| echo "$0: unrecognised option for --arch: '${1#--arch}'">&2 |
| exit 2;; |
| |
| # -d, --debug ($DEBUG) |
| -d*) |
| DEBUG=${1#-d} |
| if [[ -z $DEBUG ]] ; then |
| DEBUG=1 |
| fi |
| shift;; |
| --debug=*) |
| DEBUG=${1#*=} |
| shift;; |
| --debug) |
| DEBUG=1 |
| shift;; |
| |
| # End of option marker |
| --) |
| shift |
| break;; |
| |
| # Invalid options |
| --*) |
| echo "$0: unrecognised option: '${1%%=*}'">&2 |
| exit 2;; |
| -*) |
| echo "$0: unrecognised option: '${1:1:1}'">&2 |
| exit 2;; |
| |
| # MSVS_PREFERENCE (without end-of-option marker) |
| *) |
| break;; |
| esac |
| done |
| |
| if [[ -n ${1+x} ]] ; then |
| if [[ $MODE -eq 1 ]] ; then |
| echo "$0: cannot specify MSVS_PREFERENCE and --all">&2 |
| exit 2 |
| else |
| MSVS_PREFERENCE="$@" |
| fi |
| fi |
| fi |
| |
| # Options sanitising |
| if [[ $MODE -eq 1 ]] ; then |
| if [[ -n $TARGET_ARCH ]] ; then |
| echo "$0: --all and --arch are mutually exclusive">&2 |
| exit 2 |
| fi |
| MSVS_PREFERENCE= |
| SCAN_ENV=1 |
| elif [[ -z ${MSVS_PREFERENCE+x} ]] ; then |
| MSVS_PREFERENCE='@;VS16.*;VS15.*;VS14.0;VS12.0;VS11.0;10.0;9.0;8.0;7.1;7.0' |
| fi |
| |
| MSVS_PREFERENCE=${MSVS_PREFERENCE//;/ } |
| |
| if [[ -z $TARGET_ARCH ]] ; then |
| LEFT_ARCH=x86 |
| RIGHT_ARCH=x64 |
| else |
| LEFT_ARCH=$TARGET_ARCH |
| RIGHT_ARCH=$TARGET_ARCH |
| fi |
| |
| # Command line parsing complete (MSVS_PREFERENCE pending) |
| |
| NAME="Microsoft C Compiler Environment Detection Script" |
| case $MODE in |
| 2) |
| echo "$NAME" |
| echo "Queries the environment and registry to locate Visual Studio / Windows SDK" |
| echo "installations and uses their initialisation scripts (SetEnv.cmd, vcvarsall.bat," |
| echo "etc.) to determine INCLUDE, LIB and PATH alterations." |
| echo |
| echo "Usage:" |
| echo " $0 [OPTIONS] [--] [MSVS_PREFERENCE]" |
| echo |
| echo "Options:" |
| echo " -a, --all Display all available compiler packages" |
| echo " -x, --arch=ARCH Only consider packages for ARCH (x86 or x64). Default is" |
| echo " to return packages containing both architectures" |
| echo " -d, --debug[=LEVEL] Set debug messages level" |
| echo " -h, --help Display this help screen" |
| echo " -o, --output=OUTPUT Set final output. Default is shell. Valid values:" |
| echo " shell - shell assignments, for use with eval" |
| echo " make - make assignments, for inclusion in a Makefile" |
| echo " -v, --version Display the version" |
| echo " --with-mt Only consider packages including the Manifest Tool" |
| echo " --with-assembler Only consider packages including an assembler" |
| echo |
| echo "If MSVS_PREFERENCE is not given, then the environment variable MSVS_PREFERENCE" |
| echo "is read. MSVS_PREFERENCE is a semicolon separated list of preferred versions." |
| echo "Three kinds of version notation are supported:" |
| echo " 1. @ - which refers to the C compiler found in PATH (if it can be identified)" |
| echo " (this allows the C compiler corresponding to the opposite architecture to" |
| echo " be selected, if possible)." |
| echo " 2. mm.n - which refers to a Visual Studio version (e.g. 14.0, 7.1) but which" |
| echo " also allows an SDK to provide the compiler (e.g. Windows SDK 7.1 provides" |
| echo " 10.0). Visual Studio packages are always preferred ahead of SDKs." |
| echo " 3. SPEC - an actual package specification. Visual Studio packages are VSmm.n" |
| echo " (e.g. VS14.0, VS7.1) and SDK packages are SDKm.n (e.g. SDK7.1)." |
| echo " Any Visual Studio 2017 update can be selected with VS15.*" |
| echo "The default behaviour is to match the environment compiler followed by the most" |
| echo "recent version of the compiler." |
| exit 0;; |
| 3) |
| echo "$NAME" |
| echo "Version $VERSION" |
| exit 0;; |
| esac |
| |
| # Known compiler packages. Visual Studio .NET 2002 onwards. Detection is in place for Visual Studio |
| # 2005 Express, but because it doesn't include a Windows SDK, it can only ever be detected if the |
| # script has been launched from within a Platform SDK command prompt (this provides the Windows |
| # Headers and Libraries which allows this script to detect the rest). |
| # Each element is either a Visual Studio or SDK package and the value is the syntax for a bash |
| # associative array to be eval'd. Each of these contains the following properties: |
| # NAME - the friendly name of the package |
| # ENV - (VS only) the version-specific portion of the VSCOMNTOOLS environment variable |
| # VERSION - (VS only) version number of the package |
| # ARCH - Lists the architectures available in this version |
| # ARCH_SWITCHES - The script is assumed to accept x86 and x64 to indicate architecture. This key |
| # contains another eval'd associative array allowing alternate values to be given |
| # SETENV_RELEASE - (SDK only) script switch necessary to select release than debugging versions |
| # EXPRESS - (VS only) the prefix to the registry key to detect the Express edition |
| # EXPRESS_ARCH - (VS only) overrides ARCH if Express edition is detected |
| # EXPRESS_ARCH_SWITCHES - (VS only) overrides ARCH_SWITCHES if Express edition is detected |
| # VC_VER - (SDK only) specifies the version of the C Compilers included in the SDK (SDK |
| # equivalent of the VERSION key) |
| # REG_KEY - (SDK only) registry key to open to identify this package installation |
| # REG_VALUE - (SDK only) registry value to query to identify this package installation |
| # VSWHERE - (VS 2017+) is 1 if the compiler can only be detected using vswhere |
| # For a while, Windows SDKs followed a standard pattern which is stored in the SDK element and |
| # copied to the appropriate version. SDKs after 7.1 do not include compilers, and so are not |
| # captured (as of Visual Studio 2015, the Windows SDK is official part of Visual Studio). |
| declare -A COMPILERS |
| SDK52_KEY='HKLM\SOFTWARE\Microsoft\MicrosoftSDK\InstalledSDKs\8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3' |
| COMPILERS=( |
| ["VS7.0"]='( |
| ["NAME"]="Visual Studio .NET 2002" |
| ["ENV"]="" |
| ["VERSION"]="7.0" |
| ["ARCH"]="x86")' |
| ["VS7.1"]='( |
| ["NAME"]="Visual Studio .NET 2003" |
| ["ENV"]="71" |
| ["VERSION"]="7.1" |
| ["ARCH"]="x86")' |
| ["VS8.0"]='( |
| ["NAME"]="Visual Studio 2005" |
| ["ENV"]="80" |
| ["VERSION"]="8.0" |
| ["EXPRESS"]="VC" |
| ["ARCH"]="x86 x64" |
| ["EXPRESS_ARCH"]="x86")' |
| ["VS9.0"]='( |
| ["NAME"]="Visual Studio 2008" |
| ["ENV"]="90" |
| ["VERSION"]="9.0" |
| ["EXPRESS"]="VC" |
| ["ARCH"]="x86 x64" |
| ["EXPRESS_ARCH"]="x86")' |
| ["VS10.0"]='( |
| ["NAME"]="Visual Studio 2010" |
| ["ENV"]="100" |
| ["VERSION"]="10.0" |
| ["EXPRESS"]="VC" |
| ["ARCH"]="x86 x64" |
| ["EXPRESS_ARCH"]="x86")' |
| ["VS11.0"]='( |
| ["NAME"]="Visual Studio 2012" |
| ["ENV"]="110" |
| ["VERSION"]="11.0" |
| ["EXPRESS"]="WD" |
| ["ARCH"]="x86 x64" |
| ["EXPRESS_ARCH_SWITCHES"]="([\"x64\"]=\"x86_amd64\")")' |
| ["VS12.0"]='( |
| ["NAME"]="Visual Studio 2013" |
| ["ENV"]="120" |
| ["VERSION"]="12.0" |
| ["EXPRESS"]="WD" |
| ["ARCH"]="x86 x64" |
| ["EXPRESS_ARCH_SWITCHES"]="([\"x64\"]=\"x86_amd64\")")' |
| ["VS14.0"]='( |
| ["NAME"]="Visual Studio 2015" |
| ["ENV"]="140" |
| ["VERSION"]="14.0" |
| ["ARCH"]="x86 x64")' |
| ["VS15.*"]='( |
| ["NAME"]="Visual Studio 2017" |
| ["VSWHERE"]="1")' |
| ["VS16.*"]='( |
| ["NAME"]="Visual Studio 2019" |
| ["VSWHERE"]="1")' |
| ["SDK5.2"]='( |
| ["NAME"]="Windows Server 2003 SP1 SDK" |
| ["VC_VER"]="8.0" |
| ["REG_KEY"]="$SDK52_KEY" |
| ["REG_VALUE"]="Install Dir" |
| ["SETENV_RELEASE"]="/RETAIL" |
| ["ARCH"]="x64" |
| ["ARCH_SWITCHES"]="([\"x64\"]=\"/X64\")")' |
| ["SDK"]='( |
| ["NAME"]="Generalised Windows SDK" |
| ["SETENV_RELEASE"]="/Release" |
| ["ARCH"]="x86 x64" |
| ["ARCH_SWITCHES"]="([\"x86\"]=\"/x86\" [\"x64\"]=\"/x64\")")' |
| ["SDK6.1"]='( |
| ["NAME"]="Windows Server 2008 with .NET 3.5 SDK" |
| ["VC_VER"]="9.0")' |
| ["SDK7.0"]='( |
| ["NAME"]="Windows 7 with .NET 3.5 SP1 SDK" |
| ["VC_VER"]="9.0")' |
| ["SDK7.1"]='( |
| ["NAME"]="Windows 7 with .NET 4 SDK" |
| ["VC_VER"]="10.0")' |
| ) |
| |
| # FOUND is ultimately an associative array containing installed compiler packages. It's |
| # hijacked here as part of MSVS_PREFERENCE validation. |
| # Ultimately, it contains a copy of the value from COMPILERS with the following extra keys: |
| # IS_EXPRESS - (VS only) indicates whether the Express edition was located |
| # SETENV - (SDK only) the full location of the SetEnv.cmd script |
| # ASSEMBLER - the name of the assembler (ml or ml64) |
| # MSVS_PATH \ |
| # MSVS_INC > prefix values for PATH, INCLUDE and LIB determined by running the scripts. |
| # MSVS_LIB / |
| declare -A FOUND |
| |
| # Check that MSVS_PREFERENCE is valid and contains no repetitions. |
| for v in $MSVS_PREFERENCE ; do |
| if [[ -n ${FOUND[$v]+x} ]] ; then |
| echo "$0: corrupt MSVS_PREFERENCE: repeated '$v'">&2 |
| exit 2 |
| fi |
| if [[ $v != "@" ]] ; then |
| if [[ -z ${COMPILERS[$v]+x} && -z ${COMPILERS["VS$v"]+x} && -z ${COMPILERS[${v%.*}.*]+x} ]] ; then |
| echo "$0: corrupt MSVS_PREFERENCE: unknown compiler '$v'">&2 |
| exit 2 |
| fi |
| else |
| SCAN_ENV=1 |
| fi |
| FOUND["$v"]="" |
| done |
| |
| # Reset FOUND for later use. |
| FOUND=() |
| |
| # Scan the environment for a C compiler, and check that it's valid. Throughout the rest of the |
| # script, it is assumed that if ENV_ARCH is set then there is a valid environment compiler. |
| if [[ $SCAN_ENV -eq 1 ]] ; then |
| if "$WHICH" cl >/dev/null 2>&1 ; then |
| # Determine its architecture from the Microsoft Logo line. |
| ENV_ARCH=$(cl 2>&1 | head -1 | tr -d '\r') |
| case "${ENV_ARCH#* for }" in |
| x64|AMD64) |
| ENV_ARCH=x64;; |
| 80x86|x86) |
| ENV_ARCH=x86;; |
| *) |
| echo "Unable to identify C compiler architecture from '${ENV_ARCH#* for }'">&2 |
| echo "Environment C compiler discarded">&2 |
| unset ENV_ARCH;; |
| esac |
| |
| # Environment variable names are a bit of a nightmare on Windows - they are actually case |
| # sensitive (at the kernel level) but not at the user level! To compound the misery is that SDKs |
| # use Include and Lib where vcvars32 tends to use INCLUDE and LIB. Windows versions also contain |
| # a mix of Path and PATH, but fortunately Cygwin normalises that to PATH for us! For this |
| # reason, use env to determine the actual case of the LIB and INCLUDE variables. |
| if [[ -n ${ENV_ARCH+x} ]] ; then |
| RET=0 |
| ENV_INC=$(env | sed -ne 's/^\(INCLUDE\)=.*/\1/pi') |
| ENV_LIB=$(env | sed -ne 's/^\(LIB\)=.*/\1/pi') |
| if [[ -z $ENV_INC || -z $ENV_LIB ]] ; then |
| warning "Microsoft C Compiler Include and/or Lib not set - Environment C compiler ($ENV_ARCH) excluded" |
| unset ENV_ARCH |
| else |
| if check_environment "${PATH//:/*}" \ |
| "${!ENV_INC//;/*}" \ |
| "${!ENV_LIB//;/*}" \ |
| "Environment C compiler" \ |
| "$ENV_ARCH" ; then |
| ENV_CL=$("$WHICH" cl) |
| ENV_cl=${ENV_CL,,} |
| ENV_cl=${ENV_cl/bin\/*_/bin\/} |
| debug "Environment appears to include a compiler at $ENV_CL" |
| if [[ -n $TARGET_ARCH && $TARGET_ARCH != $ENV_ARCH ]] ; then |
| debug "But architecture doesn't match required value" |
| unset ENV_ARCH |
| fi |
| else |
| unset ENV_ARCH |
| fi |
| fi |
| fi |
| fi |
| fi |
| |
| # Even if launched from a 64-bit Command Prompt, Cygwin is usually 32-bit and so the scripts |
| # executed will inherit that fact. This is a problem when querying the registry, but fortunately |
| # WOW64 provides a mechanism to break out of the 32-bit environment by mapping $WINDIR/sysnative to |
| # the real 64-bit programs. |
| # Thus: |
| # MS_ROOT is the 32-bit Microsoft Registry key (all Visual Studio keys are located there) |
| # REG64 is the processor native version of the reg utility (allowing 64-bit keys to be read for |
| # the SDKs) |
| if [[ -n ${PROCESSOR_ARCHITEW6432+x} ]] ; then |
| debug "WOW64 detected" |
| MS_ROOT='HKLM\SOFTWARE\Microsoft' |
| REG64=$WINDIR/sysnative/reg |
| else |
| MS_ROOT='HKLM\SOFTWARE\Wow6432Node\Microsoft' |
| REG64=reg |
| fi |
| |
| # COMPILER contains each eval'd element from COMPILERS |
| declare -A COMPILER |
| |
| # Scan the registry for compiler package (vswhere is later) |
| for i in "${!COMPILERS[@]}" ; do |
| eval COMPILER=${COMPILERS[$i]} |
| |
| if [[ -n ${COMPILER["ENV"]+x} ]] ; then |
| # Visual Studio package - test for its environment variable |
| ENV=VS${COMPILER["ENV"]}COMNTOOLS |
| if [[ -n ${!ENV+x} ]] ; then |
| debug "$ENV is a candidate" |
| TEST_PATH=${!ENV%\"} |
| TEST_PATH=$(cygpath -u -f - <<< ${TEST_PATH#\"}) |
| if [[ -e $TEST_PATH/vsvars32.bat ]] ; then |
| debug "Directory pointed to by $ENV contains vsvars32.bat" |
| EXPRESS=0 |
| # Check for the primary Visual Studio registry value indicating installation |
| INSTALL_DIR=$(reg_string "$MS_ROOT\\VisualStudio\\${COMPILER["VERSION"]}" InstallDir) |
| if [[ -z $INSTALL_DIR ]] ; then |
| if [[ -n ${COMPILER["EXPRESS"]+x} ]] ; then |
| TEST_KEY="$MS_ROOT\\${COMPILER["EXPRESS"]}Express\\${COMPILER["VERSION"]}" |
| INSTALL_DIR=$(reg_string "$TEST_KEY" InstallDir) |
| # Exception for Visual Studio 2005 Express, which doesn't set the registry correctly, so |
| # set INSTALL_DIR to a fake value to pass the next test. |
| if [[ ${COMPILER["VERSION"]} = "8.0" ]] ; then |
| INSTALL_DIR=$(cygpath -w "$TEST_PATH") |
| EXPRESS=1 |
| else |
| if [[ -z $INSTALL_DIR ]] ; then |
| warning "vsvars32.bat found, but registry value not located (Exp or Pro)" |
| else |
| EXPRESS=1 |
| fi |
| fi |
| else |
| warning "vsvars32.bat found, but registry value not located" |
| fi |
| fi |
| |
| if [[ -n $INSTALL_DIR ]] ; then |
| if [[ ${TEST_PATH%/} = $(cygpath -u "$INSTALL_DIR\\..\\Tools") ]] ; then |
| RESULT=${COMPILERS[$i]%)} |
| DISPLAY=${COMPILER["NAME"]} |
| if [[ $EXPRESS -eq 1 ]] ; then |
| DISPLAY="$DISPLAY Express" |
| fi |
| FOUND+=(["$i"]="$RESULT [\"DISPLAY\"]=\"$DISPLAY\" [\"IS_EXPRESS\"]=\"$EXPRESS\")") |
| debug "${COMPILER["NAME"]} accepted for further detection" |
| else |
| warning "$ENV doesn't agree with registry" |
| fi |
| else |
| warning "vsvars32.bat found, but registry settings not found" |
| fi |
| else |
| warning "$ENV set, but vsvars32.bat not found" |
| fi |
| fi |
| elif [[ -n ${COMPILER["REG_KEY"]+x} ]] ; then |
| # SDK with explicit registry detection value |
| INSTALL_DIR=$(reg64_string "${COMPILER["REG_KEY"]}" "${COMPILER["REG_VALUE"]}") |
| if [[ -n $INSTALL_DIR ]] ; then |
| TEST_PATH=$(cygpath -u "$INSTALL_DIR") |
| if [[ -e $TEST_PATH/SetEnv.cmd ]] ; then |
| RESULT=${COMPILERS[$i]%)} |
| FOUND+=(["$i"]="$RESULT [\"DISPLAY\"]=\"${COMPILER["NAME"]}\" [\"SETENV\"]=\"$INSTALL_DIR\\SetEnv.cmd\")") |
| debug "${COMPILER["NAME"]} accepted for further detection" |
| else |
| warning "Registry set for Windows Server 2003 SDK, but SetEnv.cmd not found" |
| fi |
| fi |
| fi |
| done |
| |
| # Now enumerate installed SDKs for v6.0+ |
| SDK_ROOT='HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows' |
| for i in $(reg query "$SDK_ROOT" 2>/dev/null | tr -d '\r' | sed -ne '/Windows\\v/s/.*\\//p') ; do |
| debug "Analysing SDK key $SDK_ROOT\\$i" |
| INSTALL_DIR=$(reg_string "$SDK_ROOT\\$i" InstallationFolder) |
| if [[ -n $INSTALL_DIR ]] ; then |
| TEST_PATH=$(cygpath -u "$INSTALL_DIR") |
| if [[ -e $TEST_PATH/Bin/SetEnv.cmd ]] ; then |
| if [[ -z ${COMPILERS["SDK${i#v}"]+x} ]] ; then |
| warning "SDK $i is not known to this script - assuming compatibility" |
| DISPLAY="Windows SDK $i" |
| else |
| eval COMPILER=${COMPILERS["SDK${i#v}"]} |
| DISPLAY=${COMPILER['NAME']} |
| fi |
| RESULT=${COMPILERS['SDK']%)} |
| FOUND+=(["SDK${i/v/}"]="$RESULT [\"DISPLAY\"]=\"$DISPLAY\" [\"SETENV\"]=\"$INSTALL_DIR\\Bin\\SetEnv.cmd\")") |
| else |
| if [[ -n ${COMPILERS["SDK${i#v}"]+x} ]] ; then |
| warning "Registry set for Windows SDK $i, but SetEnv.cmd not found" |
| fi |
| fi |
| else |
| warning "Registry key for Windows SDK $i doesn't contain expected InstallationFolder value" |
| fi |
| done |
| |
| # Now enumerate Visual Studio 2017+ instances |
| VSWHERE=$(dirname $(realpath $0))/vswhere.exe |
| if [[ ! -x $VSWHERE ]] ; then |
| VSWHERE="$(printenv 'ProgramFiles(x86)')\\Microsoft Visual Studio\\Installer\\vswhere.exe" |
| VSWHERE=$(echo $VSWHERE| cygpath -f -) |
| fi |
| if [[ -x $VSWHERE ]] ; then |
| debug "$VSWHERE found" |
| while IFS= read -r line; do |
| case ${line%: *} in |
| instanceId) |
| INSTANCE=${line#*: };; |
| installationPath) |
| INSTANCE_PATH=${line#*: };; |
| installationVersion) |
| INSTANCE_VER=${line#*: } |
| INSTANCE_VER=${INSTANCE_VER%.*} |
| INSTANCE_VER=${INSTANCE_VER%.*};; |
| displayName) |
| INSTANCE_NAME=${line#*: } |
| debug "Looking at $INSTANCE in $INSTANCE_PATH ($INSTANCE_VER $INSTANCE_NAME)" |
| if [[ -e "$(echo $INSTANCE_PATH| cygpath -f -)/VC/Auxiliary/Build/vcvarsall.bat" ]] ; then |
| debug "vcvarsall.bat found" |
| FOUND+=(["VS$INSTANCE_VER"]="([\"DISPLAY\"]=\"$INSTANCE_NAME\" [\"ARCH\"]=\"x86 x64\" [\"SETENV\"]=\"$INSTANCE_PATH\\VC\\Auxiliary\\Build\\vcvarsall.bat\" [\"SETENV_RELEASE\"]=\"\")") |
| else |
| warning "vcvarsall.bat not found for $INSTANCE" |
| fi;; |
| esac |
| done < <("$VSWHERE" -all -nologo | tr -d '\r') |
| fi |
| |
| if [[ $DEBUG -gt 1 ]] ; then |
| for i in "${!FOUND[@]}" ; do |
| echo "Inspect $i">&2 |
| done |
| fi |
| |
| # Basic scanning is complete, now interrogate the packages which seem to be installed and ensure |
| # that they pass the check_environment tests. |
| |
| # CANDIDATES is a hash table of the keys of FOUND. The result of the next piece of processing is to |
| # derive two arrays PREFERENCE and TEST. TEST will contain a list of the keys of FOUND in the order |
| # in which they should be evaluated. PREFERENCE contains a parsed version of MSVS_PREFERENCE but |
| # filtered on the basis of the compiler packages already identified. The current "hoped for" |
| # preference is stored in $pref (the index into PREFERENCE) and $PREF (which is |
| # ${PREFERENCE[$pref]}). These two arrays together allow testing to complete quickly if the desired |
| # version is found (note that often this won't be possible as the @ environment option requires all |
| # packages to be tested in order to be sure that the environment compiler is not ambiguous). |
| declare -A CANDIDATES |
| for i in "${!FOUND[@]}" ; do |
| CANDIDATES[$i]=""; |
| done |
| |
| # For --all, act as though MSVS_PREFERENCE were "@" because this causes all packages to be tested. |
| if [[ $MODE -eq 1 ]] ; then |
| PREFER_ENV=1 |
| PREFERENCE=("@") |
| else |
| PREFER_ENV=0 |
| PREFERENCE=() |
| fi |
| |
| TEST=() |
| for i in $MSVS_PREFERENCE ; do |
| if [[ $i = "@" ]] ; then |
| if [[ -n ${ENV_ARCH+x} ]] ; then |
| PREFERENCE+=("@") |
| PREFER_ENV=1 |
| else |
| debug "Preference @ ignored since no environment compiler selected" |
| fi |
| else |
| if [[ -n ${COMPILERS[$i]+x} || -n ${COMPILERS[${i%.*}.*]+x} ]] ; then |
| if [[ -n ${CANDIDATES[$i]+x} ]] ; then |
| unset CANDIDATES[$i] |
| TEST+=($i) |
| PREFERENCE+=($i) |
| elif [[ ${i#*.} = "*" ]] ; then |
| INSTANCES= |
| for j in "${!CANDIDATES[@]}" ; do |
| if [[ "${j%.*}.*" = $i ]] ; then |
| unset CANDIDATES[$j] |
| INSTANCES="$INSTANCES $j" |
| fi |
| done |
| INSTANCES="$(sort -r <<< "${INSTANCES// /$'\n'}")" |
| eval TEST+=($INSTANCES) |
| eval PREFERENCE+=($INSTANCES) |
| fi |
| else |
| if [[ -n ${CANDIDATES["VS$i"]+x} ]] ; then |
| unset CANDIDATES["VS$i"] |
| TEST+=("VS$i") |
| PREFERENCE+=("VS$i") |
| fi |
| SDKS= |
| for j in "${!COMPILERS[@]}" ; do |
| eval COMPILER=${COMPILERS[$j]} |
| if [[ -n ${COMPILER["VC_VER"]+x} ]] ; then |
| if [[ $i = ${COMPILER["VC_VER"]} && -n ${CANDIDATES[$j]+x} ]] ; then |
| unset CANDIDATES[$j] |
| SDKS="$j $SDKS" |
| fi |
| fi |
| done |
| SDKS=${SDKS% } |
| SDKS="$(sort -r <<< "${SDKS// /$'\n'}")" |
| SDKS=${SDKS//$'\n'/ } |
| eval TEST+=($SDKS) |
| eval PREFERENCE+=($SDKS) |
| fi |
| fi |
| done |
| |
| # If MSVS_PREFERENCE includes @, add any remaining items from CANDIDATES to TEST, otherwise remove |
| # them from FOUND so that they don't accidentally get reported on later. |
| for i in "${!CANDIDATES[@]}" ; do |
| if [[ $PREFER_ENV -eq 1 ]] ; then |
| TEST+=($i) |
| else |
| unset FOUND[$i] |
| fi |
| done |
| |
| # Initialise pref and PREF to ${PREFERENCE[0]} |
| pref=0 |
| PREF=${PREFERENCE[0]} |
| |
| if [[ $DEBUG -gt 1 ]] ; then |
| for i in "${!TEST[@]}" ; do |
| echo "Test ${TEST[$i]}">&2 |
| done |
| fi |
| |
| |
| # Now run each compiler's environment script and then test whether it is suitable. During this loop, |
| # attempt to identify the environment C compiler (if one was found). The environment C compiler is |
| # strongly identified if the full location of cl matches the one in PATH and both LIB and INCLUDE |
| # contain the strings returned by the script in an otherwise empty environment (if one or both of |
| # the LIB and INCLUDE variables do not contain the string returned, then the compiler is weakly |
| # identified). If the environment compiler is strongly identified by more than one package, then it |
| # is not identified at all; if it is strongly identified by no packages but weakly identified by |
| # exactly 1, then we grudgingly accept that that's probably the one. |
| ENV_COMPILER= |
| WEAK_ENV= |
| |
| # ARCHINFO contains the appropriate ARCH_SWITCHES associative array for each compiler. |
| declare -A ARCHINFO |
| |
| for i in "${TEST[@]}" ; do |
| CURRENT=${FOUND[$i]} |
| eval COMPILER=$CURRENT |
| # At the end of this process, the keys of FOUND will be augmented with the architecture found in |
| # each case (so if "VS14.0" was in FOUND from the scan and both the x86 and x64 compilers are |
| # valid, then at the end of this loop FOUND will contain "VS14.0-x86" and "VS14.0-x64"). |
| unset FOUND[$i] |
| |
| if [[ ${COMPILER["IS_EXPRESS"]}0 -gt 0 && -n ${COMPILER["EXPRESS_ARCH_SWITCHES"]+x} ]] ; then |
| eval ARCHINFO=${COMPILER["EXPRESS_ARCH_SWITCHES"]} |
| elif [[ -n ${COMPILER["ARCH_SWITCHES"]+x} ]] ; then |
| eval ARCHINFO=${COMPILER["ARCH_SWITCHES"]} |
| else |
| ARCHINFO=() |
| fi |
| |
| # Determine the script to be executed and any non-architecture specific switches needed. |
| # $ENV is will contain the value of the environment variable for the compiler (empty for an SDK) |
| # which is required for Visual Studio 7.x shim later. |
| if [[ -n ${COMPILER["ENV"]+x} ]] ; then |
| ENV=VS${COMPILER["ENV"]}COMNTOOLS |
| ENV=${!ENV%\"} |
| ENV=${ENV#\"} |
| if [[ ${COMPILER["ENV"]}0 -ge 800 ]] ; then |
| SCRIPT="$(cygpath -d -f - <<< $ENV)\\..\\..\\VC\\vcvarsall.bat" |
| SCRIPT_SWITCHES= |
| else |
| SCRIPT="$(cygpath -d -f - <<< $ENV)\\vsvars32.bat" |
| SCRIPT_SWITCHES= |
| fi |
| else |
| ENV= |
| SCRIPT=${COMPILER["SETENV"]} |
| SCRIPT_SWITCHES=${COMPILER["SETENV_RELEASE"]} |
| fi |
| # For reasons of escaping, the script is executed using its basename so the directory needs |
| # prepending to PATH. |
| DIR=$(dirname "$SCRIPT" | cygpath -u -f -) |
| |
| if [[ ${COMPILER["IS_EXPRESS"]} -gt 0 && -n ${COMPILER["EXPRESS_ARCH"]+x} ]] ; then |
| ARCHS=${COMPILER["EXPRESS_ARCH"]} |
| else |
| ARCHS=${COMPILER["ARCH"]} |
| fi |
| |
| for arch in $ARCHS ; do |
| # Determine the command line switch for this architecture |
| if [[ -n ${ARCHINFO[$arch]+x} ]] ; then |
| ARCH_SWITCHES=${ARCHINFO[$arch]} |
| else |
| ARCH_SWITCHES=$arch |
| fi |
| |
| # Run the script in order to determine changes made to PATH, INCLUDE and LIB. These scripts |
| # always prepend changes to the environment variables. |
| MSVS_PATH= |
| MSVS_LIB= |
| MSVS_INC= |
| |
| COMMAND='%EXEC_SCRIPT% && echo XMARKER && echo !PATH! && echo !LIB! && echo !INCLUDE!' |
| |
| # Note that EXEC_SCRIPT must have ARCH_SWITCHES first for older Platform SDKs (newer ones parse |
| # arguments properly) |
| if [[ $DEBUG -gt 3 ]] ; then |
| printf "Scanning %s... " "$(basename "$SCRIPT") $ARCH_SWITCHES $SCRIPT_SWITCHES">&2 |
| fi |
| num=0 |
| while IFS= read -r line; do |
| case $num in |
| 0) |
| MSVS_PATH=${line%% };; |
| 1) |
| MSVS_LIB=${line%% };; |
| 2) |
| MSVS_INC=${line%% };; |
| esac |
| ((num++)) |
| done < <(INCLUDE='' LIB='' PATH="?msvs-detect?:$DIR:$PATH" ORIGINALPATH='' \ |
| EXEC_SCRIPT="$(basename "$SCRIPT") $ARCH_SWITCHES $SCRIPT_SWITCHES" \ |
| $(cygpath "$COMSPEC") ${SWITCH_PREFIX}v:on ${SWITCH_PREFIX}c $COMMAND 2>/dev/null | grep -F XMARKER -A 3 | tr -d '\015' | tail -3) |
| if [[ $DEBUG -gt 3 ]] ; then |
| echo done>&2 |
| fi |
| |
| if [[ -n $MSVS_PATH ]] ; then |
| # Translate MSVS_PATH back to Cygwin notation (/cygdrive, etc. and colon-separated) |
| MSVS_PATH=$(cygpath "$MSVS_PATH" -p) |
| # Remove any trailing / from elements of MSVS_PATH |
| MSVS_PATH=$(echo "$MSVS_PATH" | sed -e 's|\([^:]\)/\+\(:\|$\)|\1\2|g;s/?msvs-detect?.*//') |
| # Guarantee that MSVS_PATH ends with a single : |
| MSVS_PATH="${MSVS_PATH%%:}:" |
| fi |
| # Ensure that both variables end with a semi-colon (it doesn't matter if for some erroneous |
| # reason they have come back blank, because check_environment will shortly fail) |
| MSVS_LIB="${MSVS_LIB%%;};" |
| MSVS_INC="${MSVS_INC%%;};" |
| |
| # Visual Studio .NET 2002 and 2003 do not include mt in PATH, for not entirely clear reasons. |
| # This shim detects that scenario and adds the winnt folder to MSVS_PATH. |
| RET=0 |
| if [[ ${i/.*/} = "VS7" ]] ; then |
| find_in "${MSVS_PATH//:/*}" mt.exe |
| if [[ $RET -eq 1 ]] ; then |
| MSVS_PATH="$MSVS_PATH$(cygpath -u -f - <<< $ENV\\Bin\\winnt):" |
| RET=0 |
| fi |
| fi |
| |
| # Ensure that these derived values give a valid compiler. |
| if check_environment "${MSVS_PATH//:/*}" "${MSVS_INC//;/*}" "${MSVS_LIB//;/*}" "$i" $arch ; then |
| # Put the package back into FOUND, but augmented with the architecture name and with the |
| # derived values. |
| FOUND["$i-$arch"]="${CURRENT%)} [\"MSVS_PATH\"]=\"$MSVS_PATH\" \ |
| [\"MSVS_INC\"]=\"$MSVS_INC\" \ |
| [\"MSVS_LIB\"]=\"$MSVS_LIB\" \ |
| [\"ASSEMBLER\"]=\"$ASSEMBLER\")" #"# fixes vim syn match error |
| |
| # Check to see if this is a match for the environment C compiler. |
| if [[ -n ${ENV_ARCH+x} ]] ; then |
| TEST_cl=$(PATH="$MSVS_PATH:$PATH" "$WHICH" cl) |
| TEST_cl=${TEST_cl,,} |
| TEST_cl=${TEST_cl/bin\/*_/bin\/} |
| if [[ $TEST_cl = $ENV_cl ]] ; then |
| if [[ ${!ENV_INC/"$MSVS_INC"/} != "${!ENV_INC}" && \ |
| ${!ENV_LIB/"$MSVS_LIB"/} != "${!ENV_LIB}" ]] ; then |
| debug "$i-$arch is a strong candidate for the Environment C compiler" |
| if [[ -n ${ENV_COMPILER+x} ]] ; then |
| if [[ -z ${ENV_COMPILER} ]] ; then |
| ENV_COMPILER=$i-$arch |
| unset WEAK_ENV |
| else |
| # More than one strong candidate - no fall back available |
| unset ENV_COMPILER |
| unset WEAK_ENV |
| fi |
| fi |
| else |
| debug "$i-$arch is a weak candidate for the Environment C compiler" |
| if [[ -n ${WEAK_ENV+x} ]] ; then |
| if [[ -z ${WEAK_ENV} ]] ; then |
| WEAK_ENV=$i-$arch |
| else |
| # More than one weak candidate - no fall back available |
| unset WEAK_ENV |
| fi |
| fi |
| fi |
| fi |
| fi |
| fi |
| done |
| |
| # Does this package match the current preference? Note that PREFERENCE and TEST are constructed in |
| # a cunning (and hopefully not too "You are not expected to understand this" way) such that $PREF |
| # will always equal $i, unless $PREF = "@". |
| if [[ $PREF = $i ]] ; then |
| # In which case, check that the architecture(s)s were found |
| if [[ -n ${FOUND["$i-$LEFT_ARCH"]+x} && -n ${FOUND["$i-$RIGHT_ARCH"]+x} ]] ; then |
| debug "Solved TARGET_ARCH=$TARGET_ARCH with $i" |
| SOLUTION=$i |
| break |
| fi |
| fi |
| |
| if [[ $PREF != "@" ]] ; then |
| ((pref++)) |
| PREF=${PREFERENCE[$pref]} |
| fi |
| done |
| |
| # If we got this far, then either we failed to find a compiler at all, or we were looking for the |
| # environment compiler (or --all was specified). |
| |
| # Adopt a weak match for the environment compiler, if that's the best we can do. |
| if [[ -n ${ENV_COMPILER+x} && -z ${ENV_COMPILER} && -n ${WEAK_ENV} ]] ; then |
| warning "Assuming Environment C compiler is $WEAK_ENV" |
| ENV_COMPILER=$WEAK_ENV |
| fi |
| |
| declare -A FLIP |
| FLIP=(["x86"]="x64" ["x64"]="x86") |
| |
| if [[ $MODE -eq 0 ]] ; then |
| if [[ $PREF = "@" && -n ${ENV_COMPILER} ]] ; then |
| SOLUTION=${ENV_COMPILER%-$ENV_ARCH} |
| # If --arch wasn't specified, then ensure that the other architecture was also found. If --arch |
| # was specified, then validate that the compiler was valid. This should always happen, unless |
| # something went wrong running the script to get MSVS_PATH, MSVS_LIB and MSVS_INC. |
| if [[ -n ${FOUND["$SOLUTION-${FLIP[$ENV_ARCH]}"]+x} || |
| -n ${FOUND["$SOLUTION-$TARGET_ARCH"]+x} ]] ; then |
| debug "Solved with $SOLUTION" |
| else |
| unset SOLUTION |
| unset ENV_ARCH |
| fi |
| fi |
| |
| if [[ -z ${SOLUTION+x} ]] ; then |
| ((pref++)) |
| debug "Search remaining: ${PREFERENCE[*]}" |
| TEST_ARCH=$TARGET_ARCH |
| for i in "${PREFERENCE[@]:$pref}" ; do |
| if [[ -n ${FOUND["$i-$LEFT_ARCH"]+x} && -n ${FOUND["$i-$RIGHT_ARCH"]+x} ]] ; then |
| debug "Solved TARGET_ARCH='$TARGET_ARCH' with $i" |
| SOLUTION=$i |
| break |
| fi |
| done |
| fi |
| fi |
| |
| debug "Solution: $SOLUTION" |
| |
| if [[ -n ${ENV_COMPILER} && $MODE -eq 1 ]] ; then |
| echo "Identified Environment C compiler as $ENV_COMPILER" |
| fi |
| |
| if [[ $MODE -eq 1 ]] ; then |
| echo "Installed and usable packages:" |
| for i in "${!FOUND[@]}" ; do |
| echo " $i" |
| done | sort |
| exit 0 |
| fi |
| |
| if [[ -n $SOLUTION ]] ; then |
| eval COMPILER=${FOUND[$SOLUTION-$LEFT_ARCH]} |
| output MSVS_NAME "${COMPILER["DISPLAY"]}" $LEFT_ARCH |
| output MSVS_PATH "${COMPILER["MSVS_PATH"]}" $LEFT_ARCH |
| output MSVS_INC "${COMPILER["MSVS_INC"]}" $LEFT_ARCH |
| output MSVS_LIB "${COMPILER["MSVS_LIB"]}" $LEFT_ARCH |
| if [[ $ML_REQUIRED -eq 1 ]] ; then |
| output MSVS_ML "${COMPILER["ASSEMBLER"]%.exe}" always |
| fi |
| if [[ -z $TARGET_ARCH ]] ; then |
| eval COMPILER=${FOUND[$SOLUTION-$RIGHT_ARCH]} |
| output MSVS64_PATH "${COMPILER["MSVS_PATH"]}" $RIGHT_ARCH |
| output MSVS64_INC "${COMPILER["MSVS_INC"]}" $RIGHT_ARCH |
| output MSVS64_LIB "${COMPILER["MSVS_LIB"]}" $RIGHT_ARCH |
| if [[ $ML_REQUIRED -eq 1 ]] ; then |
| output MSVS64_ML "${COMPILER["ASSEMBLER"]%.exe}" always |
| fi |
| fi |
| exit 0 |
| else |
| exit 1 |
| fi |