Running MSBuild programmatically
I would recommend stronlgy to go the official route via classes/interfaces in Microsoft.Build
namespace. Microsoft uses this all over the place, so this should count for something...
Esp. the class Microsoft.Build.Execution.BuildManager
and the Singleton Microsoft.Build.Execution.BuildManager.DefaultBuildManager
is what you are after to run a build task... source code examples:
- http://social.msdn.microsoft.com/Forums/en-US/msbuild/thread/ec95c513-f972-45ad-b108-5fcfd27f39bc/
- Logging Build messages with MSBuild 4.0
Building msbuild 15 project programmatically
I had similar needs for my team and I wrote a Builder library for C# that supports several versions of Visual Studio.
I could not make the Project.Build function work properly, so I went for executing MsBuild.exe directly.
How I built it:
Use Microsoft.Build.Framework from NuGet
Create a new Project object with 1 target called Build
Define the right ToolsVersion
Of the Project according to the Visual Studio version:
- 2010, 2012 => 4.0
- 2013 => 12.0
- 2015 => 14.0
- 2017 => 15.0
Add a new task of type MsBuild
With Projects property containing all the project I need to build
Define the ToolsVersion
of the MsBuild task with same value as for the Project
Serialize the Project into a temporary file
Find the right MsBuild.exe according to the ToolsVersion
4.0, 12.0, 14.0
Found in the registry:
Registry.LocalMachine.OpenSubKey($@"SOFTWARE\Microsoft\MSBuild\ToolsVersions\{msBuildVersion}")
15.0
Not anymore in the registry, you need to use the Nuget package Microsoft.VisualStudio.Setup.Configuration.Interop
var query = new SetupConfiguration();
var query2 = (ISetupConfiguration2)query;
var e = query2.EnumAllInstances();
var helper = (ISetupHelper)query;
int fetched;
var instances = new ISetupInstance[1];
do
{
e.Next(1, instances, out fetched);
if (fetched > 0)
{
var instance = instances[0];
var instance2 = (ISetupInstance2)instance;
var state = instance2.GetState();
// Skip non-complete instance, I guess?
// Skip non-local instance, I guess?
// Skip unregistered products?
if (state != InstanceState.Complete
|| (state & InstanceState.Local) != InstanceState.Local
|| (state & InstanceState.Registered) != InstanceState.Registered)
{
continue;
}
var msBuildComponent =
instance2.GetPackages()
.FirstOrDefault(
p =>
p.GetId()
.Equals("Microsoft.Component.MSBuild",
StringComparison.InvariantCultureIgnoreCase));
if (msBuildComponent == null)
{
continue;
}
var instanceRootDirectory = instance2.GetInstallationPath();
var msbuildPathInInstance = Path.Combine(instanceRootDirectory, "MSBuild", msBuildVersion, "Bin", "msbuild.exe");
if (File.Exists(msbuildPathInInstance))
{
return msbuildPathInInstance;
}
}
} while (fetched > 0);
Execute MsBuild.exe
And build the serialized Project using a custom XML Logger - you can use the one provided by MsBuildExtensionPack
Read the result summary
Deserialize the result summary from Xml and use it to determine if build failed or not, which errors and warnings occurred etc.
how to execute msbuild.exe command using winform c#?
System.Diagnostics.Process.Start(@"C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe");
How do I build a solution programmatically in C#?
Most of the answers are providing ways to do it by calling external commands, but there is an API, Microsoft.Build.Framework, to build via C#.
Code from blog post:
using Microsoft.Build.BuildEngine;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
public class SolutionBuilder
{
BasicFileLogger b;
public SolutionBuilder() { }
[STAThread]
public string Compile(string solution_name,string logfile)
{
b = new BasicFileLogger();
b.Parameters = logfile;
b.register();
Microsoft.Build.BuildEngine.Engine.GlobalEngine.BuildEnabled = true;
Project p = new Project (Microsoft.Build.BuildEngine.Engine.GlobalEngine);
p.BuildEnabled = true;
p.Load(solution_name);
p.Build();
string output = b.getLogoutput();
output += “nt” + b.Warningcount + ” Warnings. “;
output += “nt” + b.Errorcount + ” Errors. “;
b.Shutdown();
return output;
}
}
// The above class is used and compilation is initiated by the following code,
static void Main(string[] args)
{
SolutionBuilder builder = new SolutionBuilder();
string output = builder.Compile(@”G:CodesTestingTesting2web1.sln”, @”G:CodesTestingTesting2build_log.txt”);
Console.WriteLine(output);
Console.ReadKey();
}
Note the code in that blog post works, but it is a little dated. The
Microsoft.Build.BuildEngine
has been broken up into some pieces.
Microsoft.Build.Construction
Microsoft.Build.Evaluation
Microsoft.Build.Execution
How can I use an SLN file with MSBuild programmatically from C#?
I figured it out after more searching. Finding good examples online is a little difficult because of the two different versions of the MSBuild API, and the popularity of just running MSBuild from the command line.
Here is the code that is now working for me, using the newer MSBuild API:
var pc = new ProjectCollection();
var parameters = new BuildParameters(pc)
{
Loggers = new[] { _logger } //Instance of ILogger instantiated earlier
};
var request = new BuildRequestData(
projectFullPath: pathToMySlnFile, //Solution file path
globalProperties: myPropertyDictionary,
toolsVersion: null,
targetsToBuild: myTargetsArray,
hostServices: null,
flags: BuildRequestDataFlags.ProvideProjectStateAfterBuild);
var buildResult = BuildManager.DefaultBuildManager.Build(parameters, request);
How to call MSBuild from C#
Yes, add a reference to Microsoft.Build.Engine
and use the Engine class.
PS: Take care to reference the right version. There are 2.0 and 3.5 assemblies and you'll have to make sure that everyone gets the right one.
Delphi 11: Find correct toolsversion for MSBuild programmatically
Assuming that rsvars.bat
has been run,
FOR %%v IN ("%frameworkdir%") DO SET "toolsversion=%%~nv"
ECHO msbuild ...blah... /toolsversion:%toolsversion:~1%
The variable frameworkdir
will be set by rsvars.bat
. The for
command parses its value (eg. C:\Windows\Microsoft.NET\Framework\v4.0.30319
as though it is a filename, and picks v4.0
as the "filename" (~n
modifier [filename] of the metavariable %%v
)
Then use the value assigned to toolsversion
, starting at character 1 (where the first character is "character 0")
--- Given more info in comment
FOR %%v IN ("%frameworkdir%") DO ECHO %%~nxv|FINDSTR /R ".*\..*\.">nul&IF ERRORLEVEL 1 (SET "toolsversion=%%~nxv") ELSE SET "toolsversion=%%~nv"
Oh ye of little faith :)
Related Topics
Setting Unique Constraint with Fluent API
Mvvm in Wpf - How to Alert Viewmodel of Changes in Model... or Should I
Convert Linq Query Result to Dictionary
How to Fix Error: "Could Not Find Schema Information for the Attribute/Element" by Creating Schema
Auto Create Database in Entity Framework Core
Jcontainer, Jobject, Jtoken and Linq Confusion
Checking If an Object Is Null in C#
How to Programmatically Click a Button in Wpf
Entity Framework 6: Audit/Track Changes
Simple Animation Using C#/Windows Forms
.Net Timezoneinfo from Olson Time Zone
MVC Web API: No 'Access-Control-Allow-Origin' Header Is Present on the Requested Resource
Put Wpf Control into a Windows Forms Form