I have begun working directly with the MSBuild API in order to extend our build process and increase the flexibility.
I have successfully automated the entire process of building our project, but not without a few "gotchas". Due to the extreme lack of documentation and usage examples on the MSBuild API I was forced to endure some lengthy debugging sessions.
Hopefully the details I outline here will help others looking to integrate directly with the MSBuild API.
Compiling C# code was extremely easy, but once I tried to use the MSBuild API to compile some of our legacy VC++ projects, the process really hit a snag.
Here is a code sample of my compiler wrapper:
globalProperties["Configuration"] = CommonLibrary.BuildMode.Release.ToString();
//add the compiler variables from the specific project to the properties dictionary
if (compilerVariables != null)
{
foreach(var kvp in compilerVariables)
{
globalProperties.Add(kvp.Key, kvp.Value);
}
}
//set up a build request to be sent to MSBuild
var request = new BuildRequestData(project.SolutionFile.FullName, globalProperties, null, new string[] { "Rebuild" }, null);
//configure settings for MSBuild specifically
var buildParams = new BuildParameters();
buildParams.EnableNodeReuse = true;
buildParams.Loggers = new List<Microsoft.Build.Framework.ILogger>()
{
buildLogger,
fileLogger
};
//instantiate the BuildManager, kick off the build, and get the results
BuildManager buildManager = new BuildManager();
project.BuildResult = buildManager.Build(buildParams, request);
Another aspect of this process involves the .props file that VC++ uses in order to get the INCLUDE (and other paths) needed for the linker. This .props file lives in C:\Users\%BUILDACCOUNT%\AppData\Local\Microsoft\MSBuild\v4.0\Microsoft.Cpp.%TargetPlatform%.user.props
Now comes the interesting part.
When I run this code against a C# project/solution, everything works fine. That is because the .props file plays no part in C# compilation (Include paths are stored as the .csproj level).
However, when running against a solution that contains VC++ projects this method falls completely on it's face. The reason is because it is unable to resolve the INCLUDE paths that are presumably located within the props file (at the location detailed above).
Another interesting tid-bit is that I can replicate the arguments to MSBuild directly from the command line, and the build succeeds fine. This really puzzled me... so I turned on the verbose diagnostics and ran my test-case again.
Interesting information from logs (logs have been trimmed):
- Building from the command line
USERDOMAIN = [REDACTED]
USERNAME = MyUserName
LOCALAPPDATA = C:\Users\MyUserName\AppData\Local
- Building from the API
USERDOMAIN = [REDACTED]
USERNAME = BUILDMACHINE$
LOCALAPPDATA = C:\Windows\system32\config\systemprofile\AppData\Local
As you can see, it seems that the current identity when building from the CLI is correctly set to my identity. However, building from the API sets the current identity to the local system. This leads to the INCLUDE path not being correctly set for API builds, because the props file with that information is not available at the expected location (C:\Windows\system32\config\systemprofile\AppData\Local).
My workaround which seems to be working so far is to actually move all the props files from the AppData Location to the directory that the API is expecting it in. I do not think that this is the expected behavior... it doesn't seem very intelligible.
Additional notes:
- This code is being hosted on IIS, within an application pool running as "MyUserName" (not the local system).
- I have been able to reproduce this issue on two different machines both running Windows Server 2008
Either I am doing something incorrectly, or the MSBuild API is doing something incorrectly? Any input as to what the official means of getting the props file recognized by the MSBuild API would be extremely helpful.
Thanks.