/// <summary> /// This is the method that indexes the comments files /// </summary> /// <remarks>Rather than a partial build, we'll just index the comments files.</remarks> private IndexedCommentsCache IndexComments() { HashSet<string> projectDictionary = new HashSet<string>(); IndexedCommentsCache cache = new IndexedCommentsCache(100); MSBuildProject projRef; string lastSolution = null; // Index the framework comments based on the framework version in the project FrameworkSettings frameworkSettings = FrameworkDictionary.AllFrameworks.GetFrameworkWithRedirect( currentProject.FrameworkVersion); if(frameworkSettings == null) throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "Unable to locate information for a framework version or its redirect: {0}", currentProject.FrameworkVersion)); foreach(var location in frameworkSettings.CommentsFileLocations(currentProject.Language)) { indexTokenSource.Token.ThrowIfCancellationRequested(); cache.IndexCommentsFiles(Path.GetDirectoryName(location), Path.GetFileName(location), false, null); } // Index the comments file documentation sources foreach(string file in currentProject.DocumentationSources.SelectMany(ds => ds.CommentsFiles)) { indexTokenSource.Token.ThrowIfCancellationRequested(); cache.IndexCommentsFiles(Path.GetDirectoryName(file), Path.GetFileName(file), false, null); } // Also, index the comments files in project documentation sources foreach(DocumentationSource ds in currentProject.DocumentationSources) foreach(var sourceProject in ds.Projects( !String.IsNullOrEmpty(ds.Configuration) ? ds.Configuration : currentProject.Configuration, !String.IsNullOrEmpty(ds.Platform) ? ds.Platform : currentProject.Platform)) { indexTokenSource.Token.ThrowIfCancellationRequested(); // NOTE: This code should be similar to the code in BuildProcess.ValidateDocumentationSources! // Solutions are followed by the projects that they contain if(sourceProject.ProjectFileName.EndsWith(".sln", StringComparison.OrdinalIgnoreCase)) { lastSolution = sourceProject.ProjectFileName; continue; } // Ignore projects that we've already seen if(projectDictionary.Add(sourceProject.ProjectFileName)) { using(projRef = new MSBuildProject(sourceProject.ProjectFileName)) { // Use the project file configuration and platform properties if they are set. If // not, use the documentation source values. If they are not set, use the SHFB // project settings. projRef.SetConfiguration( !String.IsNullOrEmpty(sourceProject.Configuration) ? sourceProject.Configuration : !String.IsNullOrEmpty(ds.Configuration) ? ds.Configuration : currentProject.Configuration, !String.IsNullOrEmpty(sourceProject.Platform) ? sourceProject.Platform : !String.IsNullOrEmpty(ds.Platform) ? ds.Platform : currentProject.Platform, currentProject.MSBuildOutDir, false); // Add Visual Studio solution macros if necessary if(lastSolution != null) projRef.SetSolutionMacros(lastSolution); if(!String.IsNullOrEmpty(projRef.XmlCommentsFile)) cache.IndexCommentsFiles(Path.GetDirectoryName(projRef.XmlCommentsFile), Path.GetFileName(projRef.XmlCommentsFile), false, null); } } } return cache; }
/// <summary> /// This is used to get the build item meta data from the referenced /// project. /// </summary> /// <param name="refresh">True to force the metadata to be refreshed, /// false to only retrieve it if it doesn't exist.</param> private void GetProjectMetadata(bool refresh) { string name; if(!refresh && base.ProjectElement.HasMetadata(ProjectElement.Name) && base.ProjectElement.HasMetadata(ProjectElement.ProjectGuid)) return; using(MSBuildProject project = new MSBuildProject(projectPath)) { project.SetConfiguration(SandcastleProject.DefaultConfiguration, SandcastleProject.DefaultPlatform, null); name = Path.GetFileNameWithoutExtension(project.AssemblyName); if(!String.IsNullOrEmpty(name)) base.ProjectElement.SetMetadata(ProjectElement.Name, name); else base.ProjectElement.SetMetadata(ProjectElement.Name, "(Invalid project type)"); base.ProjectElement.SetMetadata(ProjectElement.ProjectGuid, project.ProjectGuid); } }
//===================================================================== /// <summary> /// Validate the documentation source information and copy the files to /// the working folder. /// </summary> /// <exception cref="BuilderException">This is thrown if any of /// the information is invalid.</exception> protected void ValidateDocumentationSources() { List<string> commentsList = new List<string>(); Dictionary<string, MSBuildProject> projectDictionary = new Dictionary<string, MSBuildProject>(); MSBuildProject projRef; BuildItem buildItem; XPathDocument testComments; XPathNavigator navComments; XmlCommentsFile comments; int fileCount; string workingPath, projFramework, hintPath, lastSolution = null, targetFramework = project.FrameworkVersion; this.ReportProgress(BuildStep.ValidatingDocumentationSources, "Validating and copying documentation source information"); assembliesList = new Collection<string>(); referenceDictionary = new Dictionary<string, BuildItem>(); commentsFiles = new XmlCommentsFileCollection(); if(this.ExecutePlugIns(ExecutionBehaviors.InsteadOf)) return; // It's possible a plug-in might want to add or remove assemblies // so we'll run them before checking to see if the project has any. this.ExecutePlugIns(ExecutionBehaviors.Before); if(project.DocumentationSources.Count == 0) throw new BuilderException("BE0039", "The project does " + "not have any documentation sources defined"); // Clone the project's references foreach(string refType in (new string[] { "Reference", "COMReference" })) foreach(BuildItem reference in project.MSBuildProject.GetEvaluatedItemsByName(refType)) { buildItem = reference.Clone(); // Make sure hint paths are correct by adding the project folder to any relative // paths. Skip any containing MSBuild variable references. if(buildItem.HasMetadata(ProjectElement.HintPath)) { hintPath = buildItem.GetMetadata(ProjectElement.HintPath); if(!Path.IsPathRooted(hintPath) && hintPath.IndexOf("$(", StringComparison.Ordinal) == -1) buildItem.SetMetadata(ProjectElement.HintPath, Path.Combine(projectFolder, hintPath)); } referenceDictionary.Add(reference.Include, buildItem); } // Convert project references to regular references that point to the output assembly. // Project references get built and we may not have enough info for that to happen // successfully. As such, we'll assume the project has already been built and that its // target exists. foreach(BuildItem reference in project.MSBuildProject.GetEvaluatedItemsByName("ProjectReference")) { projRef = new MSBuildProject(reference.Include); projRef.SetConfiguration(project.Configuration, project.Platform, project.MSBuildOutDir); buildItem = projRef.ProjectFile.AddNewItem("Reference", Path.GetFileNameWithoutExtension(projRef.AssemblyName)); buildItem.SetMetadata("HintPath", projRef.AssemblyName); referenceDictionary.Add(buildItem.Include, buildItem); } // For each source, make three passes: one for projects, one for assemblies and one // for comments files. Projects and comments files are optional but when all done, // at least one assembly must have been found. foreach(DocumentationSource ds in project.DocumentationSources) { fileCount = 0; this.ReportProgress("Source: {0}", ds.SourceFile); foreach(var sourceProject in DocumentationSource.Projects(ds.SourceFile, ds.IncludeSubFolders, !String.IsNullOrEmpty(ds.Configuration) ? ds.Configuration : project.Configuration, !String.IsNullOrEmpty(ds.Platform) ? ds.Platform : project.Platform)) { // NOTE: This code in EntityReferenceWindow.IndexComments should be similar to this! // Solutions are followed by the projects that they contain if(sourceProject.ProjectFileName.EndsWith(".sln", StringComparison.OrdinalIgnoreCase)) { lastSolution = sourceProject.ProjectFileName; continue; } if(!projectDictionary.ContainsKey(sourceProject.ProjectFileName)) { // These are handled below this.ReportProgress(" Found project '{0}'", sourceProject.ProjectFileName); projRef = new MSBuildProject(sourceProject.ProjectFileName); // Use the project file configuration and platform properties if they are set. If not, // use the documentation source values. If they are not set, use the SHFB project settings. projRef.SetConfiguration( !String.IsNullOrEmpty(sourceProject.Configuration) ? sourceProject.Configuration : !String.IsNullOrEmpty(ds.Configuration) ? ds.Configuration : project.Configuration, !String.IsNullOrEmpty(sourceProject.Platform) ? sourceProject.Platform : !String.IsNullOrEmpty(ds.Platform) ? ds.Platform : project.Platform, project.MSBuildOutDir); // Add Visual Studio solution macros if necessary if(lastSolution != null) projRef.SetSolutionMacros(lastSolution); projectDictionary.Add(sourceProject.ProjectFileName, projRef); } else this.ReportProgress(" Ignoring duplicate project file '{0}'", sourceProject.ProjectFileName); fileCount++; } foreach(string asmName in DocumentationSource.Assemblies(ds.SourceFile, ds.IncludeSubFolders)) { if(!assembliesList.Contains(asmName)) { // Assemblies are parsed in place by MRefBuilder so we // don't have to do anything with them here. this.ReportProgress(" Found assembly '{0}'", asmName); assembliesList.Add(asmName); } else this.ReportProgress(" Ignoring duplicate assembly file '{0}'", asmName); fileCount++; } foreach(string commentsName in DocumentationSource.CommentsFiles( ds.SourceFile, ds.IncludeSubFolders)) { if(!commentsList.Contains(commentsName)) { // These are handled below commentsList.Add(commentsName); } else this.ReportProgress(" Ignoring duplicate comments file '{0}'", commentsName); fileCount++; } if(fileCount == 0) this.ReportWarning("BE0006", "Unable to location any " + "documentation sources for '{0}'", ds.SourceFile); } // Parse projects for assembly, comments, and reference info if(projectDictionary.Count != 0) { this.ReportProgress("\r\nParsing project files"); foreach(MSBuildProject msbProject in projectDictionary.Values) { workingPath = msbProject.AssemblyName; if(!String.IsNullOrEmpty(workingPath)) { if(!File.Exists(workingPath)) throw new BuilderException("BE0040", "Project assembly does not exist: " + workingPath); this.ReportProgress(" Found assembly '{0}'", workingPath); assembliesList.Add(workingPath); } else throw new BuilderException("BE0067", String.Format(CultureInfo.InvariantCulture, "Unable to obtain assembly name from project file '{0}' using Configuration " + "'{1}', Platform '{2}'", msbProject.ProjectFile.FullFileName, msbProject.ProjectFile.GetEvaluatedProperty(ProjectElement.Configuration), msbProject.ProjectFile.GetEvaluatedProperty(ProjectElement.Platform))); workingPath = msbProject.XmlCommentsFile; if(!String.IsNullOrEmpty(workingPath)) { if(!File.Exists(workingPath)) throw new BuilderException("BE0041", "Project XML comments file does not exist: " + workingPath); commentsList.Add(workingPath); } // Note the highest framework version used projFramework = msbProject.TargetFrameworkVersion; if(!String.IsNullOrEmpty(projFramework)) { projFramework = FrameworkVersionTypeConverter.LatestMatching(projFramework.Substring(1)); if(String.Compare(targetFramework, projFramework, StringComparison.OrdinalIgnoreCase) < 0) targetFramework = projFramework; } // Clone the project's references msbProject.CloneReferences(referenceDictionary); } } if(project.FrameworkVersion != targetFramework) { this.ReportWarning("BE0007", "A project with a higher " + "framework version was found. Changing project " + "FrameworkVersion property from '{0}' to '{1}' for " + "the build", project.FrameworkVersion, targetFramework); project.FrameworkVersion = targetFramework; } if(assembliesList.Count == 0) throw new BuilderException("BE0042", "You must specify at " + "least one documentation source in the form of an " + "assembly or a Visual Studio solution/project file"); // Log the references found, if any if(referenceDictionary.Count != 0) { this.ReportProgress("\r\nReferences to use:"); string[] keys = new string[referenceDictionary.Keys.Count]; referenceDictionary.Keys.CopyTo(keys, 0); Array.Sort(keys); foreach(string key in keys) this.ReportProgress(" {0}", key); } if(commentsList.Count != 0) this.ReportProgress("\r\nCopying XML comments files"); // XML comments files are copied to the working folder in case they // need to be fixed up. foreach(string commentsName in commentsList) { workingPath = workingFolder + Path.GetFileName(commentsName); // Warn if there is a duplicate and copy the comments file // to a unique name to preserve its content. if(File.Exists(workingPath)) { workingPath = workingFolder + Guid.NewGuid().ToString("B"); this.ReportWarning("BE0063", "'{0}' matches a " + "previously copied comments filename. The " + "duplicate will be copied to a unique name to " + "preserve the comments it contains.", commentsName); } try { // Not all XML files found may be comments files. Ignore // those that are not. testComments = new XPathDocument(commentsName); navComments = testComments.CreateNavigator(); if(navComments.SelectSingleNode("doc/members") == null) { this.ReportWarning("BE0005", "File '{0}' does not " + "contain a 'doc/members' node and will not be " + "used as an XML comments file.", commentsName); continue; } } catch(Exception ex) { this.ReportWarning("BE0061", "File '{0}' could not be " + "loaded and will not be used as an XML comments file." + " Error: {1}", commentsName, ex.Message); continue; } File.Copy(commentsName, workingPath, true); File.SetAttributes(workingPath, FileAttributes.Normal); // Add the file to the XML comments file collection comments = new XmlCommentsFile(workingPath); // Fixup comments for CPP comments files? if(project.CppCommentsFixup) comments.FixupComments(); commentsFiles.Add(comments); this.ReportProgress(" {0} -> {1}", commentsName, workingPath); } if(commentsFiles.Count == 0) this.ReportWarning("BE0062", "No XML comments files found. " + "The help file will not contain any member comments."); this.ExecutePlugIns(ExecutionBehaviors.After); }
//===================================================================== /// <summary> /// Validate the documentation source information and copy the files to the working folder /// </summary> /// <exception cref="BuilderException">This is thrown if any of the information is invalid</exception> private void ValidateDocumentationSources() { List<string> commentsList = new List<string>(); Dictionary<string, MSBuildProject> projectDictionary = new Dictionary<string, MSBuildProject>(); HashSet<string> targetFrameworksSeen = new HashSet<string>(), targetFrameworkVersionsSeen = new HashSet<string>(); MSBuildProject projRef; XPathDocument testComments; XPathNavigator navComments; int fileCount; string workingPath, lastSolution = null; this.ReportProgress(BuildStep.ValidatingDocumentationSources, "Validating and copying documentation source information"); assembliesList = new Collection<string>(); referenceDictionary = new Dictionary<string, Tuple<string, string, List<KeyValuePair<string, string>>>>(); commentsFiles = new XmlCommentsFileCollection(); if(this.ExecutePlugIns(ExecutionBehaviors.InsteadOf)) return; // It's possible a plug-in might want to add or remove assemblies so we'll run them before checking // to see if the project has any. this.ExecutePlugIns(ExecutionBehaviors.Before); if(project.DocumentationSources.Count() == 0) throw new BuilderException("BE0039", "The project does not have any documentation sources defined"); // Clone the project's references. These will be added to a build project later on so we'll note the // necessary information needed to create the reference in the future project. foreach(string refType in (new string[] { "Reference", "COMReference" })) foreach(ProjectItem reference in project.MSBuildProject.GetItems(refType)) referenceDictionary.Add(reference.EvaluatedInclude, Tuple.Create(reference.ItemType, reference.EvaluatedInclude, reference.Metadata.Select(m => new KeyValuePair<string, string>(m.Name, m.EvaluatedValue)).ToList())); // Convert project references to regular references that point to the output assembly. Project // references get built and we may not have enough info for that to happen successfully. As such, // we'll assume the project has already been built and that its target exists. foreach(ProjectItem reference in project.MSBuildProject.GetItems("ProjectReference")) { // Ignore references used only for MSBuild dependency determination var refOutput = reference.GetMetadata(BuildItemMetadata.ReferenceOutputAssembly); if(refOutput != null && refOutput.EvaluatedValue.Equals("false", StringComparison.OrdinalIgnoreCase)) { this.ReportProgress("Ignoring reference to '{0}' which is only used for MSBuild dependency " + "determination", reference.EvaluatedInclude); continue; } using(projRef = new MSBuildProject(reference.EvaluatedInclude)) { // .NET 4.5 supports a property that tells MSBuild to put the project output into a // project-specific folder in OutDir. var projectSpecificFolder = project.MSBuildProject.AllEvaluatedProperties.FirstOrDefault( p => p.Name == "GenerateProjectSpecificOutputFolder"); bool usesProjectSpecificOutput = (projectSpecificFolder != null && !String.IsNullOrWhiteSpace(projectSpecificFolder.EvaluatedValue) && Convert.ToBoolean(projectSpecificFolder.EvaluatedValue, CultureInfo.InvariantCulture)); projRef.SetConfiguration(project.Configuration, project.Platform, project.MSBuildOutDir, usesProjectSpecificOutput); referenceDictionary.Add(projRef.AssemblyName, Tuple.Create("Reference", Path.GetFileNameWithoutExtension(projRef.AssemblyName), (new [] { new KeyValuePair<string, string>("HintPath", projRef.AssemblyName) }).ToList())); } } try { // For each source, make three passes: one for projects, one for assemblies and one for comments // files. Projects and comments files are optional but when all done, at least one assembly must // have been found. foreach(DocumentationSource ds in project.DocumentationSources) { fileCount = 0; this.ReportProgress("Source: {0}", ds.SourceFile); foreach(var sourceProject in ds.Projects( !String.IsNullOrEmpty(ds.Configuration) ? ds.Configuration : project.Configuration, !String.IsNullOrEmpty(ds.Platform) ? ds.Platform : project.Platform)) { // NOTE: This code in EntityReferenceWindow.IndexComments should be similar to this! // Solutions are followed by the projects that they contain if(sourceProject.ProjectFileName.EndsWith(".sln", StringComparison.OrdinalIgnoreCase)) { lastSolution = sourceProject.ProjectFileName; continue; } if(!projectDictionary.ContainsKey(sourceProject.ProjectFileName)) { // These are handled below this.ReportProgress(" Found project '{0}'", sourceProject.ProjectFileName); // .NET 4.5 supports a property that tells MSBuild to put the project output into a // project-specific folder in OutDir. var projectSpecificFolder = project.MSBuildProject.AllEvaluatedProperties.FirstOrDefault( p => p.Name == "GenerateProjectSpecificOutputFolder"); bool usesProjectSpecificOutput = (projectSpecificFolder != null && !String.IsNullOrWhiteSpace(projectSpecificFolder.EvaluatedValue) && Convert.ToBoolean(projectSpecificFolder.EvaluatedValue, CultureInfo.InvariantCulture)); projRef = new MSBuildProject(sourceProject.ProjectFileName); // Use the project file configuration and platform properties if they are set. If not, // use the documentation source values. If they are not set, use the SHFB project settings. projRef.SetConfiguration( !String.IsNullOrEmpty(sourceProject.Configuration) ? sourceProject.Configuration : !String.IsNullOrEmpty(ds.Configuration) ? ds.Configuration : project.Configuration, !String.IsNullOrEmpty(sourceProject.Platform) ? sourceProject.Platform : !String.IsNullOrEmpty(ds.Platform) ? ds.Platform : project.Platform, project.MSBuildOutDir, usesProjectSpecificOutput); // Add Visual Studio solution macros if necessary if(lastSolution != null) projRef.SetSolutionMacros(lastSolution); projectDictionary.Add(sourceProject.ProjectFileName, projRef); } else this.ReportProgress(" Ignoring duplicate project file '{0}'", sourceProject.ProjectFileName); fileCount++; } foreach(string asmName in ds.Assemblies) { if(!assembliesList.Contains(asmName)) { // Assemblies are parsed in place by MRefBuilder so we don't have to do anything with // them here. this.ReportProgress(" Found assembly '{0}'", asmName); assembliesList.Add(asmName); } else this.ReportProgress(" Ignoring duplicate assembly file '{0}'", asmName); fileCount++; } foreach(string commentsName in ds.CommentsFiles) { if(!commentsList.Contains(commentsName)) { // These are handled below commentsList.Add(commentsName); } else this.ReportProgress(" Ignoring duplicate comments file '{0}'", commentsName); fileCount++; } if(fileCount == 0) this.ReportWarning("BE0006", "Unable to locate any documentation sources for '{0}' " + "(Configuration: {1} Platform: {2})", ds.SourceFile, !String.IsNullOrEmpty(ds.Configuration) ? ds.Configuration : project.Configuration, !String.IsNullOrEmpty(ds.Platform) ? ds.Platform : project.Platform); } // Parse projects for assembly, comments, and reference info if(projectDictionary.Count != 0) { this.ReportProgress("\r\nParsing project files"); foreach(MSBuildProject msbProject in projectDictionary.Values) { workingPath = msbProject.AssemblyName; if(!String.IsNullOrEmpty(workingPath)) { if(!File.Exists(workingPath)) throw new BuilderException("BE0040", "Project assembly does not exist: " + workingPath); if(!assembliesList.Contains(workingPath)) { // Assemblies are parsed in place by MRefBuilder so we don't have to do anything // with them here. this.ReportProgress(" Found assembly '{0}'", workingPath); assembliesList.Add(workingPath); } else this.ReportProgress(" Ignoring duplicate assembly file '{0}'", workingPath); } else throw new BuilderException("BE0067", String.Format(CultureInfo.CurrentCulture, "Unable to obtain assembly name from project file '{0}' using Configuration " + "'{1}', Platform '{2}'", msbProject.ProjectFile.FullPath, msbProject.ProjectFile.AllEvaluatedProperties.Last( p => p.Name == BuildItemMetadata.Configuration).EvaluatedValue, msbProject.ProjectFile.AllEvaluatedProperties.Last( p => p.Name == BuildItemMetadata.Platform).EvaluatedValue)); workingPath = msbProject.XmlCommentsFile; if(!String.IsNullOrEmpty(workingPath)) { if(!File.Exists(workingPath)) throw new BuilderException("BE0041", "Project XML comments file does not exist: " + workingPath); if(!commentsList.Contains(workingPath)) { // These are handled below commentsList.Add(workingPath); } else this.ReportProgress(" Ignoring duplicate comments file '{0}'", workingPath); } // Note the platforms seen and the highest framework version used targetFrameworksSeen.Add(msbProject.TargetFrameworkIdentifier); targetFrameworkVersionsSeen.Add(msbProject.TargetFrameworkVersion); // Clone the project's reference information msbProject.CloneReferenceInfo(referenceDictionary); } // If we saw multiple framework types in the projects, stop now. Due to the different // assemblies used, we cannot mix the project types within the same SHFB project. They will // need to be documented separately and can be merged using the Version Builder plug-in if // needed. if(targetFrameworksSeen.Count > 1) throw new BuilderException("BE0070", "Differing framework types were detected in the " + "documentation sources (i.e. .NET, Silverlight, Portable). Due to the different " + "sets of assemblies used, the different frameworks cannot be mixed within the same " + "documentation project. See the error number topic in the help file for details."); // If a project with a higher framework version was found, switch to that version now var projectFramework = reflectionDataDictionary.CoreFrameworkMatching( targetFrameworksSeen.First(), new Version(targetFrameworkVersionsSeen.Max(f => f)), true); if(frameworkReflectionData != projectFramework) { // If redirected and no suitable version was found, we can't go any further if(projectFramework == null) throw new BuilderException("BE0073", String.Format(CultureInfo.CurrentCulture, "A project with a different or higher framework version was found but that " + "version ({0} {1}) or a suitable redirected version was not found on this " + "system. The build cannot continue.", targetFrameworksSeen.First(), targetFrameworkVersionsSeen.Max(f => f))); this.ReportWarning("BE0007", "A project with a different or higher framework version " + "was found. Changing project FrameworkVersion property from '{0}' to '{1}' for " + "the build.", project.FrameworkVersion, projectFramework.Title); project.FrameworkVersion = projectFramework.Title; frameworkReflectionData = projectFramework; } } } finally { // Dispose of any MSBuild projects that we loaded foreach(var p in projectDictionary.Values) p.Dispose(); } if(assembliesList.Count == 0) throw new BuilderException("BE0042", "You must specify at least one documentation source in " + "the form of an assembly or a Visual Studio solution/project file"); // Log the references found, if any if(referenceDictionary.Count != 0) { this.ReportProgress("\r\nReferences to include (excluding framework assemblies):"); string[] keys = new string[referenceDictionary.Keys.Count]; referenceDictionary.Keys.CopyTo(keys, 0); Array.Sort(keys); // Filter out references related to the framework. MRefBuilder will resolve these // automatically. foreach(string key in keys) if(frameworkReflectionData.ContainsAssembly(key)) referenceDictionary.Remove(key); else this.ReportProgress(" {0}", key); if(referenceDictionary.Count == 0) this.ReportProgress(" None"); } if(commentsList.Count != 0) this.ReportProgress("\r\nCopying XML comments files"); // XML comments files are copied to the working folder in case they need to be fixed up foreach(string commentsName in commentsList) { workingPath = workingFolder + Path.GetFileName(commentsName); // Warn if there is a duplicate and copy the comments file to a unique name to preserve its // content. if(File.Exists(workingPath)) { workingPath = workingFolder + Guid.NewGuid().ToString("B"); this.ReportWarning("BE0063", "'{0}' matches a previously copied comments filename. The " + "duplicate will be copied to a unique name to preserve the comments it contains.", commentsName); } try { // Not all XML files found may be comments files. Ignore those that are not. testComments = new XPathDocument(commentsName); navComments = testComments.CreateNavigator(); if(navComments.SelectSingleNode("doc/members") == null) { this.ReportWarning("BE0005", "File '{0}' does not contain a 'doc/members' node and " + "will not be used as an XML comments file.", commentsName); continue; } } catch(Exception ex) { this.ReportWarning("BE0061", "File '{0}' could not be loaded and will not be used as an " + "XML comments file. Error: {1}", commentsName, ex.Message); continue; } File.Copy(commentsName, workingPath, true); File.SetAttributes(workingPath, FileAttributes.Normal); // Add the file to the XML comments file collection commentsFiles.Add(new XmlCommentsFile(workingPath)); this.ReportProgress(" {0} -> {1}", commentsName, workingPath); } if(commentsFiles.Count == 0) this.ReportWarning("BE0062", "No XML comments files found. The help file will not contain " + "any member comments."); this.ExecutePlugIns(ExecutionBehaviors.After); }
/// <summary> /// This is the thread method that indexes the comments files /// </summary> /// <remarks>Rather than a partial build, we'll just index the /// comments files.</remarks> private void IndexComments() { HashSet<string> projectDictionary = new HashSet<string>(); Collection<string> frameworkLocations = new Collection<string>(); Dictionary<string, string> cacheName = new Dictionary<string,string>(); IndexedCommentsCache cache = new IndexedCommentsCache(100); MSBuildProject projRef; string path, lastSolution = null; try { BuildProcess.GetFrameworkCommentsFiles(frameworkLocations, cacheName, currentProject.Language, currentProject.FrameworkVersion); // Index the framework comments foreach(string location in frameworkLocations) { path = Environment.ExpandEnvironmentVariables(location); cache.IndexCommentsFiles(path, null, true, null); } // Index the comments file documentation sources foreach(string file in currentProject.DocumentationSources.CommentsFiles) cache.IndexCommentsFiles(Path.GetDirectoryName(file), Path.GetFileName(file), false, null); // Also, index the comments files in project documentation sources foreach(DocumentationSource ds in currentProject.DocumentationSources) foreach(var sourceProject in DocumentationSource.Projects(ds.SourceFile, ds.IncludeSubFolders, !String.IsNullOrEmpty(ds.Configuration) ? ds.Configuration : currentProject.Configuration, !String.IsNullOrEmpty(ds.Platform) ? ds.Platform : currentProject.Platform)) { // NOTE: This code should be similar to the code in BuildProcess.ValidateDocumentationSources! // Solutions are followed by the projects that they contain if(sourceProject.ProjectFileName.EndsWith(".sln", StringComparison.OrdinalIgnoreCase)) { lastSolution = sourceProject.ProjectFileName; continue; } // Ignore projects that we've already seen if(projectDictionary.Add(sourceProject.ProjectFileName)) { projRef = new MSBuildProject(sourceProject.ProjectFileName); // Use the project file configuration and platform properties if they are set. If not, // use the documentation source values. If they are not set, use the SHFB project settings. projRef.SetConfiguration( !String.IsNullOrEmpty(sourceProject.Configuration) ? sourceProject.Configuration : !String.IsNullOrEmpty(ds.Configuration) ? ds.Configuration : currentProject.Configuration, !String.IsNullOrEmpty(sourceProject.Platform) ? sourceProject.Platform : !String.IsNullOrEmpty(ds.Platform) ? ds.Platform : currentProject.Platform, currentProject.MSBuildOutDir); // Add Visual Studio solution macros if necessary if(lastSolution != null) projRef.SetSolutionMacros(lastSolution); if(!String.IsNullOrEmpty(projRef.XmlCommentsFile)) cache.IndexCommentsFiles(Path.GetDirectoryName(projRef.XmlCommentsFile), Path.GetFileName(projRef.XmlCommentsFile), false, null); } } this.Invoke(new IndexingCompleted(this.Completed), new object[] { cache }); } catch(Exception ex) { System.Diagnostics.Debug.WriteLine(ex.ToString()); this.Invoke(new IndexingFailed(this.Failed), new object[] { ex.Message }); } }