Last active
February 13, 2025 07:40
-
-
Save fearthecowboy/9e06ad9d92c5d939582147a35c049693 to your computer and use it in GitHub Desktop.
The definitive way to use PowerShell from an msbuild script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |
<!-- #1 Place this line at the top of any msbuild script (ie, csproj, etc) --> | |
<PropertyGroup><PowerShell># 2>nul || type %~df0|find /v "setlocal"|find /v "errorlevel"|powershell.exe -noninteractive -& exit %errorlevel% || #</PowerShell></PropertyGroup> | |
<!-- #2 in any target you want to run a script --> | |
<Target Name="default" > | |
<PropertyGroup> <!-- #3 prefix your powershell script with the $(PowerShell) variable, then code as normal! --> | |
<myscript>$(PowerShell) | |
# | |
# powershell script can do whatever you need. | |
# | |
dir ".\*.cs" -recurse |% { | |
write-host Examining file named: $_.FullName | |
# do other stuff here... | |
} | |
$answer = 2+5 | |
write-host Answer is $answer ! | |
</myscript> | |
</PropertyGroup> | |
<!-- #4 and execute the script like this --> | |
<Exec Command="$(myscript)" EchoOff="true" /> | |
<!-- | |
Notes: | |
====== | |
- You can still use the standard Exec Task features! (see: https://msdn.microsoft.com/en-us/library/x8zx72cd.aspx) | |
- if your powershell script needs to use < > or & characters, just place the contents in a CDATA wrapper: | |
<script2><![CDATA[ $(PowerShell) | |
# your powershell code goes here! | |
write-host "<<Hi mom!>>" | |
]]></script2> | |
- if you want return items to the msbuild script you can get them: | |
<script3>$(PowerShell) | |
# your powershell code goes here! | |
(dir "*.cs" -recurse).FullName | |
</script3> | |
<Exec Command="$(script3)" EchoOff="true" ConsoleToMSBuild="true"> | |
<Output TaskParameter="ConsoleOutput" PropertyName="items" /> | |
</Exec> | |
<Touch Files="$(items)" /> <- see! then you can use those items with another msbuild Task | |
--> | |
</Target> | |
</Project> |
<Target Name="Trigger" AfterTargets="Generate" >
<PropertyGroup>
<PowerShellCommand Condition=" '$(OS)' == 'Windows_NT' " >powershell</PowerShellCommand>
<PowerShellCommand Condition=" '$(OS)' == 'Unix' " >pwsh</PowerShellCommand>
<ClientGenTriggerScript>$(MSBuildProjectDirectory)\..\..\..\.build\client_gen.ps1</ClientGenTriggerScript>
</PropertyGroup>
<Exec Command="$(PowerShellCommand) -ExecutionPolicy Unrestricted -NoProfile -File $(ClientGenTriggerScript) -parameter1 p1 -parameter2 $(p2) --Verbose" />
</Target>
I use it this way.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I found this to be extremely helpful ! However, I ran through some limitations with the PowerShell command. Those limitations are:
Exec
task. e.g.<Exec Command="$(PowerShell)" Write-Host "ok"" />
& exit %errorlevel%
on the same line as the PowerShell command,0
is always returned even ifExit -1
is in the PowerShell script. In order for the PowerShell process return code to be returned to Visual Studio, theexit %errorlevel%
must be on a separate line.last powershell command ]]
(where]]
are the closing braces of the![CDATA[
tag on the same line as the last PowerShell command).%0D%0A
is not added at the end of the command. e.g.<Exec Command="$(PowerShell) $(ScriptWithFunctionRequiringParameters) "@(ItemGroupUsedAsParameter)" %0D%0A" />
Now, that being said, I used your scheme and changed the
find /v
commands with PowerShell-replace
commands which allow using regular expressions. What I came up with is:With this improved PowerShell command, I was able to fix all the limitations previously mentioned, I thought I'd share it with you since the original command found here really helped be grasp how MsBuild was actually calling PowerShell.