-
-
Save fearthecowboy/9e06ad9d92c5d939582147a35c049693 to your computer and use it in GitHub Desktop.
| <?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> |
This was a massive help! Whenever i tried running powershell scripts another way notepad just opened with the contents of the script!?!?
I must say that if this approach works (which I assume it does) then I owe you a big thank you. Being able to define the script inside the csproj itself is just pure awesomeness.
This is great - do you know how to reference other variables from within the script? For example, accessing the MyNumber property from within the script:
<PropertyGroup>
<MyNumber>2</MyNumber>
</PropertyGroup>
<PropertyGroup> <!-- #3 prefix your powershell script with the $(PowerShell) variable, then code as normal! -->
<myscript>$(PowerShell)
.....
$x = 2
If ($x -eq $MyNumber) {
.....
}
.....
</myscript>
</PropertyGroup>
did you try $(MyNumber) ? IIRC that should work
I found this to be extremely helpful ! However, I ran through some limitations with the PowerShell command. Those limitations are:
- It fails if the PowerShell script is inlined in the
Exectask. e.g.<Exec Command="$(PowerShell)" Write-Host "ok"" /> - The actual return code of PowerShell is not returned. This is a limitation of the Windows Command Line. With the
& exit %errorlevel%on the same line as the PowerShell command,0is always returned even ifExit -1is 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. - It fails if the PowerShell script declared in the property ends with
last powershell command ]](where]]are the closing braces of the![CDATA[tag on the same line as the last PowerShell command). - If the PowerShell script is a function ending with a call to that function and that function has required parameters, the PowerShell command fails if an explicit
%0D%0Ais 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:
<PropertyGroup>
<PowerShellCommand>
powershell.exe -Command "&{((Get-Content \"%~df0\" -Raw) -replace \"(?sm).*:START_POWERSHELL_SCRIPT\", \"\" -replace \"exit %%.*%%\", \"\") + \"`n\"}"|powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -
exit %ERRORLEVEL%
:START_POWERSHELL_SCRIPT
%0D%0A
</PowerShellCommand>
</PropertyGroup>
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.
<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.
This is amazing. Can you explain the value of the PowerShell property? What does the leading # mean to CMD? And how might one get something like this to work using dotnet core on a non-windows platform (to invoke PowerShell Core from bash platforms?) I have come up with the following which seems close but is giving me strange, erratic errors from PowerShell: