Visual Studio Power Environment

It is sometimes interesting to speculate about how unusual feature patterns in Microsoft projects may reflect the traditions or prejudices of the teams involved with creating and evolving those projects. Today's example is "%VS140COMNTOOLS%"..\..\vc\bin\vcvars32.bat, that wily little batch script that mucks your command environment enough to enable most Visual Studio declarative-like thingies to happen (easily) on the command prompt.

So PowerShell has been around for like 17 years now (if you include the Monad proto period). That's enough time for an effective rewrite or two of the Visual Studio codebase and yet we're still stuck with this inconvenient script written in possibly the worst shell still in popular use today. How can we get it to work with possibly the best shell in popular use today? Unfortunately there is no single command that will dump the environment of a cmd subprocess into the parent powershell environment. I found several hacks online proposing to do this, the most correct and elegant being Invoke-Environment.

Note

[Digression] reStructureText's link formatting is egregiously inferior.

What I really wanted was some Groovy code that will dynamically modify the environment of a declarative pipeline with the results of vcvars32.bat. Jenkins pipeline allows for setting environment variables dynamically, but the dynamism packaged into this feature only allows for arbitrary values for a set of a priori variable names. The vcvars32.bat script sets an indeterminate set of variable names. There may still be a way to do this, but my Groovy-fu does not show me how to edit the env object at sufficient introspection.

Here is the essential functionality of Invoke-Environment applied to vcvars32.bat. This is what I used to prime the PowerShell environment for Visual Studio work by pasting the code block directly into the powershell() step before executing Visual Studio commands. This is not the most elegant, but it is only a few more lines than directly sourcing the batch script and seems to be the plainest way to absorb its environment.

foreach ($_ in cmd /c "`"%VS140COMNTOOLS%`"..\\..\\vc\\bin\\vcvars32.bat > nul 2>&1 & SET") {
    if ($_ -match '^([^=]+)=(.*)') {
        [System.Environment]::SetEnvironmentVariable($matches[1], $matches[2])
    }
}

Note

If you are using this in a Jenkins pipeline, all $'s will need to be escaped, \$, to signify that they signal PowerShell variables and not Groovy variables. Also ` is PowerShell's escape character, so `" is an escaped quote.