views:

2923

answers:

4

I am considering running MSBuild from a Powershell script by tapping directly to the MSBuild assemblies (as opposed to looking up MSBuild install path and starting msbuild.exe as a child process).

Has anyone done this? What would be the simplest, most straightforward way to run the build? Are there any pros/cons to either technique you'd like to point out? (I'm especially interested in any issues that might arise from running msbuild in the same process/appdomain as the rest of the script).

Currently my thinking is something along these lines:

[void][System.Reflection.Assembly]::Load('Microsoft.Build.Engine, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a')
[void][Microsoft.Build.BuildEngine.Engine]::GlobalEngine.BuildProjectFile("path/main.proj")
+2  A: 

A different and potentially more usable approach would be to make an msbuild cmdlet. MsBuild has a nice API and there are many samples out there on how to use it from a compiled language such as C#/VB. It would be very easy to build a cmdlet that would provide a much nicer syntax to your powershell scripts.

JaredPar
Well that's my question - do you have any links to "the many samples on how to use it" or provide your own?
Milan Gardian
+2  A: 

I would very highly suggest looking at PSake.

Let me quote a portion of that page:

Remember that psake is syntactic sugar around PowerShell. So anything you can do in PowerShell, you can do in psake. That means that you can run MSBuild, NAnt, or other scripts. There is no need to completely replace your current build system. You can use psake to automate and extend it!

psake automatically adds the appropriate version of .NET Framework to its path. So you can access MSBuild, csc.exe, vbc.exe, or any other tools installed in $env:windir\Microsoft.NET\Framework\$version\ without the fully qualified path.

EBGreen
Please, let's not go into philosophical debate on 'msbuild sucks, use powershell-based build instead' as that was not my question, really.
Milan Gardian
What philosophical debate are you talking about? Did you read the page? It provides a simple way to wrap MSBuild.
EBGreen
Yes, I did read the page. And it's describing this rake-like "no XML brackets tax" build tool. I did use rake before I switched to msbuild and I'm not going back :-). The mechanism psake's using to invoke msbuild is that it adds it to 'path' - i.e. external process. That's exactly what I don't want.
Milan Gardian
That's fine then. Just wanted to be sure it wasn't a reflex reaction.
EBGreen
I'm sorry, I was too harsh. I'm just frustrated because I can't find a decent description on how to use the latest (3.5) BuildEngine.Engine class (google, SO, MSDN). MSDN info is obsolete; it's using methods that have been obsoleted in 3.5 impl of Engine class (I used Reflector to investigate).
Milan Gardian
I would suggest contacting James directly to see if he could help. He seems very knowledgeable on the matter. I have not used the class directly myself.
EBGreen
+1  A: 

I was looking for this same thing. Following JaredPar's lead I found the following:

This is a how-to on making a cmdlet.

http://bartdesmet.net/blogs/bart/archive/2008/02/03/easy-windows-powershell-cmdlet-development-and-debugging.aspx

The MSBuild API is part of these namespaces:

Microsoft.Build.Framework

Microsoft.Build.BuildEngine

And the MSBuild documentation can be found here (this is more for completeness than in response to your question):

http://msdn.microsoft.com/en-us/library/wea2sca5.aspx

Anthony Potts
Thank you Anthony, but moving the code from pure powershell to cmdlet is really just performance/convenience optimization. I am interested in steps/code as well as gotchas for using the Microsoft.Build.BuildEngine directly. I can move the actual impl between ps1 and cmdlet fairly easily.
Milan Gardian
I have msbuild scripts in many different places, and I was wanting to be able to run something from one central place. If you end up with a working answer, I'd love to see a blog post or something.
Anthony Potts
+6  A: 

The simplest embedded-build invocation that worked and produced output was:

[void][System.Reflection.Assembly]::Load('Microsoft.Build.Engine, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a')
$engine = New-Object Microsoft.Build.BuildEngine.Engine
$engine.RegisterLogger((New-Object Microsoft.Build.BuildEngine.ConsoleLogger))
$engine.BuildProjectFile('fullPath\some.proj')

However, it turns out embedding MSBuild directly in Powershell (V1) is problematic:

'MSBUILD : warning MSB4056: The MSBuild engine must be called on
a single-threaded-apartment. Current threading model is "MTA".
Proceeding, but some tasks may not function correctly.'

Why oh why are we still paying COM tax in 2009 while working in a managed environment?

My conclusion is that embedding MSBuild in Powershell (V1) is not a good idea. For reference, I'm also including the process-based approach I ended up using:

[void][System.Reflection.Assembly]::Load('Microsoft.Build.Utilities.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a')
$msbuild = [Microsoft.Build.Utilities.ToolLocationHelper]::GetPathToDotNetFrameworkFile("msbuild.exe", "VersionLatest")
&$msbuild fullPath\some.proj
Milan Gardian