diff options
Diffstat (limited to 'tools/vccexe/vccexe.nim')
-rw-r--r-- | tools/vccexe/vccexe.nim | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/tools/vccexe/vccexe.nim b/tools/vccexe/vccexe.nim new file mode 100644 index 000000000..2dcff8ce4 --- /dev/null +++ b/tools/vccexe/vccexe.nim @@ -0,0 +1,188 @@ +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" + verbosePrefix = "--verbose" + + vccversionSepIdx = vccversionPrefix.len + vcvarsallSepIdx = vcvarsallPrefix.len + commandSepIdx = commandPrefix.len + platformSepIdx = platformPrefix.len + sdktypeSepIdx = sdktypePrefix.len + sdkversionSepIdx = sdkversionPrefix.len + + helpText = """ ++-----------------------------------------------------------------+ +| Microsoft C/C++ compiler wrapper for Nim | +| & | +| Microsoft C/C++ Compiler Discovery Utility | +| (c) 2017 Fredrik Hoeisaether Rasch | ++-----------------------------------------------------------------+ + +Usage: + vccexe [options] [compileroptions] +Options: + --vccversion:<v> Optionally specify the VCC version to discover + <v>: 0, 90, 100, 110, 120, 140 + If <v> is omitted, attempts to discover the latest + installed version. <v>: 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: <version>: <path> + --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> 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:<exec> Specify the command to run once the development environment is loaded. + <exec> 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:<arch> Specify the Compiler Platform Tools architecture + <arch>: x86 | amd64 | arm | x86_amd64 | x86_arm | amd64_x86 | amd64_arm + 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:<type> Specify the SDK flavor to use. Defaults to the Desktop SDK. + <type>: {empty} | store | uwp | onecore + --sdkversion:<v> Use a specific Windows SDK version: + <v> 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 +Microsoft (R) C/C++ Optimizing Compiler if no secondary +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 + var verboseArg: bool = false + + 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 + 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 + 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 + clArgs.add(wargv) + + # Support for multiple specified versions. Attempt VCC discovery for each version + # specified, first successful discovery wins + var vccversionValue: VccVersion = vccUndefined + for vccversionItem in vccversionArg: + try: + vccversionValue = cast[VccVersion](parseInt(vccversionItem)) + except ValueError: + continue + 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: + 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: + for vccEnvKey, vccEnvVal in vcvars: + putEnv(vccEnvKey, vccEnvVal) + + var vccOptions = {poParentStreams} + if verboseArg: + vccOptions.incl poEchoCmd + + # Default to the cl.exe command if no secondary command was specified + if commandArg.len < 1: + commandArg = "cl.exe" + + if not noCommandArg: + # Run VCC command with the VCC process environment + let vccProcess = startProcess( + commandArg, + args = clArgs, + options = vccOptions + ) + quit vccProcess.waitForExit() |