//===================================================================== /// <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; XmlCommentsFile comments; 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(ProjectElement.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)) { projRef.SetConfiguration(project.Configuration, project.Platform, project.MSBuildOutDir); 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 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 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 == ProjectElement.Configuration).EvaluatedValue, msbProject.ProjectFile.AllEvaluatedProperties.Last( p => p.Name == ProjectElement.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 = FrameworkDictionary.AllFrameworks.FrameworkMatching( targetFrameworksSeen.First(), new Version(targetFrameworkVersionsSeen.Max(f => f)), true); if(frameworkSettings != 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; frameworkSettings = 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(frameworkSettings.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 comments = new XmlCommentsFile(workingPath); // Fix up 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> /// This method is used to execute the plug-in during the build process /// </summary> /// <param name="context">The current execution context</param> public void Execute(Utils.PlugIn.ExecutionContext context) { Encoding enc = Encoding.Default; Thread browserThread; XmlDocument sharedContent; XPathNavigator navContent, item; XmlCommentsFile comments; string sharedContentFilename, workingPath, content; // Copy any XML comments files from the project to the working // folder. Solutions, projects, and assemblies are ignored as // they won't be used with this build type. if(context.BuildStep == BuildStep.ValidatingDocumentationSources) { builder.ExecuteBeforeStepPlugIns(); foreach(DocumentationSource ds in builder.CurrentProject.DocumentationSources) foreach(string commentsName in DocumentationSource.CommentsFiles(ds.SourceFile, ds.IncludeSubFolders)) { workingPath = builder.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 = builder.WorkingFolder + Guid.NewGuid().ToString("B"); builder.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); } 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(builder.CurrentProject.CppCommentsFixup) comments.FixupComments(); builder.CommentsFiles.Add(comments); builder.ReportProgress(" {0} -> {1}", commentsName, workingPath); } builder.ExecuteAfterStepPlugIns(); return; } // Remove the version information items from the shared content // file as the AjaxDoc reflection file doesn't contain version // information. if(context.BuildStep == BuildStep.GenerateSharedContent) { builder.ReportProgress("Removing version information items " + "from shared content file"); sharedContentFilename = builder.WorkingFolder + "SharedBuilderContent.xml"; sharedContent = new XmlDocument(); sharedContent.Load(sharedContentFilename); navContent = sharedContent.CreateNavigator(); item = navContent.SelectSingleNode("content/item[@id='" + "locationInformation']"); if(item != null) item.DeleteSelf(); item = navContent.SelectSingleNode("content/item[@id='" + "assemblyNameAndModule']"); if(item != null) item.DeleteSelf(); sharedContent.Save(sharedContentFilename); return; } builder.ReportProgress("Using project '{0}'", projectName); if(regenerateFiles) { // Regenerate the files first. This is done by starting a // thread to invoke the AjaxDoc application via a web browser // control. This is necessary as the brower control needs to // run in a thread with a single-threaded apartment state. // We can't just request the page as AjaxDoc has to post back // to itself in order to store the generated information. builder.ReportProgress("Generating XML comments and " + "reflection information via AjaxDoc"); browserThread = new Thread(RunBrowser); browserThread.SetApartmentState(ApartmentState.STA); navCount = 0; browserThread.Start(); if(!browserThread.Join(11000)) browserThread.Abort(); if(!String.IsNullOrEmpty(errorText)) throw new BuilderException("ADP0003", "AjaxDoc encountered " + "a scripting error: " + errorText); if(commentsFile == null || reflectionFile == null) throw new BuilderException("ADP0004", "Unable to produce " + "comments file and/or reflection file"); builder.ReportProgress("Generated comments file '{0}' " + "and reflection file '{1}'", commentsFile, reflectionFile); } else { // Use the existing files commentsFile = "Output/" + projectName + ".xml"; reflectionFile = "Output/" + projectName + ".org"; builder.ReportProgress("Using existing XML comments file " + "'{0}' and reflection information file '{1}'", commentsFile, reflectionFile); } // Allow Before step plug-ins to run builder.ExecuteBeforeStepPlugIns(); // Download the files using(WebClient webClient = new WebClient()) { webClient.UseDefaultCredentials = userCreds.UseDefaultCredentials; if(!userCreds.UseDefaultCredentials) webClient.Credentials = new NetworkCredential( userCreds.UserName, userCreds.Password); webClient.CachePolicy = new RequestCachePolicy( RequestCacheLevel.NoCacheNoStore); if(proxyCreds.UseProxyServer) { webClient.Proxy = new WebProxy(proxyCreds.ProxyServer, true); if(!proxyCreds.Credentials.UseDefaultCredentials) webClient.Proxy.Credentials = new NetworkCredential( proxyCreds.Credentials.UserName, proxyCreds.Credentials.Password); } // Since there are only two, download them synchronously workingPath = builder.WorkingFolder + projectName + ".xml"; builder.ReportProgress("Downloading {0}", commentsFile); webClient.DownloadFile(ajaxDocUrl + commentsFile, workingPath); builder.CommentsFiles.Add(new XmlCommentsFile(workingPath)); builder.ReportProgress("Downloading {0}", reflectionFile); webClient.DownloadFile(ajaxDocUrl + reflectionFile, builder.ReflectionInfoFilename); builder.ReportProgress("Downloads completed successfully"); } // AjaxDoc 1.1 prefixes all member names with "J#" which causes // BuildAssembler's ResolveReferenceLinksComponent2 component in // the Sept 2007 CTP to crash. As such, we'll strip it out. I // can't see a need for it anyway. content = BuildProcess.ReadWithEncoding(workingPath, ref enc); content = content.Replace(":J#", ":"); using(StreamWriter sw = new StreamWriter(workingPath, false, enc)) { sw.Write(content); } content = BuildProcess.ReadWithEncoding( builder.ReflectionInfoFilename, ref enc); content = content.Replace(":J#", ":"); using(StreamWriter sw = new StreamWriter( builder.ReflectionInfoFilename, false, enc)) { sw.Write(content); } // Don't apply the filter in a partial build if(!builder.IsPartialBuild && builder.BuildApiFilter.Count != 0) { builder.ReportProgress("Applying API filter manually"); builder.ApplyManualApiFilter(builder.BuildApiFilter, builder.ReflectionInfoFilename); } // Allow After step plug-ins to run builder.ExecuteAfterStepPlugIns(); }
//===================================================================== /// <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); }