/// <summary> /// Saves the project once the imports have been evaluated /// </summary> private void SavePostEvaluationProject(ProjectInstance projectInstance) { string postBuildProject = projectInstance.FullPath + ".postbuild.proj"; projectInstance.ToProjectRootElement().Save(postBuildProject); this.TestContext.AddResultFile(postBuildProject); }
//===================================================================== /// <summary> /// This is used to execute the task and perform the build /// </summary> /// <returns>True on success or false on failure.</returns> public override bool Execute() { Project msBuildProject = null; ProjectInstance projectInstance = null; bool removeProjectWhenDisposed = false; string line; // If canceled already, just return if (buildCancelled) { return(false); } try { if (!this.AlwaysLoadProject) { // Use the current project if possible. This is preferable as we can make use // of command line property overrides and any other user-defined properties. this.ProjectFile = Path.GetFullPath(this.ProjectFile); // This collection is new to the MSBuild 4.0 API. If you load a project, it appears // in the collection. However, if you run MSBuild.exe, it doesn't put the project in // this collection. As such, we must still resort to reflection to get the executing // project. I'm leaving this here in case that ever changes as this is preferable to // using Reflection to get at the current project. var matchingProjects = ProjectCollection.GlobalProjectCollection.GetLoadedProjects( this.ProjectFile); if (matchingProjects.Count != 0) { if (matchingProjects.Count != 1) { Log.LogWarning(null, "BHT0004", "BHT0004", "SHFB", 0, 0, 0, 0, "Multiple matching " + "projects were found. Only the first one found will be built."); } msBuildProject = matchingProjects.First(); } else { projectInstance = this.GetCurrentProjectInstance(); } } } catch (Exception ex) { // Ignore exceptions but issue a warning and fall back to using // the passed project filename instead. Log.LogWarning(null, "BHT0001", "BHT0001", "SHFB", 0, 0, 0, 0, "Unable to get executing " + "project: {0}. The specified project will be loaded but command line property " + "overrides will be ignored.", ex.Message); } try { if (msBuildProject == null) { removeProjectWhenDisposed = true; if (projectInstance != null) { msBuildProject = new Project(projectInstance.ToProjectRootElement()); // ToProjectRootElement() will not add properties in the global collection to the // project. One problem with this is that command line overrides get missed. As such, // we'll add them back to the project as long as they are not reserved names and are not // there already. foreach (var p in projectInstance.GlobalProperties) { if (!SandcastleProject.RestrictedProperties.Contains(p.Key) && !msBuildProject.AllEvaluatedProperties.Any(ep => ep.Name == p.Key)) { msBuildProject.SetProperty(p.Key, p.Value); } } msBuildProject.FullPath = this.ProjectFile; } else { if (!File.Exists(this.ProjectFile)) { throw new BuilderException("BHT0003", "The specified project file does not exist: " + this.ProjectFile); } Log.LogWarning(null, "BHT0001", "BHT0001", "SHFB", 0, 0, 0, 0, "Unable to get " + "executing project: Unable to obtain matching project from the global " + "collection. The specified project will be loaded but command line property " + "overrides will be ignored."); // Create the project and set the configuration and platform options msBuildProject = new Project(this.ProjectFile); } msBuildProject.SetGlobalProperty(BuildItemMetadata.Configuration, this.Configuration); msBuildProject.SetGlobalProperty(BuildItemMetadata.Platform, this.Platform); // Override the OutDir property if defined for Team Build. Ignore ".\" as that's our default. if (!String.IsNullOrEmpty(this.OutDir) && this.OutDir != @".\") { msBuildProject.SetGlobalProperty(BuildItemMetadata.OutDir, this.OutDir); } msBuildProject.ReevaluateIfNecessary(); } // initialize properties that where provided in Properties if (!String.IsNullOrWhiteSpace(Properties)) { foreach (string propertyKeyValue in this.Properties.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { #if NET472_OR_GREATER int length = propertyKeyValue.IndexOf('='); #else int length = propertyKeyValue.IndexOf('=', StringComparison.Ordinal); #endif if (length != -1) { string propertyKey = propertyKeyValue.Substring(0, length).Trim(); string propertyValue = propertyKeyValue.Substring(length + 1).Trim(); if (!String.IsNullOrWhiteSpace(propertyKey)) { Log.LogMessage(MessageImportance.Low, "Setting property {0}={1}", propertyKey, propertyValue); msBuildProject.SetGlobalProperty(propertyKey, propertyValue); } } } msBuildProject.ReevaluateIfNecessary(); } cts = new CancellationTokenSource(); // Associate the MSBuild project with a SHFB project instance and build it using (sandcastleProject = new SandcastleProject(msBuildProject)) { buildProcess = new BuildProcess(sandcastleProject) { ProgressReportProvider = this, CancellationToken = cts.Token }; // Since this is an MSBuild task, we'll run it directly rather than in a background thread Log.LogMessage(MessageImportance.High, "Building {0}", msBuildProject.FullPath); // When ran with dotnet.exe, sometimes it can't find the core dependencies. The resolver // fixes that. using (var resolver = new ComponentAssemblyResolver()) { buildProcess.Build(); } lastBuildStep = buildProcess.CurrentBuildStep; } } catch (Exception ex) { Log.LogError(null, "BHT0002", "BHT0002", "SHFB", 0, 0, 0, 0, "Unable to build project '{0}': {1}", msBuildProject.FullPath, ex); } finally { if (cts != null) { cts.Dispose(); cts = null; } // If we loaded it, we must unload it. If not, it is cached and may cause problems later. if (removeProjectWhenDisposed && msBuildProject != null) { ProjectCollection.GlobalProjectCollection.UnloadProject(msBuildProject); ProjectCollection.GlobalProjectCollection.UnloadProject(msBuildProject.Xml); } } if (this.DumpLogOnFailure && lastBuildStep == BuildStep.Failed) { using (StreamReader sr = new StreamReader(buildProcess.LogFilename)) { Log.LogMessage(MessageImportance.High, "Log Content:"); do { line = sr.ReadLine(); // Don't output the XML elements, just the text if (line != null && (line.Trim().Length == 0 || line.Trim()[0] != '<')) { Log.LogMessage(MessageImportance.High, line); } } while(line != null); } } return(lastBuildStep == BuildStep.Completed); }