//===================================================================== /// <summary> /// This is used to start the background build process from which we will get the information to load the /// tree view. /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> private async void ApiFilterEditorDlg_Load(object sender, EventArgs e) { string tempPath; tvApiList.Enabled = splitContainer.Panel2.Enabled = btnReset.Enabled = false; try { // Clone the project for the build and adjust its properties for our needs tempProject = new SandcastleProject(apiFilter.Project.MSBuildProject); // Build output is stored in a temporary folder and it keeps the intermediate files tempProject.CleanIntermediates = false; tempPath = Path.GetTempFileName(); File.Delete(tempPath); tempPath = Path.Combine(Path.GetDirectoryName(tempPath), "SHFBPartialBuild"); if(!Directory.Exists(tempPath)) Directory.CreateDirectory(tempPath); tempProject.OutputPath = tempPath; cancellationTokenSource = new CancellationTokenSource(); buildProcess = new BuildProcess(tempProject, PartialBuildType.GenerateReflectionInfo) { ProgressReportProvider = new Progress<BuildProgressEventArgs>(buildProcess_ReportProgress), CancellationToken = cancellationTokenSource.Token, SuppressApiFilter = true // We must suppress the current API filter for this build }; await Task.Run(() => buildProcess.Build(), cancellationTokenSource.Token); if(!cancellationTokenSource.IsCancellationRequested) { // Restore the current project's base path Directory.SetCurrentDirectory(Path.GetDirectoryName(apiFilter.Project.Filename)); // If successful, load the namespace nodes, and enable the UI if(buildProcess.CurrentBuildStep == BuildStep.Completed) { reflectionFile = buildProcess.ReflectionInfoFilename; // Convert the build API filter to a dictionary to make it easier to find entries buildFilterEntries = new Dictionary<string, ApiFilter>(); this.ConvertApiFilter(buildProcess.CurrentProject.ApiFilter); this.LoadNamespaces(); tvApiList.Enabled = splitContainer.Panel2.Enabled = btnReset.Enabled = true; } else MessageBox.Show("Unable to build project to obtain API information. Please perform a " + "normal build to identify and correct the problem.", Constants.AppName, MessageBoxButtons.OK, MessageBoxIcon.Error); pbWait.Visible = lblLoading.Visible = false; } else { this.DialogResult = DialogResult.Cancel; this.Close(); } buildProcess = null; } catch(Exception ex) { System.Diagnostics.Debug.WriteLine(ex.ToString()); MessageBox.Show("Unable to build project to obtain API information. Error: " + ex.Message, Constants.AppName, MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { if(cancellationTokenSource != null) { cancellationTokenSource.Dispose(); cancellationTokenSource = null; } } }
/// <summary> /// Build the help file using the current project settings /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> private async void miBuildProject_Click(object sender, EventArgs e) { if(project == null || !this.SaveBeforeBuild()) return; miClearOutput_Click(miBuildProject, e); this.SetUIEnabledState(false); Application.DoEvents(); StatusBarTextProvider.InitializeProgressBar(0, (int)BuildStep.Completed, "Building help file"); try { cancellationTokenSource = new CancellationTokenSource(); buildProcess = new BuildProcess(project) { ProgressReportProvider = new Progress<BuildProgressEventArgs>(buildProcess_ReportProgress), CancellationToken = cancellationTokenSource.Token, }; await Task.Run(() => buildProcess.Build(), cancellationTokenSource.Token); } finally { if(cancellationTokenSource != null) { cancellationTokenSource.Dispose(); cancellationTokenSource = null; } StatusBarTextProvider.ResetProgressBar(); this.SetUIEnabledState(true); outputWindow.LogFile = buildProcess.LogFilename; if(buildProcess.CurrentBuildStep == BuildStep.Completed && Settings.Default.OpenHelpAfterBuild) miViewHelpFile.PerformClick(); buildProcess = null; } }
/// <summary> /// This is called to build a project /// </summary> /// <param name="project">The project to build</param> /// <param name="workingPath">The working path for the project</param> /// <returns>Returns true if successful, false if not</returns> private bool BuildProject(SandcastleProject project, string workingPath) { BuildProcess buildProcess; lastBuildStep = BuildStep.None; builder.ReportProgress("\r\nBuilding {0}", project.Filename); try { // For the plug-in, we'll override some project settings project.HtmlHelp1xCompilerPath = new FolderPath(builder.Help1CompilerFolder, true, project); project.HtmlHelp2xCompilerPath = new FolderPath(builder.Help2CompilerFolder, true, project); project.WorkingPath = new FolderPath(workingPath, true, project); project.OutputPath = new FolderPath(workingPath + @"..\PartialBuildLog\", true, project); // If the current project has defined OutDir, pass it on to the sub-project. string outDir = builder.CurrentProject.MSBuildProject.GetProperty("OutDir").EvaluatedValue; if(!String.IsNullOrEmpty(outDir) && outDir != @".\") project.MSBuildOutDir = outDir; buildProcess = new BuildProcess(project, PartialBuildType.GenerateReflectionInfo); buildProcess.BuildStepChanged += buildProcess_BuildStepChanged; // Since this is a plug-in, we'll run it directly rather than in a background thread buildProcess.Build(); // Add the list of the comments files in the other project to this build if(lastBuildStep == BuildStep.Completed) foreach(XmlCommentsFile comments in buildProcess.CommentsFiles) builder.CommentsFiles.Insert(0, comments); } catch(Exception ex) { throw new BuilderException("VBP0004", String.Format(CultureInfo.InvariantCulture, "Fatal error, unable to compile project '{0}': {1}", project.Filename, ex.ToString())); } return (lastBuildStep == BuildStep.Completed); }
/// <summary> /// This is called to build a project /// </summary> /// <param name="project">The project to build</param> /// <param name="workingPath">The working path for the project</param> /// <returns>Returns true if successful, false if not</returns> private bool BuildProject(SandcastleProject project, string workingPath) { BuildProcess buildProcess; lastBuildStep = BuildStep.None; builder.ReportProgress("\r\nBuilding {0}", project.Filename); try { // For the plug-in, we'll override some project settings project.SandcastlePath = new FolderPath(builder.SandcastleFolder, true, project); project.HtmlHelp1xCompilerPath = new FolderPath(builder.Help1CompilerFolder, true, project); project.HtmlHelp2xCompilerPath = new FolderPath(builder.Help2CompilerFolder, true, project); project.WorkingPath = new FolderPath(workingPath, true, project); project.OutputPath = new FolderPath(workingPath + @"..\PartialBuildLog\", true, project); buildProcess = new BuildProcess(project, true); buildProcess.BuildStepChanged += buildProcess_BuildStepChanged; // Since this is a plug-in, we'll run it directly rather // than in a background thread. buildProcess.Build(); // Add the list of the comments files in the other project to // this build. foreach(XmlCommentsFile comments in buildProcess.CommentsFiles) builder.CommentsFiles.Insert(0, comments); } catch(Exception ex) { throw new BuilderException("VBP0005", String.Format(CultureInfo.InvariantCulture, "Fatal error, unable to compile project '{0}': {1}", project.Filename, ex.ToString())); } return (lastBuildStep == BuildStep.Completed); }
//===================================================================== /// <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.restrictedProps.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(ProjectElement.Configuration, this.Configuration); msBuildProject.SetGlobalProperty(ProjectElement.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(ProjectElement.OutDir, this.OutDir); msBuildProject.ReevaluateIfNecessary(); } // Associate the MSBuild project with a SHFB project instance and build it using(sandcastleProject = new SandcastleProject(msBuildProject)) { buildProcess = new BuildProcess(sandcastleProject); buildProcess.BuildStepChanged += buildProcess_BuildStepChanged; buildProcess.BuildProgress += buildProcess_BuildProgress; // 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); buildProcess.Build(); } } catch(Exception ex) { Log.LogError(null, "BHT0002", "BHT0002", "SHFB", 0, 0, 0, 0, "Unable to build project '{0}': {1}", msBuildProject.FullPath, ex); } finally { // 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); }
//===================================================================== /// <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; string line; try { if(!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. msBuildProject = this.GetCurrentProject(); if(msBuildProject == null) { // We can't use a prior MSBuild version if(isPriorMSBuildVersion) { Log.LogError(null, "BHT0004", "BHT0004", "SHFB", 0, 0, 0, 0, "An older MSBuild version is " + "being used. Unable to build help project."); return false; } Log.LogWarning(null, "BHT0001", "BHT0001", "SHFB", 0, 0, 0, 0, "Unable to get executing project: " + "Unable to obtain internal reference. The " + "specified project will be loaded but command " + "line property overrides will be ignored."); } } } 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); } if(msBuildProject == null) { // Create the project and set the configuration and platform // options. msBuildProject = new Project(Engine.GlobalEngine); msBuildProject.GlobalProperties.SetProperty( ProjectElement.Configuration, configuration); msBuildProject.GlobalProperties.SetProperty( ProjectElement.Platform, platform); // Override the OutDir property if defined for Team Build if(!String.IsNullOrEmpty(outDir)) msBuildProject.GlobalProperties.SetProperty( ProjectElement.OutDir, outDir); if(!File.Exists(projectFile)) throw new BuilderException("BHT0003", "The specified " + "project file does not exist: " + projectFile); msBuildProject.Load(projectFile); } // Load the MSBuild project and associate it with a SHFB // project instance. sandcastleProject = new SandcastleProject(msBuildProject, true); try { buildProcess = new BuildProcess(sandcastleProject); buildProcess.BuildStepChanged += new EventHandler<BuildProgressEventArgs>( buildProcess_BuildStepChanged); buildProcess.BuildProgress += new EventHandler<BuildProgressEventArgs>( buildProcess_BuildProgress); // Since this is an MSBuild task, we'll run it directly rather // than in a background thread. Log.LogMessage("Building {0}", msBuildProject.FullFileName); buildProcess.Build(); } catch(Exception ex) { Log.LogError(null, "BHT0002", "BHT0002", "SHFB", 0, 0, 0, 0, "Unable to build project '{0}': {1}", msBuildProject.FullFileName, ex); } if(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); }