summary refs log tree commit diff stats
path: root/tools/vccexe/vccexe.nim
diff options
context:
space:
mode:
Diffstat (limited to 'tools/vccexe/vccexe.nim')
-rw-r--r--tools/vccexe/vccexe.nim188
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()