From 4002bdf8738860f835c8b8af440b2d34672c1695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Mon, 20 Mar 2017 11:20:22 +0100 Subject: [PATCH 01/18] Added missing VCC C++ options to nim.cfg --- config/nim.cfg | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/config/nim.cfg b/config/nim.cfg index 9374e2b883..2734975464 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -181,26 +181,39 @@ clang.options.size = "-Os" # Configuration for the Visual C/C++ compiler: vcc.exe = "vccexe.exe" +vcc.cpp.exe = "vccexe.exe" vcc.linkerexe = "vccexe.exe" +vcc.cpp.linkerexe = "vccexe.exe" # set the options for specific platforms: @if i386: vcc.options.always = "--platform:x86 /nologo" +vcc.cpp.options.always = "--platform:x86 /nologo /EHsc" vcc.options.linker = "--platform:x86 /nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB +vcc.cpp.options.linker = "--platform:x86 /nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB @elif amd64: vcc.options.always = "--platform:amd64 /nologo" +vcc.cpp.options.always = "--platform:amd64 /nologo /EHsc" vcc.options.linker = "--platform:amd64 /nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB +vcc.cpp.options.linker = "--platform:amd64 /nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB @elif arm: vcc.options.always = "--platform:arm /nologo" +vcc.cpp.options.always = "--platform:arm /nologo /EHsc" vcc.options.linker = "--platform:arm /nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB +vcc.cpp.options.linker = "--platform:arm /nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB @else: vcc.options.always = "/nologo" +vcc.cpp.options.always = "/nologo /EHsc" vcc.options.linker = "/nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB +vcc.cpp.options.linker = "/nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB @end vcc.options.debug = "/Zi /FS /Od" +vcc.cpp.options.debug = "/Zi /FS /Od" vcc.options.speed = "/O2" +vcc.cpp.options.speed = "/O2" vcc.options.size = "/O1" +vcc.cpp.options.size = "/O1" # Configuration for the Tiny C Compiler: tcc.options.always = "-w" From 7267d35c3fe761364d90410ba1aae991bd8e92ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Mon, 20 Mar 2017 11:22:21 +0100 Subject: [PATCH 02/18] Removed arch option in compiler definition Previous Artifiact, changed to better reflect the settings in the default nim.cfg --- compiler/extccomp.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 70cd411fef..f92fe82d64 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -112,7 +112,7 @@ compiler vcc: result = ( name: "vcc", objExt: "obj", - optSpeed: " /Ogityb2 /G7 /arch:SSE2 ", + optSpeed: " /Ogityb2 /G7 ", optSize: " /O1 /G7 ", compilerExe: "cl", cppCompiler: "cl", From 93e03144fc769e95a92fcf91c34f40610b754a3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Mon, 20 Mar 2017 16:22:23 +0100 Subject: [PATCH 03/18] Added Visual Studio build artifacts to gitignore The Microsoft Linker creates an incremental linker database (extension: ilk) Debugging symbols are stored in pdb files. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 50fa9a431b..1b9beb5c16 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,8 @@ dnimcache/ *.zip *.iss *.log +*.ilk +*.pdb mapping.txt tags From 9446ae5576e43285a28c4b1705a139d317867328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Mon, 20 Mar 2017 16:23:54 +0100 Subject: [PATCH 04/18] Moved vccenv over to vcvarsall Updated vccexe with new command-line arguments --- tools/vccenv/vccenv.nim | 58 ------------------------------ tools/vccenv/vccexe.nim | 57 +++++++++++++++++++++-------- tools/vccenv/vcvarsall.nim | 73 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 72 deletions(-) delete mode 100644 tools/vccenv/vccenv.nim create mode 100644 tools/vccenv/vcvarsall.nim diff --git a/tools/vccenv/vccenv.nim b/tools/vccenv/vccenv.nim deleted file mode 100644 index a335efd10f..0000000000 --- a/tools/vccenv/vccenv.nim +++ /dev/null @@ -1,58 +0,0 @@ -import strtabs, os, osproc, streams, strutils - -const - comSpecEnvKey = "ComSpec" - vsComnToolsEnvKeys = [ - "VS140COMNTOOLS", - "VS130COMNTOOLS", - "VS120COMNTOOLS", - "VS110COMNTOOLS", - "VS100COMNTOOLS", - "VS90COMNTOOLS" - ] - vcvarsallRelativePath = joinPath("..", "..", "VC", "vcvarsall") - -proc getVsComnToolsPath*(): TaintedString = - for vsComnToolsEnvKey in vsComnToolsEnvKeys: - let vsComnToolsEnvVal = getEnv vsComnToolsEnvKey - if vsComnToolsEnvVal.len > 0: - return vsComnToolsEnvVal - -proc getVccEnv*(platform: string, windowsStoreSdk: bool = false, - sdkVersion: string = nil): StringTableRef = - var comSpecCommandString = getEnv comSpecEnvKey - if comSpecCommandString.len == 0: - comSpecCommandString = "cmd" - - let vsComnToolsPath = getVsComnToolsPath() - if vsComnToolsPath.len < 1: - return nil - let vcvarsallPath = expandFilename joinPath(vsComnToolsPath, vcvarsallRelativePath) - - var vcvarsallArgs: seq[string] = @[] - if platform.len > 0: - vcvarsallArgs.add(platform) - if windowsStoreSdk: - vcvarsallArgs.add("store") - if sdkVersion.len > 0: - vcvarsallArgs.add(sdkVersion) - let vcvarsallArgString = vcvarsallArgs.join(" ") - - var vcvarsallCommandString: string - if vcvarsallArgString.len > 0: - vcvarsallCommandString = "\"$1\" $2" % [vcvarsallPath, vcvarsallArgString] - else: - vcvarsallCommandString = vcvarsallPath - - let vcvarsallExecCommand = "\"$1\" /C \"$2 && SET\"" % - [comSpecCommandString, vcvarsallCommandString] - when defined(release): - let vccvarsallOptions = {poEvalCommand, poDemon} - else: - let vccvarsallOptions = {poEchoCmd, poEvalCommand, poDemon} - let vcvarsallStdOut = execProcess(vcvarsallExecCommand, options = vccvarsallOptions) - result = newStringTable(modeCaseInsensitive) - for line in vcvarsallStdOut.splitLines: - let idx = line.find('=') - if idx > 0: - result[line[0..(idx - 1)]] = line[(idx + 1)..(line.len - 1)] diff --git a/tools/vccenv/vccexe.nim b/tools/vccenv/vccexe.nim index 8922468309..4ecbfe50e1 100644 --- a/tools/vccenv/vccexe.nim +++ b/tools/vccenv/vccexe.nim @@ -1,4 +1,4 @@ -import strutils, strtabs, os, osproc, vccenv +import strutils, strtabs, os, osproc, vcvarsall when defined(release): let vccOptions = {poParentStreams} @@ -6,47 +6,73 @@ else: let vccOptions = {poEchoCmd, poParentStreams} const + vcvarsallPrefix = "--vcvarsall" + commandPrefix = "--command" platformPrefix = "--platform" - winstorePrefix = "--winstore" + sdktypePrefix = "--sdktype" sdkversionPrefix = "--sdkversion" + vcvarsallSepIdx = vcvarsallPrefix.len + commandSepIdx = commandPrefix.len platformSepIdx = platformPrefix.len + sdktypeSepIdx = sdktypePrefix.len sdkversionSepIdx = sdkversionPrefix.len HelpText = """ +-----------------------------------------------------------------+ | Microsoft C/C++ compiler wrapper for Nim | -| (c) 2016 Fredrik Høisæther Rasch | +| (c) 2017 Fredrik Hoeisaether Rasch | +-----------------------------------------------------------------+ Usage: vccexe [options] [compileroptions] Options: + --vcvarsall: Path to the Developer Command Prompt utility vcvarsall.bat that selects + the appropiate devlopment settings. + Usual path for Visual Studio 2015 and below: + %VSInstallDir%\VC\vcvarsall + Usual path for Visual Studio 2017 and above: + %VSInstallDir%\VC\Auxiliary\Build\vcvarsall + --command: Specify the command to run once the development environment is loaded. + can be any command-line argument. Any arguments not recognized by vccexe + are passed on as arguments to this command. + cl.exe is invoked by default if this argument is omitted. --platform: Specify the Compiler Platform Tools architecture : x86 | amd64 | arm | x86_amd64 | x86_arm | amd64_x86 | amd64_arm - --winstore Use Windows Store (rather than desktop) development tools + Values with two architectures (like x86_amd64) specify the architecture + of the cross-platform compiler (e.g. x86) and the target it compiles to (e.g. amd64). + --sdktype: Specify the SDK flavor to use. Defaults to the Desktop SDK. + : {empty} | store | uwp | onecore --sdkversion: Use a specific Windows SDK version: is either the full Windows 10 SDK version number or "8.1" to use the windows 8.1 SDK Other command line arguments are passed on to the -Microsoft C/C++ compiler for the specified SDK toolset +secondary command specified by --command or to the +Microsoft (R) C/C++ Optimizing Compiler if no secondary +command was specified """ when isMainModule: - var platformArg: string = nil + var vcvarsallArg: string = nil + var commandArg: string = nil + var platformArg: VccArch + var sdkTypeArg: VccPlatformType var sdkVersionArg: string = nil - var storeArg: bool = false var clArgs: seq[TaintedString] = @[] var wrapperArgs = commandLineParams() for wargv in wrapperArgs: # Check whether the current argument contains -- prefix - if wargv.startsWith(platformPrefix): # Check for platform - platformArg = wargv.substr(platformSepIdx + 1) - elif wargv == winstorePrefix: # Check for winstore - storeArg = true + if wargv.startsWith(vcvarsallPrefix): # Check for vcvarsall + vcvarsallArg = wargv.substr(vcvarsallSepIdx + 1) + elif wargv.startsWith(commandPrefix): # Check for command + commandArg = wargv.substr(commandSepIdx + 1) + elif wargv.startsWith(platformPrefix): # Check for platform + platformArg = parseEnum[VccArch](wargv.substr(platformSepIdx + 1)) + elif wargv.startsWith(sdktypePrefix): # Check for sdktype + sdkTypeArg = parseEnum[VccPlatformType](wargv.substr(sdktypeSepIdx + 1)) elif wargv.startsWith(sdkversionPrefix): # Check for sdkversion sdkVersionArg = wargv.substr(sdkversionSepIdx + 1) else: # Regular cl.exe argument -> store for final cl.exe invocation @@ -54,10 +80,13 @@ when isMainModule: echo HelpText clArgs.add(wargv) - var vccEnvStrTab = getVccEnv(platformArg, storeArg, sdkVersionArg) - if vccEnvStrTab != nil: - for vccEnvKey, vccEnvVal in vccEnvStrTab: + var vcvars = vccVarsAll(vcvarsallArg, platformArg, sdkTypeArg, sdkVersionArg) + if vcvars != nil: + for vccEnvKey, vccEnvVal in vcvars: putEnv(vccEnvKey, vccEnvVal) + + if commandArg.len < 1: + commandArg = "cl.exe" let vccProcess = startProcess( "cl.exe", args = clArgs, diff --git a/tools/vccenv/vcvarsall.nim b/tools/vccenv/vcvarsall.nim new file mode 100644 index 0000000000..db97177b0f --- /dev/null +++ b/tools/vccenv/vcvarsall.nim @@ -0,0 +1,73 @@ +import strtabs, strutils, os, osproc + +const + comSpecEnvKey = "ComSpec" # Environment Variable that specifies the command-line application path in Windows + # Usually set to cmd.exe + vcvarsallDefaultPath = "vcvarsall.bat" + +type + VccArch* = enum + vccarchUnspecified = "", + vccarchX86 = "x86", + vccarchAmd64 = "amd64", + vccarchX86Amd64 = "x86_amd64", + vccarchX86Arm = "x86_arm", + vccarchX86Arm64 = "x86_arm64", + vccarchAmd64X86 = "amd64_x86", + vccarchAmd64Arm = "amd64_arm", + vccarchAmd64Arm64 = "amd64_arm64", + vccarchX64 = "x64", + vccarchX64X86 = "x64_x86", + vccarchX64Arm = "x64_arm", + vccarchX64Arm64 = "x64_arm64" + + VccPlatformType* = enum + vccplatEmpty = "", + vccplatStore = "store", + vccplatUWP = "uwp", + vccplatOneCore = "onecore" + +proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type: VccPlatformType = vccplatEmpty, sdk_version: string = nil): StringTableRef = + var vccvarsallpath = path + # Assume that default executable is in current directory or in PATH + if path == nil or path.len < 1: + vccvarsallpath = vcvarsallDefaultPath + + var args: seq[string] = @[] + + let archStr: string = $arch + if archStr.len > 0: + args.add(archStr) + + let platStr: string = $platform_type + if platStr.len > 0: + args.add(platStr) + + if sdk_version.len > 0: + args.add(sdk_version) + + let argStr = args.join " " + + var vcvarsExec: string + if argStr.len > 0: + vcvarsExec = "\"$1\" $2" % [vccvarsallpath, argStr] + else: + vcvarsExec = "\"$1\"" % vccvarsallpath + + var comSpecCmd = getenv comSpecEnvKey + if comSpecCmd.len < 1: + comSpecCmd = "cmd" + + let comSpecExec = "\"$1\" /C \"$2 && SET\"" % [comSpecCmd, vcvarsExec] + when defined(release): + let comSpecOpts = {poEvalCommand, poDemon, poStdErrToStdOut} + else: + let comSpecOpts = {poEchoCmd, poEvalCommand, poDemon, poStdErrToStdOut} + let comSpecOut = execProcess(comSpecExec, options = comSpecOpts) + result = newStringTable(modeCaseInsensitive) + for line in comSpecOut.splitLines: + when not defined(release) or defined(debug): + echo line + let idx = line.find('=') + if idx > 0: + result[line[0..(idx - 1)]] = line[(idx + 1)..(line.len - 1)] From 45f61ad0466d317bb77b151ed633389fd0eeac22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Mon, 20 Mar 2017 16:36:16 +0100 Subject: [PATCH 05/18] Moved vccexe to a more logical place (vccexe) --- tools/{vccenv => vccexe}/vccexe.nim | 0 tools/{vccenv => vccexe}/vcvarsall.nim | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tools/{vccenv => vccexe}/vccexe.nim (100%) rename tools/{vccenv => vccexe}/vcvarsall.nim (100%) diff --git a/tools/vccenv/vccexe.nim b/tools/vccexe/vccexe.nim similarity index 100% rename from tools/vccenv/vccexe.nim rename to tools/vccexe/vccexe.nim diff --git a/tools/vccenv/vcvarsall.nim b/tools/vccexe/vcvarsall.nim similarity index 100% rename from tools/vccenv/vcvarsall.nim rename to tools/vccexe/vcvarsall.nim From 503a816b362c124b23ec45bf19113bac57bdb136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Mon, 20 Mar 2017 22:18:26 +0100 Subject: [PATCH 06/18] Corrected erroneous vccexe tool path in koch --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 6eee09b7bc..2ea53b2a75 100644 --- a/koch.nim +++ b/koch.nim @@ -219,7 +219,7 @@ proc bundleNimsuggest(buildExe: bool) = removeFile("nimsuggest/nimsuggest".exe) proc buildVccTool() = - nimexec("c -o:bin/vccexe.exe tools/vccenv/vccexe") + nimexec("c -o:bin/vccexe.exe tools/vccexe/vccexe") proc bundleWinTools() = nimexec("c tools/finish.nim") From 86206b253346ac221ca4392b339741064f031970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Mon, 20 Mar 2017 22:32:17 +0100 Subject: [PATCH 07/18] Added explanatory comments about vccexe and its path argument in nim.cfg --- config/nim.cfg | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/config/nim.cfg b/config/nim.cfg index 2734975464..aff74af53f 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -180,6 +180,39 @@ clang.options.speed = "-O3" clang.options.size = "-Os" # Configuration for the Visual C/C++ compiler: +# VCCEXE is a tool that invokes the Visual Studio Developer Command Prompt +# before calling the compiler. +# Please make sure either Visual Studio or C++ Build SKU is installed when using the vcc compiler backend. + +# With Side-by-side installation support in VS2017, the VCC SDK no longer defines global environment variables +# that can be used to discover the path for your SDK installation. +# Because of that and greater flexibility you have to specify the path to the vcvarsall file that initializes +# the C/C++ Devloper Environment for a specified target architecture. + +# The following are the default paths for vcvarsall ordered by Visual Studio versions. +# Uncomment ONE of the following lines and/or adjust it to match your specific installation + +# Visual Studio 2008 +# --passC:"--vcvarsall:\"%VS90COMNTOOLS%..\\\..\\VC\\vcvarsall\"" +# --passL:"--vcvarsall:\"%VS90COMNTOOLS%..\\\..\\VC\\vcvarsall\"" +# Visual Studio 2010 +# --passC:"--vcvarsall:\"%VS100COMNTOOLS%..\\\..\\VC\\vcvarsall\"" +# --passL:"--vcvarsall:\"%VS100COMNTOOLS%..\\\..\\VC\\vcvarsall\"" +# Visual Studio 2012 +# --passC:"--vcvarsall:\"%VS110COMNTOOLS%..\\\..\\VC\\vcvarsall\"" +# --passL:"--vcvarsall:\"%VS110COMNTOOLS%..\\\..\\VC\\vcvarsall\"" +# Visual Studio 2013 +# --passC:"--vcvarsall:\"%VS120COMNTOOLS%..\\\..\\VC\\vcvarsall\"" +# --passL:"--vcvarsall:\"%VS120COMNTOOLS%..\\\..\\VC\\vcvarsall\"" +# Visual Studio 2015 +# --passC:"--vcvarsall:\"%VS140COMNTOOLS%..\\\..\\VC\\vcvarsall\"" +# --passL:"--vcvarsall:\"%VS140COMNTOOLS%..\\\..\\VC\\vcvarsall\"" +# Visual Studio 2017 Community Edition (replace the 'Community' path segment, with the Installation Instance name for your installtion) +# --passC:"--vcvarsall:\"C:\\Program Files (x86)\\Microsoft Visual Studio\\2107\\Community\\VC\\Auxiliary\\Build\\vcvarsall\"" +# --passL:"--vcvarsall:\"C:\\Program Files (x86)\\Microsoft Visual Studio\\2107\\Community\\VC\\Auxiliary\\Build\\vcvarsall\"" + +# Note that there are two entries, one 'passC' and one 'passL' entry, so that nim loads the tools both for compilation and linking. + vcc.exe = "vccexe.exe" vcc.cpp.exe = "vccexe.exe" vcc.linkerexe = "vccexe.exe" From 5341d36397ed726d4174cba54fc6bdf52fb77074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Tue, 21 Mar 2017 01:08:57 +0100 Subject: [PATCH 08/18] Reduced verbosity of vcvarsall Only lines that are not part of the loaded developer environment are echoed --- tools/vccexe/vcvarsall.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/vccexe/vcvarsall.nim b/tools/vccexe/vcvarsall.nim index db97177b0f..7980707992 100644 --- a/tools/vccexe/vcvarsall.nim +++ b/tools/vccexe/vcvarsall.nim @@ -66,8 +66,9 @@ proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type let comSpecOut = execProcess(comSpecExec, options = comSpecOpts) result = newStringTable(modeCaseInsensitive) for line in comSpecOut.splitLines: - when not defined(release) or defined(debug): - echo line let idx = line.find('=') if idx > 0: result[line[0..(idx - 1)]] = line[(idx + 1)..(line.len - 1)] + else: + when not defined(release) or defined(debug): + echo line From 289f72ad67faf273b63d49c0983072f43960e370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Tue, 21 Mar 2017 01:10:11 +0100 Subject: [PATCH 09/18] Fixed vccexe, now regards the command argument --- tools/vccexe/vccexe.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/vccexe/vccexe.nim b/tools/vccexe/vccexe.nim index 4ecbfe50e1..d5ed143a56 100644 --- a/tools/vccexe/vccexe.nim +++ b/tools/vccexe/vccexe.nim @@ -88,7 +88,7 @@ when isMainModule: if commandArg.len < 1: commandArg = "cl.exe" let vccProcess = startProcess( - "cl.exe", + commandArg, args = clArgs, options = vccOptions ) From 747e5a6b64ef783479bda03b1505e9fdfea6c78a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Tue, 21 Mar 2017 01:24:45 +0100 Subject: [PATCH 10/18] vccenv module for vcc auto-discovery Only works for VCC Installations with Visual Studio 2015 and below --- tools/vccexe/vccenv.nim | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tools/vccexe/vccenv.nim diff --git a/tools/vccexe/vccenv.nim b/tools/vccexe/vccenv.nim new file mode 100644 index 0000000000..addf77e381 --- /dev/null +++ b/tools/vccexe/vccenv.nim @@ -0,0 +1,25 @@ +import os + +type + VccEnvVersion* = enum + vsUndefined = (0, ""), + vs90 = (90, "VS90COMNTOOLS"), # Visual Studio 2008 + vs100 = (100, "VS100COMNTOOLS"), # Visual Studio 2010 + vs110 = (110, "VS110COMNTOOLS"), # Visual Studio 2012 + vs120 = (120, "VS120COMNTOOLS"), # Visual Studio 2013 + vs140 = (140, "VS140COMNTOOLS") # Visual Studio 2015 + +const + vcvarsallRelativePath = joinPath("..", "..", "VC", "vcvarsall") + +proc vccEnvVcVarsAllPath*(version: VccEnvVersion = vsUndefined): string = + if version == vsUndefined: + for tryVersion in [vs140, vs120, vs110, vs100, vs90]: + let tryPath = vccEnvVcVarsAllPath(tryVersion) + if tryPath.len > 0: + result = tryPath + else: # Specific version requested + let key = $version + let val = getEnv key + if val.len > 0: + result = expandFilename(val & vcvarsallRelativePath) From aa14ee1856ccb5f32cd71ca272620482f3964f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Tue, 21 Mar 2017 01:25:16 +0100 Subject: [PATCH 11/18] Created vccdiscover utility for vcc auto-discovery --- tools/vccexe/vccdiscover.nim | 76 ++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 tools/vccexe/vccdiscover.nim diff --git a/tools/vccexe/vccdiscover.nim b/tools/vccexe/vccdiscover.nim new file mode 100644 index 0000000000..72e415f3d6 --- /dev/null +++ b/tools/vccexe/vccdiscover.nim @@ -0,0 +1,76 @@ +import strutils, os, vccenv + +type + VccVersion* = enum + vccUndefined = (0, ""), + vcc90 = vs90, # Visual Studio 2008 + vcc100 = vs100, # Visual Studio 2010 + vcc110 = vs110, # Visual Studio 2012 + vcc120 = vs120, # Visual Studio 2013 + vcc140 = vs140 # Visual Studio 2015 + +proc discoverVccVcVarsAllPath*(version: VccVersion = vccUndefined): string = + # TODO: Attempt discovery using vswhere utility. + + # Attempt discovery through VccEnv + # (Trying Visual Studio Common Tools Environment Variables) + result = vccEnvVcVarsAllPath(cast[VccEnvVersion](version)) + if result.len > 0: + return + + # All attempts to dicover vcc failed + +when isMainModule: + const + helpText = """ ++-----------------------------------------------------------------+ +| Microsoft C/C++ Compiler Discovery Utility | +| (c) 2017 Fredrik Hoeisaether Rasch | ++-----------------------------------------------------------------+ + +Discovers the path to the Developer Command Prompt for the +specified versions, or attempts to discover the latest installed +version if no specific version is requested. + +Usage: + vccdiscover [...] +Arguments: + Optionally specify the version to discover + Valid values: 0, 90, 100, 110, 120, 140 + A value of 0 will discover the latest installed SDK +""" + var quitValue = 0 + let args = commandLineParams() + if args.len < 1: + let path = discoverVccVcVarsAllPath() + if path.len < 1: + echo "latest: VCC installation discovery failed." + quitValue = 1 + else: + echo "latest: " & path + + for argv in args: + # Strip leading hyphens or slashes, if someone tries -?, /?, --help or /help + if argv.len < 1: + continue + var argvValue = argv + while argvValue[0] == '-' or argvValue[0] == '/': + argvValue = argvValue.substr(1) + if argvValue.len < 1: + continue + + if cmpIgnoreCase(argvValue, "help") == 0 or cmpIgnoreCase(argvValue, "?") == 0: + echo helpText + continue + + let version = cast[VccVersion](parseInt(argvValue)) + let path = discoverVccVcVarsAllPath(version) + var head = $version + if head.len < 1: + head = "latest" + if path.len < 1: + echo head & ": VCC installation discovery failed." + inc quitValue + else: + echo "$1: $2" % [head, path] + quit quitValue \ No newline at end of file From 05faad30a456d772bc24f3e7d1197bf6bb0e6387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Tue, 21 Mar 2017 01:26:44 +0100 Subject: [PATCH 12/18] vccexe tool can use vccdiscover utility Used when vcvarsall path is not specified, or if only a vccversion is specified --- tools/vccexe/vccexe.nim | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/tools/vccexe/vccexe.nim b/tools/vccexe/vccexe.nim index d5ed143a56..6d0908c3b7 100644 --- a/tools/vccexe/vccexe.nim +++ b/tools/vccexe/vccexe.nim @@ -1,4 +1,4 @@ -import strutils, strtabs, os, osproc, vcvarsall +import strutils, strtabs, os, osproc, vcvarsall, vccdiscover when defined(release): let vccOptions = {poParentStreams} @@ -6,12 +6,14 @@ else: let vccOptions = {poEchoCmd, poParentStreams} const + vccversionPrefix = "--vccversion" vcvarsallPrefix = "--vcvarsall" commandPrefix = "--command" platformPrefix = "--platform" sdktypePrefix = "--sdktype" sdkversionPrefix = "--sdkversion" + vccversionSepIdx = vccversionPrefix.len vcvarsallSepIdx = vcvarsallPrefix.len commandSepIdx = commandPrefix.len platformSepIdx = platformPrefix.len @@ -27,6 +29,9 @@ const Usage: vccexe [options] [compileroptions] Options: + --vccversion: Optionally specify the VCC version to discover + : 0, 90, 100, 110, 120, 140 + Argument value is passed on to vccdiscover utility --vcvarsall: Path to the Developer Command Prompt utility vcvarsall.bat that selects the appropiate devlopment settings. Usual path for Visual Studio 2015 and below: @@ -54,6 +59,7 @@ command was specified """ when isMainModule: + var vccversionArg: seq[string] = @[] var vcvarsallArg: string = nil var commandArg: string = nil var platformArg: VccArch @@ -65,7 +71,9 @@ when isMainModule: var wrapperArgs = commandLineParams() for wargv in wrapperArgs: # Check whether the current argument contains -- prefix - if wargv.startsWith(vcvarsallPrefix): # Check for vcvarsall + if wargv.startsWith(vccversionPrefix): # Check for vccversion + vccversionArg.add(wargv.substr(vccversionSepIdx + 1)) + elif wargv.startsWith(vcvarsallPrefix): # Check for vcvarsall vcvarsallArg = wargv.substr(vcvarsallSepIdx + 1) elif wargv.startsWith(commandPrefix): # Check for command commandArg = wargv.substr(commandSepIdx + 1) @@ -80,6 +88,18 @@ when isMainModule: echo HelpText clArgs.add(wargv) + for vccversionItem in vccversionArg: + var vccversionValue: VccVersion + try: + vccversionValue = cast[VccVersion](parseInt(vccversionItem)) + except ValueError: + continue + vcvarsallArg = discoverVccVcVarsAllPath(vccversionValue) + if vcvarsallArg.len > 0: + break + if vcvarsallArg.len < 1 and vccversionArg.len < 1: + vcvarsallArg = discoverVccVcVarsAllPath() + var vcvars = vccVarsAll(vcvarsallArg, platformArg, sdkTypeArg, sdkVersionArg) if vcvars != nil: for vccEnvKey, vccEnvVal in vcvars: From c7a0087c827bfd07e4a0fb9984986bd48e4e2701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Tue, 21 Mar 2017 01:31:31 +0100 Subject: [PATCH 13/18] vccdiscover: Added additional documentation Added explaination that vccdiscover output is machine-parsable --- tools/vccexe/vccdiscover.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/vccexe/vccdiscover.nim b/tools/vccexe/vccdiscover.nim index 72e415f3d6..88dfd19995 100644 --- a/tools/vccexe/vccdiscover.nim +++ b/tools/vccexe/vccdiscover.nim @@ -38,6 +38,10 @@ Arguments: Optionally specify the version to discover Valid values: 0, 90, 100, 110, 120, 140 A value of 0 will discover the latest installed SDK + +For each specified version the utility prints a line with the +following format: +: """ var quitValue = 0 let args = commandLineParams() From 513b0fed4da0a3bab46bc55b83e21575f41c2145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Tue, 21 Mar 2017 01:35:46 +0100 Subject: [PATCH 14/18] vcvarsallpath explaination no longer needed with vccdiscover This reverts commit 7c4911d --- config/nim.cfg | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/config/nim.cfg b/config/nim.cfg index aff74af53f..9c94793639 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -184,35 +184,6 @@ clang.options.size = "-Os" # before calling the compiler. # Please make sure either Visual Studio or C++ Build SKU is installed when using the vcc compiler backend. -# With Side-by-side installation support in VS2017, the VCC SDK no longer defines global environment variables -# that can be used to discover the path for your SDK installation. -# Because of that and greater flexibility you have to specify the path to the vcvarsall file that initializes -# the C/C++ Devloper Environment for a specified target architecture. - -# The following are the default paths for vcvarsall ordered by Visual Studio versions. -# Uncomment ONE of the following lines and/or adjust it to match your specific installation - -# Visual Studio 2008 -# --passC:"--vcvarsall:\"%VS90COMNTOOLS%..\\\..\\VC\\vcvarsall\"" -# --passL:"--vcvarsall:\"%VS90COMNTOOLS%..\\\..\\VC\\vcvarsall\"" -# Visual Studio 2010 -# --passC:"--vcvarsall:\"%VS100COMNTOOLS%..\\\..\\VC\\vcvarsall\"" -# --passL:"--vcvarsall:\"%VS100COMNTOOLS%..\\\..\\VC\\vcvarsall\"" -# Visual Studio 2012 -# --passC:"--vcvarsall:\"%VS110COMNTOOLS%..\\\..\\VC\\vcvarsall\"" -# --passL:"--vcvarsall:\"%VS110COMNTOOLS%..\\\..\\VC\\vcvarsall\"" -# Visual Studio 2013 -# --passC:"--vcvarsall:\"%VS120COMNTOOLS%..\\\..\\VC\\vcvarsall\"" -# --passL:"--vcvarsall:\"%VS120COMNTOOLS%..\\\..\\VC\\vcvarsall\"" -# Visual Studio 2015 -# --passC:"--vcvarsall:\"%VS140COMNTOOLS%..\\\..\\VC\\vcvarsall\"" -# --passL:"--vcvarsall:\"%VS140COMNTOOLS%..\\\..\\VC\\vcvarsall\"" -# Visual Studio 2017 Community Edition (replace the 'Community' path segment, with the Installation Instance name for your installtion) -# --passC:"--vcvarsall:\"C:\\Program Files (x86)\\Microsoft Visual Studio\\2107\\Community\\VC\\Auxiliary\\Build\\vcvarsall\"" -# --passL:"--vcvarsall:\"C:\\Program Files (x86)\\Microsoft Visual Studio\\2107\\Community\\VC\\Auxiliary\\Build\\vcvarsall\"" - -# Note that there are two entries, one 'passC' and one 'passL' entry, so that nim loads the tools both for compilation and linking. - vcc.exe = "vccexe.exe" vcc.cpp.exe = "vccexe.exe" vcc.linkerexe = "vccexe.exe" From 24f07d9b71a5302047fa0183318d35fbc74222b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Tue, 21 Mar 2017 12:14:58 +0100 Subject: [PATCH 15/18] Exchanged debug compilation with verbose command-line argument --- tools/vccexe/vccexe.nim | 15 +++++++++------ tools/vccexe/vcvarsall.nim | 14 ++++++-------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/tools/vccexe/vccexe.nim b/tools/vccexe/vccexe.nim index 6d0908c3b7..4dec0db042 100644 --- a/tools/vccexe/vccexe.nim +++ b/tools/vccexe/vccexe.nim @@ -1,10 +1,5 @@ import strutils, strtabs, os, osproc, vcvarsall, vccdiscover -when defined(release): - let vccOptions = {poParentStreams} -else: - let vccOptions = {poEchoCmd, poParentStreams} - const vccversionPrefix = "--vccversion" vcvarsallPrefix = "--vcvarsall" @@ -12,6 +7,7 @@ const platformPrefix = "--platform" sdktypePrefix = "--sdktype" sdkversionPrefix = "--sdkversion" + verbosePrefix = "--verbose" vccversionSepIdx = vccversionPrefix.len vcvarsallSepIdx = vcvarsallPrefix.len @@ -65,6 +61,7 @@ when isMainModule: var platformArg: VccArch var sdkTypeArg: VccPlatformType var sdkVersionArg: string = nil + var verboseArg: bool = false var clArgs: seq[TaintedString] = @[] @@ -83,6 +80,8 @@ when isMainModule: sdkTypeArg = parseEnum[VccPlatformType](wargv.substr(sdktypeSepIdx + 1)) elif wargv.startsWith(sdkversionPrefix): # Check for sdkversion sdkVersionArg = wargv.substr(sdkversionSepIdx + 1) + elif wargv.startsWith(verbosePrefix): + verboseArg = true else: # Regular cl.exe argument -> store for final cl.exe invocation if (wargv.len == 2) and (wargv[1] == '?'): echo HelpText @@ -100,11 +99,15 @@ when isMainModule: if vcvarsallArg.len < 1 and vccversionArg.len < 1: vcvarsallArg = discoverVccVcVarsAllPath() - var vcvars = vccVarsAll(vcvarsallArg, platformArg, sdkTypeArg, sdkVersionArg) + var vcvars = vccVarsAll(vcvarsallArg, platformArg, sdkTypeArg, sdkVersionArg, verboseArg) if vcvars != nil: for vccEnvKey, vccEnvVal in vcvars: putEnv(vccEnvKey, vccEnvVal) + var vccOptions = {poParentStreams} + if verboseArg: + vccOptions.incl poEchoCmd + if commandArg.len < 1: commandArg = "cl.exe" let vccProcess = startProcess( diff --git a/tools/vccexe/vcvarsall.nim b/tools/vccexe/vcvarsall.nim index 7980707992..dcd7ef8688 100644 --- a/tools/vccexe/vcvarsall.nim +++ b/tools/vccexe/vcvarsall.nim @@ -27,7 +27,7 @@ type vccplatUWP = "uwp", vccplatOneCore = "onecore" -proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type: VccPlatformType = vccplatEmpty, sdk_version: string = nil): StringTableRef = +proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type: VccPlatformType = vccplatEmpty, sdk_version: string = nil, verbose: bool = false): StringTableRef = var vccvarsallpath = path # Assume that default executable is in current directory or in PATH if path == nil or path.len < 1: @@ -59,16 +59,14 @@ proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type comSpecCmd = "cmd" let comSpecExec = "\"$1\" /C \"$2 && SET\"" % [comSpecCmd, vcvarsExec] - when defined(release): - let comSpecOpts = {poEvalCommand, poDemon, poStdErrToStdOut} - else: - let comSpecOpts = {poEchoCmd, poEvalCommand, poDemon, poStdErrToStdOut} + var comSpecOpts = {poEvalCommand, poDemon, poStdErrToStdOut} + if verbose: + comSpecOpts.incl poEchoCmd let comSpecOut = execProcess(comSpecExec, options = comSpecOpts) result = newStringTable(modeCaseInsensitive) for line in comSpecOut.splitLines: let idx = line.find('=') if idx > 0: result[line[0..(idx - 1)]] = line[(idx + 1)..(line.len - 1)] - else: - when not defined(release) or defined(debug): - echo line + elif verbose: + echo line From 3d21e2ab668c1bf2df58829185507bc739440526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Sun, 26 Mar 2017 17:08:41 +0200 Subject: [PATCH 16/18] Added documentation and explanatory comments to vccexe --- tools/vccexe/vccdiscover.nim | 32 +++++++++++++---- tools/vccexe/vccenv.nim | 40 ++++++++++++++++----- tools/vccexe/vccexe.nim | 14 ++++++++ tools/vccexe/vcvarsall.nim | 67 +++++++++++++++++++++++++----------- 4 files changed, 117 insertions(+), 36 deletions(-) diff --git a/tools/vccexe/vccdiscover.nim b/tools/vccexe/vccdiscover.nim index 88dfd19995..f20bf24418 100644 --- a/tools/vccexe/vccdiscover.nim +++ b/tools/vccexe/vccdiscover.nim @@ -1,15 +1,33 @@ +## VCC compiler backend discovery Utility +## +## Module to discover the path to the vcvarsall utility of a VCC compiler backend. +## The module supports discovery for either the latest recognizable version (default) +## or discovery of a specific VCC compiler backend version. +## +## This module can also produce a standalone command-line executable. +## It supports a `--help` command-line argument. Refer to its output for further +## documentation on the `vccdiscover` standalone command-line application. + import strutils, os, vccenv type - VccVersion* = enum - vccUndefined = (0, ""), - vcc90 = vs90, # Visual Studio 2008 - vcc100 = vs100, # Visual Studio 2010 - vcc110 = vs110, # Visual Studio 2012 - vcc120 = vs120, # Visual Studio 2013 - vcc140 = vs140 # Visual Studio 2015 + VccVersion* = enum ## VCC compiler backend versions + vccUndefined = (0, ""), ## VCC version undefined, resolves to the latest recognizable VCC version + vcc90 = vs90, ## Visual Studio 2008 (Version 9.0) + vcc100 = vs100, ## Visual Studio 2010 (Version 10.0) + vcc110 = vs110, ## Visual Studio 2012 (Version 11.0) + vcc120 = vs120, ## Visual Studio 2013 (Version 12.0) + vcc140 = vs140 ## Visual Studio 2015 (Version 14.0) proc discoverVccVcVarsAllPath*(version: VccVersion = vccUndefined): string = + ## Returns the path to the vcvarsall utility of the specified VCC compiler backend. + ## + ## version + ## The specific version of the VCC compiler backend to discover. + ## Defaults to the latest recognized VCC compiler backend that is found on the system. + ## + ## Returns `nil` if the VCC compiler backend discovery failed. + # TODO: Attempt discovery using vswhere utility. # Attempt discovery through VccEnv diff --git a/tools/vccexe/vccenv.nim b/tools/vccexe/vccenv.nim index addf77e381..6ddf2e29a5 100644 --- a/tools/vccexe/vccenv.nim +++ b/tools/vccexe/vccenv.nim @@ -1,18 +1,40 @@ +## VCC compiler backend installation discovery using Visual Studio common tools +## environment variables. + import os type - VccEnvVersion* = enum - vsUndefined = (0, ""), - vs90 = (90, "VS90COMNTOOLS"), # Visual Studio 2008 - vs100 = (100, "VS100COMNTOOLS"), # Visual Studio 2010 - vs110 = (110, "VS110COMNTOOLS"), # Visual Studio 2012 - vs120 = (120, "VS120COMNTOOLS"), # Visual Studio 2013 - vs140 = (140, "VS140COMNTOOLS") # Visual Studio 2015 + VccEnvVersion* = enum ## The version of the Visual Studio C/C++ Developer Environment to load + ## Valid versions are Versions of Visual Studio that permanently set a COMNTOOLS + ## environment variable. That includes Visual Studio version up to and including + ## Visual Studio 2015 + vsUndefined = (0, ""), ## Version not specified, use latest recogized version on the system + vs90 = (90, "VS90COMNTOOLS"), ## Visual Studio 2008 + vs100 = (100, "VS100COMNTOOLS"), ## Visual Studio 2010 + vs110 = (110, "VS110COMNTOOLS"), ## Visual Studio 2012 + vs120 = (120, "VS120COMNTOOLS"), ## Visual Studio 2013 + vs140 = (140, "VS140COMNTOOLS") ## Visual Studio 2015 const - vcvarsallRelativePath = joinPath("..", "..", "VC", "vcvarsall") + vcvarsallRelativePath = joinPath("..", "..", "VC", "vcvarsall") ## Relative path from the COMNTOOLS path to the vcvarsall file. + +proc vccEnvVcVarsAllPath*(version: VccEnvVersion = vsUndefined): string = + ## Returns the path to the VCC Developer Command Prompt executable for the specified VCC version. + ## + ## Returns `nil` if the specified VCC compiler backend installation was not found. + ## + ## If the `version` parameter is omitted or set to `vsUndefined`, `vccEnvVcVarsAllPath` searches + ## for the latest recognizable version of the VCC tools it can find. + ## + ## `vccEnvVcVarsAllPath` uses the COMNTOOLS environment variables to find the Developer Command Prompt + ## executable path. The COMNTOOLS environment variable are permanently set when Visual Studio is installed. + ## Each version of Visual Studio has its own COMNTOOLS environment variable. E.g.: Visual Studio 2015 sets + ## The VS140COMNTOOLS environment variable. + ## + ## Note: Beginning with Visual Studio 2017, the installers no longer set environment variables to allow for + ## multiple side-by-side installations of Visual Studio. Therefore, `vccEnvVcVarsAllPath` cannot be used + ## to detect the VCC Developer Command Prompt executable path for Visual Studio 2017 and later. -proc vccEnvVcVarsAllPath*(version: VccEnvVersion = vsUndefined): string = if version == vsUndefined: for tryVersion in [vs140, vs120, vs110, vs100, vs90]: let tryPath = vccEnvVcVarsAllPath(tryVersion) diff --git a/tools/vccexe/vccexe.nim b/tools/vccexe/vccexe.nim index 4dec0db042..86ad86d977 100644 --- a/tools/vccexe/vccexe.nim +++ b/tools/vccexe/vccexe.nim @@ -47,6 +47,8 @@ Options: --sdkversion: Use a specific Windows SDK version: is either the full Windows 10 SDK version number or "8.1" to use the windows 8.1 SDK + --verbose Echoes the command line for loading the Developer Command Prompt + and the command line passed on to the secondary command. Other command line arguments are passed on to the secondary command specified by --command or to the @@ -65,6 +67,11 @@ when isMainModule: var clArgs: seq[TaintedString] = @[] + # Cannot use usual command-line argument parser here + # Since vccexe command-line arguments are intermingled + # with the secondary command-line arguments which have + # a syntax that is not supported by the default nim + # argument parser. var wrapperArgs = commandLineParams() for wargv in wrapperArgs: # Check whether the current argument contains -- prefix @@ -87,6 +94,8 @@ when isMainModule: echo HelpText clArgs.add(wargv) + # Support for multiple specified versions. Attempt VCC discovery for each version + # specified, first successful discovery wins for vccversionItem in vccversionArg: var vccversionValue: VccVersion try: @@ -96,9 +105,11 @@ when isMainModule: vcvarsallArg = discoverVccVcVarsAllPath(vccversionValue) if vcvarsallArg.len > 0: break + # VCC version not specified, discover latest (call discover without args) if vcvarsallArg.len < 1 and vccversionArg.len < 1: vcvarsallArg = discoverVccVcVarsAllPath() + # Call vcvarsall to get the appropiate VCC process environment var vcvars = vccVarsAll(vcvarsallArg, platformArg, sdkTypeArg, sdkVersionArg, verboseArg) if vcvars != nil: for vccEnvKey, vccEnvVal in vcvars: @@ -108,8 +119,11 @@ when isMainModule: if verboseArg: vccOptions.incl poEchoCmd + # Default to the cl.exe command if no secondary command was specified if commandArg.len < 1: commandArg = "cl.exe" + + # Run VCC command with the VCC process environment let vccProcess = startProcess( commandArg, args = clArgs, diff --git a/tools/vccexe/vcvarsall.nim b/tools/vccexe/vcvarsall.nim index dcd7ef8688..e7a55069c0 100644 --- a/tools/vccexe/vcvarsall.nim +++ b/tools/vccexe/vcvarsall.nim @@ -1,33 +1,53 @@ +## VCC Developer Command Prompt Loader +## +## In order for the VCC compiler backend to work properly, it requires numerous +## environment variables to be set properly for the desired architecture and compile target. +## For that purpose the VCC compiler ships with the vcvarsall utility which is an executable +## batch script that can be used to properly set up an Command Prompt environment. + import strtabs, strutils, os, osproc const - comSpecEnvKey = "ComSpec" # Environment Variable that specifies the command-line application path in Windows - # Usually set to cmd.exe + comSpecEnvKey = "ComSpec" ## Environment Variable that specifies the command-line application path in Windows + ## Usually set to cmd.exe vcvarsallDefaultPath = "vcvarsall.bat" type - VccArch* = enum + VccArch* = enum ## The VCC compile target architectures vccarchUnspecified = "", - vccarchX86 = "x86", - vccarchAmd64 = "amd64", - vccarchX86Amd64 = "x86_amd64", - vccarchX86Arm = "x86_arm", - vccarchX86Arm64 = "x86_arm64", - vccarchAmd64X86 = "amd64_x86", - vccarchAmd64Arm = "amd64_arm", - vccarchAmd64Arm64 = "amd64_arm64", - vccarchX64 = "x64", - vccarchX64X86 = "x64_x86", - vccarchX64Arm = "x64_arm", - vccarchX64Arm64 = "x64_arm64" + vccarchX86 = "x86", ## VCC for compilation against the x86 architecture. + vccarchAmd64 = "amd64", ## VCC for compilation against the amd64 architecture. + vccarchX86Amd64 = "x86_amd64", ## VCC cross-compilation tools using x86 VCC for compilation against the amd64 architecture. + vccarchX86Arm = "x86_arm", ## VCC cross-compilation tools using x86 VCC for compilation against the ARM architecture. + vccarchX86Arm64 = "x86_arm64", ## VCC cross-compilation tools using x86 VCC for compilation against the ARM (64-bit) architecture. + vccarchAmd64X86 = "amd64_x86", ## VCC cross-compilation tools using amd64 VCC for compilation against the x86 architecture. + vccarchAmd64Arm = "amd64_arm", ## VCC cross-compilation tools using amd64 VCC for compilation against the ARM architecture. + vccarchAmd64Arm64 = "amd64_arm64", ## VCC cross-compilation tools using amd64 VCC for compilation against the ARM (64-bit) architecture. + vccarchX64 = "x64", ## VCC for compilation against the x64 architecture. + vccarchX64X86 = "x64_x86", ## VCC cross-compilation tools using x64 VCC for compilation against the x86 architecture. + vccarchX64Arm = "x64_arm", ## VCC cross-compilation tools using x64 VCC for compilation against the ARM architecture. + vccarchX64Arm64 = "x64_arm64" ## VCC cross-compilation tools using x64 VCC for compilation against the ARM (64-bit) architecture. - VccPlatformType* = enum - vccplatEmpty = "", - vccplatStore = "store", - vccplatUWP = "uwp", - vccplatOneCore = "onecore" + VccPlatformType* = enum ## The VCC platform type of the compile target + vccplatEmpty = "", ## Default (i.e. Desktop) Platfor Type + vccplatStore = "store", ## Windows Store Application + vccplatUWP = "uwp", ## Universal Windows Platform (UWP) Application + vccplatOneCore = "onecore" # Undocumented platform type in the Windows SDK, probably XBox One SDK platform type. proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type: VccPlatformType = vccplatEmpty, sdk_version: string = nil, verbose: bool = false): StringTableRef = + ## Returns a string table containing the proper process environment to successfully execute VCC compile commands for the specified SDK version, CPU architecture and platform type. + ## + ## path + ## The path to the vcvarsall utility for VCC compiler backend. + ## arch + ## The compile target CPU architecture. Starting with Visual Studio 2017, this value must be specified and must not be set to `vccarchUnspecified`. + ## platform_type + ## The compile target Platform Type. Defaults to the Windows Desktop platform, i.e. a regular Windows executable binary. + ## sdk_version + ## The Windows SDK version to use. + ## verbose + ## Echo the command-line passed on to the system to load the VCC environment. Defaults to `false`. + var vccvarsallpath = path # Assume that default executable is in current directory or in PATH if path == nil or path.len < 1: @@ -58,12 +78,19 @@ proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type if comSpecCmd.len < 1: comSpecCmd = "cmd" + # Run the Windows Command Prompt with the /C argument + # Execute vcvarsall with its command-line arguments + # and then execute the SET command to list all environment variables let comSpecExec = "\"$1\" /C \"$2 && SET\"" % [comSpecCmd, vcvarsExec] var comSpecOpts = {poEvalCommand, poDemon, poStdErrToStdOut} if verbose: comSpecOpts.incl poEchoCmd let comSpecOut = execProcess(comSpecExec, options = comSpecOpts) + result = newStringTable(modeCaseInsensitive) + + # Parse the output of the final SET command to construct a String Table + # with the appropiate environment variables for line in comSpecOut.splitLines: let idx = line.find('=') if idx > 0: From 618a15ed48b92ddb40bd816c2f0589fa3ed93a2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Tue, 4 Apr 2017 19:26:07 +0200 Subject: [PATCH 17/18] Moved vccdiscover to vccexe --- tools/vccexe/vccdiscover.nim | 98 ------------------------------------ tools/vccexe/vccexe.nim | 76 ++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 108 deletions(-) delete mode 100644 tools/vccexe/vccdiscover.nim diff --git a/tools/vccexe/vccdiscover.nim b/tools/vccexe/vccdiscover.nim deleted file mode 100644 index f20bf24418..0000000000 --- a/tools/vccexe/vccdiscover.nim +++ /dev/null @@ -1,98 +0,0 @@ -## VCC compiler backend discovery Utility -## -## Module to discover the path to the vcvarsall utility of a VCC compiler backend. -## The module supports discovery for either the latest recognizable version (default) -## or discovery of a specific VCC compiler backend version. -## -## This module can also produce a standalone command-line executable. -## It supports a `--help` command-line argument. Refer to its output for further -## documentation on the `vccdiscover` standalone command-line application. - -import strutils, os, vccenv - -type - VccVersion* = enum ## VCC compiler backend versions - vccUndefined = (0, ""), ## VCC version undefined, resolves to the latest recognizable VCC version - vcc90 = vs90, ## Visual Studio 2008 (Version 9.0) - vcc100 = vs100, ## Visual Studio 2010 (Version 10.0) - vcc110 = vs110, ## Visual Studio 2012 (Version 11.0) - vcc120 = vs120, ## Visual Studio 2013 (Version 12.0) - vcc140 = vs140 ## Visual Studio 2015 (Version 14.0) - -proc discoverVccVcVarsAllPath*(version: VccVersion = vccUndefined): string = - ## Returns the path to the vcvarsall utility of the specified VCC compiler backend. - ## - ## version - ## The specific version of the VCC compiler backend to discover. - ## Defaults to the latest recognized VCC compiler backend that is found on the system. - ## - ## Returns `nil` if the VCC compiler backend discovery failed. - - # TODO: Attempt discovery using vswhere utility. - - # Attempt discovery through VccEnv - # (Trying Visual Studio Common Tools Environment Variables) - result = vccEnvVcVarsAllPath(cast[VccEnvVersion](version)) - if result.len > 0: - return - - # All attempts to dicover vcc failed - -when isMainModule: - const - helpText = """ -+-----------------------------------------------------------------+ -| Microsoft C/C++ Compiler Discovery Utility | -| (c) 2017 Fredrik Hoeisaether Rasch | -+-----------------------------------------------------------------+ - -Discovers the path to the Developer Command Prompt for the -specified versions, or attempts to discover the latest installed -version if no specific version is requested. - -Usage: - vccdiscover [...] -Arguments: - Optionally specify the version to discover - Valid values: 0, 90, 100, 110, 120, 140 - A value of 0 will discover the latest installed SDK - -For each specified version the utility prints a line with the -following format: -: -""" - var quitValue = 0 - let args = commandLineParams() - if args.len < 1: - let path = discoverVccVcVarsAllPath() - if path.len < 1: - echo "latest: VCC installation discovery failed." - quitValue = 1 - else: - echo "latest: " & path - - for argv in args: - # Strip leading hyphens or slashes, if someone tries -?, /?, --help or /help - if argv.len < 1: - continue - var argvValue = argv - while argvValue[0] == '-' or argvValue[0] == '/': - argvValue = argvValue.substr(1) - if argvValue.len < 1: - continue - - if cmpIgnoreCase(argvValue, "help") == 0 or cmpIgnoreCase(argvValue, "?") == 0: - echo helpText - continue - - let version = cast[VccVersion](parseInt(argvValue)) - let path = discoverVccVcVarsAllPath(version) - var head = $version - if head.len < 1: - head = "latest" - if path.len < 1: - echo head & ": VCC installation discovery failed." - inc quitValue - else: - echo "$1: $2" % [head, path] - quit quitValue \ No newline at end of file diff --git a/tools/vccexe/vccexe.nim b/tools/vccexe/vccexe.nim index 86ad86d977..891b4fb2df 100644 --- a/tools/vccexe/vccexe.nim +++ b/tools/vccexe/vccexe.nim @@ -1,9 +1,39 @@ -import strutils, strtabs, os, osproc, vcvarsall, vccdiscover +import strutils, strtabs, os, osproc, vcvarsall, vccenv + +type + VccVersion* = enum ## VCC compiler backend versions + vccUndefined = (0, ""), ## VCC version undefined, resolves to the latest recognizable VCC version + vcc90 = vs90, ## Visual Studio 2008 (Version 9.0) + vcc100 = vs100, ## Visual Studio 2010 (Version 10.0) + vcc110 = vs110, ## Visual Studio 2012 (Version 11.0) + vcc120 = vs120, ## Visual Studio 2013 (Version 12.0) + vcc140 = vs140 ## Visual Studio 2015 (Version 14.0) + +proc discoverVccVcVarsAllPath*(version: VccVersion = vccUndefined): string = + ## Returns the path to the vcvarsall utility of the specified VCC compiler backend. + ## + ## version + ## The specific version of the VCC compiler backend to discover. + ## Defaults to the latest recognized VCC compiler backend that is found on the system. + ## + ## Returns `nil` if the VCC compiler backend discovery failed. + + # TODO: Attempt discovery using vswhere utility. + + # Attempt discovery through VccEnv + # (Trying Visual Studio Common Tools Environment Variables) + result = vccEnvVcVarsAllPath(cast[VccEnvVersion](version)) + if result.len > 0: + return + + # All attempts to dicover vcc failed const vccversionPrefix = "--vccversion" + printPathPrefix = "--printPath" vcvarsallPrefix = "--vcvarsall" commandPrefix = "--command" + noCommandPrefix = "--noCommand" platformPrefix = "--platform" sdktypePrefix = "--sdktype" sdkversionPrefix = "--sdkversion" @@ -19,6 +49,8 @@ const HelpText = """ +-----------------------------------------------------------------+ | Microsoft C/C++ compiler wrapper for Nim | +| & | +| Microsoft C/C++ Compiler Discovery Utility | | (c) 2017 Fredrik Hoeisaether Rasch | +-----------------------------------------------------------------+ @@ -27,7 +59,17 @@ Usage: Options: --vccversion: Optionally specify the VCC version to discover : 0, 90, 100, 110, 120, 140 - Argument value is passed on to vccdiscover utility + If is omitted, attempts to discover the latest + installed version. : 0, 90, 100, 110, 120, 140 + A value of 0 will discover the latest installed SDK + Multiple values can be specified, separated by , + --printPath Print the discovered path of the vcvarsall utility + of the VCC version specified with the --vccversion argument. + For each specified version the utility prints a line with the + following format: : + --noCommand Flag to supress VCC secondary command execution + Useful in conjuction with --vccversion and --printPath to + only perfom VCC discovery, but without executing VCC tools --vcvarsall: Path to the Developer Command Prompt utility vcvarsall.bat that selects the appropiate devlopment settings. Usual path for Visual Studio 2015 and below: @@ -58,8 +100,10 @@ command was specified when isMainModule: var vccversionArg: seq[string] = @[] + var printPathArg: bool = false var vcvarsallArg: string = nil var commandArg: string = nil + var noCommandArg: bool = false var platformArg: VccArch var sdkTypeArg: VccPlatformType var sdkVersionArg: string = nil @@ -77,10 +121,14 @@ when isMainModule: # Check whether the current argument contains -- prefix if wargv.startsWith(vccversionPrefix): # Check for vccversion vccversionArg.add(wargv.substr(vccversionSepIdx + 1)) + elif wargv.cmpIgnoreCase(printPathPrefix) == 0: # Check for printPath + printPathArg = true elif wargv.startsWith(vcvarsallPrefix): # Check for vcvarsall vcvarsallArg = wargv.substr(vcvarsallSepIdx + 1) elif wargv.startsWith(commandPrefix): # Check for command commandArg = wargv.substr(commandSepIdx + 1) + elif wargv.cmpIgnoreCase(noCommandPrefix) == 0: # Check for noCommand + noCommandArg = true elif wargv.startsWith(platformPrefix): # Check for platform platformArg = parseEnum[VccArch](wargv.substr(platformSepIdx + 1)) elif wargv.startsWith(sdktypePrefix): # Check for sdktype @@ -96,8 +144,8 @@ when isMainModule: # Support for multiple specified versions. Attempt VCC discovery for each version # specified, first successful discovery wins + var vccversionValue: VccVersion = vccUndefined for vccversionItem in vccversionArg: - var vccversionValue: VccVersion try: vccversionValue = cast[VccVersion](parseInt(vccversionItem)) except ValueError: @@ -107,8 +155,15 @@ when isMainModule: break # VCC version not specified, discover latest (call discover without args) if vcvarsallArg.len < 1 and vccversionArg.len < 1: + vccversionValue = vccUndefined vcvarsallArg = discoverVccVcVarsAllPath() + if printPathArg: + var head = $vccversionValue + if head.len < 1: + head = "latest" + echo "$1: $2" % [head, vcvarsallArg] + # Call vcvarsall to get the appropiate VCC process environment var vcvars = vccVarsAll(vcvarsallArg, platformArg, sdkTypeArg, sdkVersionArg, verboseArg) if vcvars != nil: @@ -123,10 +178,11 @@ when isMainModule: if commandArg.len < 1: commandArg = "cl.exe" - # Run VCC command with the VCC process environment - let vccProcess = startProcess( - commandArg, - args = clArgs, - options = vccOptions - ) - quit vccProcess.waitForExit() + if not noCommandArg: + # Run VCC command with the VCC process environment + let vccProcess = startProcess( + commandArg, + args = clArgs, + options = vccOptions + ) + quit vccProcess.waitForExit() From caeb6110e6742e01a4daf07ed45752b5466f5d1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Tue, 4 Apr 2017 19:26:59 +0200 Subject: [PATCH 18/18] VCCEXE: lower case const identifier --- tools/vccexe/vccexe.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/vccexe/vccexe.nim b/tools/vccexe/vccexe.nim index 891b4fb2df..2dcff8ce4d 100644 --- a/tools/vccexe/vccexe.nim +++ b/tools/vccexe/vccexe.nim @@ -46,7 +46,7 @@ const sdktypeSepIdx = sdktypePrefix.len sdkversionSepIdx = sdkversionPrefix.len - HelpText = """ + helpText = """ +-----------------------------------------------------------------+ | Microsoft C/C++ compiler wrapper for Nim | | & | @@ -139,7 +139,7 @@ when isMainModule: verboseArg = true else: # Regular cl.exe argument -> store for final cl.exe invocation if (wargv.len == 2) and (wargv[1] == '?'): - echo HelpText + echo helpText clArgs.add(wargv) # Support for multiple specified versions. Attempt VCC discovery for each version