I am using EWDK and msbuild to build some drivers.
I have a build.cmd file that works.
call <EWDK root>/BuildEnv\SetupBuildEnv.cmd
cd <working directory>
msbuild /t:build /p:...
Calling this file from PowerShell works
& build.cmd
I want to write it natively in PowerShell so I won't have a mix of languages.
I tried running it with PowerShell
& <EWDK root>/BuildEnv\SetupBuildEnv.cmd
& msbuild /t:build /p:...
And the build failed because none of the environment variables set in SetupBuildEnv.cmd
were kept in the PowerShell session that called the script.
I can't re-write SetupBuildEnv.cmd
as it comes with the SDK package so this is the only .cmd script which PowerShell should call.
I'd like to have msbuild.exe called directly in PowerShell.
Is there anyway to make this work?
CodePudding user response:
Batch/cmd scripts called from PowerShell run in a new process (cmd.exe). Environment variables set by a batch script (using the set
command) don't persist when the cmd.exe process ends, so the PowerShell parent process can't access them.
As a workaround, create a helper batch script that calls SetupBuildEnv.cmd
and then outputs the current values of all environment variables. This output can be captured by the PowerShell script which can then set the environment variables of the PowerShell process accordingly, so msbuild
called from PowerShell will inherit them.
The helper script doesn't need to be a separate file, it can be a cmd.exe /c
one-liner, as in the following code sample:
$envBlockFound = $false
cmd /c 'call "<EWDK root>\BuildEnv\SetupBuildEnv.cmd" & echo ###EnvVars### & set' | ForEach-Object {
if( $envBlockFound ) {
$name, $value = $_ -split '='
Set-Item env:$name $value
}
elseif( $_.StartsWith('###EnvVars###') ) {
$envBlockFound = $true
}
}
# uncomment to verify env vars
# Get-Item env:
msbuild /t:build /p:...
- The
cmd /c …
line breaks down to:call "<EWDK root>\BuildEnv\SetupBuildEnv.cmd"
…runs the given script and waits until it has finished.echo ###EnvVars###
…outputs a delimiter line so the PowerShell script can ignore the stdout from SetupBuildEnv. Note that the space character before the next&
ends up in the output as a trailing space, which the PowerShell script has to handle.set
…without arguments outputs all environment variables as key/value pairs separated by=
.
- Using
ForEach-Object
, we process each line ($_
) from stdout ofcmd.exe
, which also includes any stdout lines ofSetupBuildEnv.cmd
.- Ignore all lines until the delimiter line
'###EnvVars###'
is found. - When the delimiter string has been found (comparing using
.StartsWith()
instead of-eq
to ignore the trailing space), then for each line split the line on=
and set an environment variable.
- Ignore all lines until the delimiter line
- Finally call the
msbuild
process which now inherits the env vars.