// Retrieves a property from an MSBuild project file by executing MSBuild and getting MSBuild to print the property. // We do this by creating a custom MSBuild file which: // * Will import the project file we're inspecting // * Has a target that will print a given property // and then executing MSBuild on this custom MSBuild file. public static async Task <string> GetPropertyByMSBuildEvaluationAsync(this XmlDocument csproj, ILog log, IProcessManager processManager, string projectPath, string evaluateProperty, string dependsOnTargets = "", Dictionary <string, string> properties = null) { var xml = @"<Project DefaultTargets='WriteProperty' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> <!-- Import the project we want to inspect --> <Import Project='$(ProjectFile)' Condition=""'$(ProjectFile)' != ''"" /> <!-- Target to write out the property we want --> <Target Name='WriteProperty' DependsOnTargets='%DEPENDSONTARGETS%'> <PropertyGroup> <_Properties>$(%PROPERTY%)</_Properties> </PropertyGroup> <Error Text='The ProjectFile variable must be set.' Condition=""'$(ProjectFile)' == ''"" /> <Error Text='The OutputFile variable must be set.' Condition=""'$(OutputFile)' == ''"" /> <WriteLinesToFile File='$(OutputFile)' Lines='$(_Properties)' Overwrite='true' /> </Target> </Project> "; var dir = Path.GetDirectoryName(projectPath); var inspector = Path.Combine(dir, "PropertyInspector.csproj"); var output = Path.Combine(dir, "PropertyInspector.txt"); try { File.WriteAllText(inspector, xml.Replace("%PROPERTY%", evaluateProperty).Replace("%DEPENDSONTARGETS%", dependsOnTargets)); using (var proc = new Process()) { var isDotNetProject = csproj.IsDotNetProject(); proc.StartInfo.FileName = isDotNetProject ? processManager.GetDotNetExecutable(projectPath) : processManager.MSBuildPath; var args = new List <string> (); if (isDotNetProject) { args.Add("build"); } args.Add("/p:ProjectFile=" + projectPath); args.Add("/p:OutputFile=" + output); foreach (var prop in properties) { args.Add($"/p:{prop.Key}={prop.Value}"); } args.Add(inspector); proc.StartInfo.Arguments = StringUtils.FormatArguments(args); proc.StartInfo.WorkingDirectory = dir; var rv = await processManager.RunAsync(proc, log, timeout : TimeSpan.FromSeconds(15)); if (!rv.Succeeded) { throw new Exception($"Unable to evaluate the property {evaluateProperty}."); } return(File.ReadAllText(output).Trim()); } } finally { File.Delete(inspector); File.Delete(output); } }