/// <summary> /// This method is used to initialize the plug-in at the start of the build process /// </summary> /// <param name="buildProcess">A reference to the current build process</param> /// <param name="configuration">The configuration data that the plug-in should use to initialize itself</param> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { builder = buildProcess; var metadata = (HelpFileBuilderPlugInExportAttribute)this.GetType().GetCustomAttributes( typeof(HelpFileBuilderPlugInExportAttribute), false).First(); builder.ReportProgress("{0} Version {1}\r\n{2}\r\n This build will only include additional " + "content items.", metadata.Id, metadata.Version, metadata.Copyright); if(!builder.CurrentProject.HasItems(BuildAction.ContentLayout) && !builder.CurrentProject.HasItems(BuildAction.SiteMap) && !builder.CurrentProject.HasItems(BuildAction.Content)) throw new BuilderException("ACP0001", "The Additional Content Only plug-in requires a " + "conceptual content layout file, a site map file, or content items in the project."); }
/// <summary> /// This method is used to initialize the plug-in at the start of the /// build process. /// </summary> /// <param name="buildProcess">A reference to the current build /// process.</param> /// <param name="configuration">The configuration data that the plug-in /// should use to initialize itself.</param> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { builder = buildProcess; builder.ReportProgress("{0} Version {1}\r\n{2}", this.Name, this.Version, this.Copyright); exclusionList = new List<string>(); }
/// <inheritdoc/> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { builder = buildProcess; var metadata = (HelpFileBuilderPlugInExportAttribute)this.GetType().GetCustomAttributes( typeof(HelpFileBuilderPlugInExportAttribute), false).First(); builder.ReportProgress("{0} Version {1}\r\n{2}", metadata.Id, metadata.Version, metadata.Copyright); }
/// <summary> /// This method is used to initialize the plug-in at the start of the build process /// </summary> /// <param name="buildProcess">A reference to the current build process</param> /// <param name="configuration">The configuration data that the plug-in should use to initialize itself</param> /// <exception cref="BuilderException">This is thrown if the plug-in configuration is not valid</exception> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { XPathNavigator root, msHelpViewer; string value; builder = buildProcess; var metadata = (HelpFileBuilderPlugInExportAttribute)this.GetType().GetCustomAttributes( typeof(HelpFileBuilderPlugInExportAttribute), false).First(); builder.ReportProgress("{0} Version {1}\r\n{2}", metadata.Id, metadata.Version, metadata.Copyright); root = configuration.SelectSingleNode("configuration"); value = root.GetAttribute("deleteAfterDeploy", String.Empty); if(!String.IsNullOrEmpty(value)) deleteAfterDeploy = Convert.ToBoolean(value, CultureInfo.InvariantCulture); if(root.IsEmptyElement) throw new BuilderException("ODP0001", "The Output Deployment plug-in has not been " + "configured yet"); deployHelp1 = DeploymentLocation.FromXPathNavigator(root, "help1x"); deployHelp2 = DeploymentLocation.FromXPathNavigator(root, "help2x"); deployHelpViewer = DeploymentLocation.FromXPathNavigator(root, "helpViewer"); deployWebsite = DeploymentLocation.FromXPathNavigator(root, "website"); deployOpenXml = DeploymentLocation.FromXPathNavigator(root, "openXml"); msHelpViewer = root.SelectSingleNode("deploymentLocation[@id='helpViewer']"); if(msHelpViewer == null || !Boolean.TryParse(msHelpViewer.GetAttribute("renameMSHA", String.Empty).Trim(), out renameMSHA)) renameMSHA = false; // At least one deployment location must be defined if(deployHelp1.Location == null && deployHelp2.Location == null && deployHelpViewer.Location == null && deployWebsite.Location == null && deployOpenXml.Location == null) throw new BuilderException("ODP0002", "The output deployment plug-in must have at least " + "one configured deployment location"); // Issue a warning if the deployment location is null and the associated help file format is active if(deployHelp1.Location == null && (builder.CurrentProject.HelpFileFormat & HelpFileFormats.HtmlHelp1) != 0) builder.ReportWarning("ODP0003", "HTML Help 1 will be generated but not deployed due to " + "missing deployment location information"); if(deployHelp2.Location == null && (builder.CurrentProject.HelpFileFormat & HelpFileFormats.MSHelp2) != 0) builder.ReportWarning("ODP0003", "MS Help 2 will be generated but not deployed due to " + "missing deployment location information"); if(deployHelpViewer.Location == null && (builder.CurrentProject.HelpFileFormat & HelpFileFormats.MSHelpViewer) != 0) builder.ReportWarning("ODP0003", "MS Help Viewer will be generated but not deployed due " + "to missing deployment location information"); if(deployWebsite.Location == null && (builder.CurrentProject.HelpFileFormat & HelpFileFormats.Website) != 0) builder.ReportWarning("ODP0003", "Website will be generated but not deployed due to " + "missing deployment location information"); if(deployOpenXml.Location == null && (builder.CurrentProject.HelpFileFormat & HelpFileFormats.OpenXml) != 0) builder.ReportWarning("ODP0003", "Open XML will be generated but not deployed due to " + "missing deployment location information"); }
/// <summary> /// This is used to create the companion files used to resolve conceptual links /// </summary> /// <param name="builder">The build process</param> private void CreateCompanionFiles(BuildProcess builder) { string destFolder = builder.WorkingFolder + "xmlComp\\"; builder.ReportProgress(" Companion files"); if(!Directory.Exists(destFolder)) Directory.CreateDirectory(destFolder); foreach(TopicCollection tc in topics) foreach(Topic t in tc) t.WriteCompanionFile(destFolder, builder); }
/// <summary> /// Merge the conceptual topic IDs into the BuildAssembler manifest file. /// </summary> /// <param name="builder">The build process</param> private void MergeConceptualManifest(BuildProcess builder) { XmlWriterSettings settings = new XmlWriterSettings(); XmlWriter writer = null; string conceptualManifest = builder.WorkingFolder + "ConceptualManifest.xml", referenceManifest = builder.WorkingFolder + "manifest.xml"; builder.ReportProgress(" Merging topic IDs into manifest.xml"); try { settings.Indent = true; settings.CloseOutput = true; writer = XmlWriter.Create(conceptualManifest, settings); writer.WriteStartDocument(); writer.WriteStartElement("topics"); foreach(TopicCollection tc in topics) foreach(Topic t in tc) t.WriteManifest(writer, builder); if(File.Exists(referenceManifest)) foreach(var topic in ComponentUtilities.XmlStreamAxis(referenceManifest, "topic")) { writer.WriteStartElement("topic"); foreach(var attr in topic.Attributes()) writer.WriteAttributeString(attr.Name.LocalName, attr.Value); writer.WriteEndElement(); } writer.WriteEndElement(); // </topics> writer.WriteEndDocument(); } finally { if(writer != null) writer.Close(); if(File.Exists(referenceManifest)) File.Copy(referenceManifest, Path.ChangeExtension(referenceManifest, "old"), true); File.Copy(conceptualManifest, referenceManifest, true); File.Delete(conceptualManifest); } }
/// <summary> /// This method is used to initialize the plug-in at the start of the build process /// </summary> /// <param name="buildProcess">A reference to the current build process</param> /// <param name="configuration">The configuration data that the plug-in should use to initialize itself</param> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { XPathNavigator root; ignoreIfUnresolved = new List<string>(); builder = buildProcess; var metadata = (HelpFileBuilderPlugInExportAttribute)this.GetType().GetCustomAttributes( typeof(HelpFileBuilderPlugInExportAttribute), false).First(); builder.ReportProgress("{0} Version {1}\r\n{2}", metadata.Id, metadata.Version, metadata.Copyright); root = configuration.SelectSingleNode("configuration"); if(root.IsEmptyElement) throw new BuilderException("ABR0001", "The Assembly Binding Redirection Resolver plug-in " + "has not been configured yet"); // Load the configuration string value = root.GetAttribute("useGAC", String.Empty); if(!Boolean.TryParse(value, out useGac)) useGac = false; redirects = new BindingRedirectSettingsCollection(); redirects.FromXml(builder.CurrentProject, root); foreach(XPathNavigator nav in root.Select("ignoreIfUnresolved/assemblyIdentity/@name")) ignoreIfUnresolved.Add(nav.Value); }
//===================================================================== /// <summary> /// This is used to copy the additional content token, image, and topic files to the build folder /// </summary> /// <param name="builder">The build process</param> /// <remarks>This will copy the code snippet file if specified, save token information to a shared /// content file called <strong>_Tokens_.xml</strong> in the build process's working folder, copy the /// image files to the <strong>.\media</strong> folder in the build process's working folder, save the /// media map to a file called <strong>_MediaContent_.xml</strong> in the build process's working folder, /// and save the topic files to the <strong>.\ddueXml</strong> folder in the build process's working /// folder. The topic files will have their content wrapped in a <c><topic></c> tag if needed and /// will be named using their <see cref="Topic.Id" /> value.</remarks> public void CopyContentFiles(BuildProcess builder) { string folder; bool missingFile = false; builder.ReportProgress("Copying standard token shared content file..."); builder.SubstitutionTags.TransformTemplate("HelpFileBuilderTokens.tokens", builder.TemplateFolder, builder.WorkingFolder); builder.ReportProgress("Checking for other token files..."); foreach(var tokenFile in this.tokenFiles) if(!File.Exists(tokenFile.FullPath)) { missingFile = true; builder.ReportProgress(" Missing token file: {0}", tokenFile.FullPath); } else { builder.ReportProgress(" {0} -> {1}", tokenFile.FullPath, Path.Combine(builder.WorkingFolder, Path.GetFileName(tokenFile.FullPath))); builder.SubstitutionTags.TransformTemplate(Path.GetFileName(tokenFile.FullPath), Path.GetDirectoryName(tokenFile.FullPath), builder.WorkingFolder); } if(missingFile) throw new BuilderException("BE0052", "One or more token files could not be found"); builder.ReportProgress("Checking for code snippets files..."); foreach(var snippetsFile in this.codeSnippetFiles) if(!File.Exists(snippetsFile.FullPath)) { missingFile = true; builder.ReportProgress(" Missing code snippets file: {0}", snippetsFile.FullPath); } else builder.ReportProgress(" Found {0}", snippetsFile.FullPath); if(missingFile) throw new BuilderException("BE0053", "One or more code snippets files could not be found"); // Save the image info to a shared content file and copy the image files to the working folder folder = builder.WorkingFolder + "media"; if(!Directory.Exists(folder)) Directory.CreateDirectory(folder); // Create the build process's help format output folders too if needed builder.EnsureOutputFoldersExist("media"); builder.ReportProgress("Copying images and creating the media map file..."); // Copy all image project items and create the content file this.SaveImageSharedContent(builder.WorkingFolder + "_MediaContent_.xml", folder, builder); // Copy the topic files folder = builder.WorkingFolder + "ddueXml"; if(!Directory.Exists(folder)) Directory.CreateDirectory(folder); builder.ReportProgress("Generating conceptual topic files"); // Get the list of valid framework namespaces for the referenced namespace search in each topic HashSet<string> validNamespaces = new HashSet<string>(Directory.EnumerateFiles(builder.FrameworkReflectionDataFolder, "*.xml", SearchOption.AllDirectories).Select(f => Path.GetFileNameWithoutExtension(f))); // Create topic files foreach(TopicCollection tc in topics) { tc.Load(); tc.GenerateConceptualTopics(folder, builder, validNamespaces); } }
/// <summary> /// This method is used to initialize the plug-in at the start of the /// build process. /// </summary> /// <param name="buildProcess">A reference to the current build /// process.</param> /// <param name="configuration">The configuration data that the plug-in /// should use to initialize itself.</param> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { XPathNavigator root; builder = buildProcess; builder.ReportProgress("{0} Version {1}\r\n{2}\r\n", this.Name, this.Version, this.Copyright); root = configuration.SelectSingleNode("configuration"); if(root.IsEmptyElement) throw new BuilderException("ABR0001", "The Assembly Binding " + "Redirection Resolver plug-in has not been configured yet"); // Load the configuration redirects = new BindingRedirectSettingsCollection(); redirects.FromXml(builder.CurrentProject, root); }
/// <summary> /// This method is used to initialize the plug-in at the start of the /// build process. /// </summary> /// <param name="buildProcess">A reference to the current build /// process.</param> /// <param name="configuration">The configuration data that the plug-in /// should use to initialize itself.</param> /// <exception cref="BuilderException">This is thrown if the plug-in /// configuration is not valid.</exception> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { XPathNavigator root, node; string ripOld; builder = buildProcess; allVersions = new VersionSettingsCollection(); uniqueLabels = new List<string>(); builder.ReportProgress("{0} Version {1}\r\n{2}", this.Name, this.Version, this.Copyright); root = configuration.SelectSingleNode("configuration"); if(root.IsEmptyElement) throw new BuilderException("VBP0002", "The Version Builder plug-in has not been configured yet"); // Add an element for the current project. This one won't have // a project to build. currentVersion = new VersionSettings(); allVersions.Add(currentVersion); node = root.SelectSingleNode("currentProject"); if(node != null) { currentVersion.FrameworkLabel = node.GetAttribute("label", String.Empty).Trim(); currentVersion.Version = node.GetAttribute("version", String.Empty).Trim(); ripOld = node.GetAttribute("ripOldApis", String.Empty); // This wasn't in older versions if(!String.IsNullOrEmpty(ripOld)) ripOldApis = Convert.ToBoolean(ripOld, CultureInfo.InvariantCulture); } allVersions.FromXml(builder.CurrentProject, root); // An empty label messes up the HTML so use a single space // if it's blank. if(String.IsNullOrEmpty(currentVersion.FrameworkLabel)) currentVersion.FrameworkLabel = " "; if(node == null || allVersions.Count == 1) throw new BuilderException("VBP0003", "A version value and at least one prior version " + "are required for the Version Builder plug-in."); foreach(VersionSettings vs in allVersions) if(!uniqueLabels.Contains(vs.FrameworkLabel)) uniqueLabels.Add(vs.FrameworkLabel); uniqueLabels.Sort(); }
/// <summary> /// This method is used to initialize the plug-in at the start of the /// build process. /// </summary> /// <param name="buildProcess">A reference to the current build /// process.</param> /// <param name="configuration">The configuration data that the plug-in /// should use to initialize itself.</param> /// <exception cref="BuilderException">This is thrown if the plug-in /// configuration is not valid.</exception> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { XPathNavigator root; builder = buildProcess; otherLinks = new ReferenceLinkSettingsCollection(); builder.ReportProgress("{0} Version {1}\r\n{2}", this.Name, this.Version, this.Copyright); root = configuration.SelectSingleNode("configuration"); if(root.IsEmptyElement) throw new BuilderException("ARL0001", "The Additional Reference " + "Links plug-in has not been configured yet"); // Load the reference links settings otherLinks.FromXml(buildProcess.CurrentProject, root); if(otherLinks.Count == 0) throw new BuilderException("ARL0002", "At least one target is " + "required for the Additional Reference Links plug-in."); }
/// <summary> /// This method is used to initialize the plug-in at the start of the build process /// </summary> /// <param name="buildProcess">A reference to the current build process</param> /// <param name="configuration">The configuration data that the plug-in should use to initialize itself</param> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { XPathNavigator root; string option; builder = buildProcess; minParts = 2; var metadata = (HelpFileBuilderPlugInExportAttribute)this.GetType().GetCustomAttributes( typeof(HelpFileBuilderPlugInExportAttribute), false).First(); builder.ReportProgress("{0} Version {1}\r\n{2}", metadata.Id, metadata.Version, metadata.Copyright); // This plug-in cannot be used if the namespace grouping option is enabled as that replaces this // plug-in. if(builder.CurrentProject.NamespaceGrouping) { if(executionPoints == null) executionPoints = new List<ExecutionPoint>(); else executionPoints.Clear(); builder.ReportWarning("HTP0001", "The project being built has namespace grouping enabled which " + "supersedes the Hierarchical TOC Plug-In. The plug-in will not be used in this build."); return; } // The Hierarchical TOC plug-in is not compatible with MS Help Viewer output. The problem is that // the table of contents is generated off of the help topics when the help viewer file is installed // and, since there are no physical topics for the namespace nodes added to the intermediate TOC file // by the plug-in, they do not appear in the help file. // // The project namespace grouping options supersede this plug-in and fix the above issue as topics // are generated for the namespace group entries. As such, this plug-in is deprecated and will not // receive further changes. It will available for the time being to support the older presentation // styles that do not support MS Help Viewer output or the namespace grouping project options. if((builder.CurrentProject.HelpFileFormat & HelpFileFormats.MSHelpViewer) != 0) { if(executionPoints == null) executionPoints = new List<ExecutionPoint>(); else executionPoints.Clear(); builder.ReportWarning("HTP0002", "This build produces MS Help Viewer output with which the " + "Hierarchical TOC Plug-In is not compatible. It will not be used in this build."); return; } // Load the configuration root = configuration.SelectSingleNode("configuration/toc"); if(root != null) { option = root.GetAttribute("minParts", String.Empty); if(!String.IsNullOrEmpty(option)) minParts = Convert.ToInt32(option, CultureInfo.InvariantCulture); if(minParts < 1) minParts = 1; option = root.GetAttribute("insertBelow", String.Empty); if(!String.IsNullOrEmpty(option)) insertBelow = Convert.ToBoolean(option, CultureInfo.InvariantCulture); } }
/// <summary> /// This method is used to initialize the plug-in at the start of the build process. /// </summary> /// <param name="buildProcess">A reference to the current build process.</param> /// <param name="configuration">The configuration data that the plug-in should use to initialize /// itself.</param> /// <exception cref="BuilderException">This is thrown if the plug-in configuration is not valid.</exception> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { XPathNavigator root; builder = buildProcess; otherLinks = new ReferenceLinkSettingsCollection(); var metadata = (HelpFileBuilderPlugInExportAttribute)this.GetType().GetCustomAttributes( typeof(HelpFileBuilderPlugInExportAttribute), false).First(); builder.ReportProgress("{0} Version {1}\r\n{2}", metadata.Id, metadata.Version, metadata.Copyright); root = configuration.SelectSingleNode("configuration"); if(root.IsEmptyElement) throw new BuilderException("ARL0001", "The Additional Reference Links plug-in has not been " + "configured yet"); // Load the reference links settings otherLinks.FromXml(buildProcess.CurrentProject, root); if(otherLinks.Count == 0) throw new BuilderException("ARL0002", "At least one target is required for the Additional " + "Reference Links plug-in."); }
/// <summary> /// This method is used to initialize the plug-in at the start of the /// build process. /// </summary> /// <param name="buildProcess">A reference to the current build /// process.</param> /// <param name="configuration">The configuration data that the plug-in /// should use to initialize itself.</param> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { builder = buildProcess; builder.ReportProgress("{0} Version {1}\r\n{2}\r\n", this.Name, this.Version, this.Copyright); // TODO: Add your initialization code here such as reading the // configuration data. }
//===================================================================== /// <summary> /// This is used to copy the additional content token, image, and /// topic files to the build folder. /// </summary> /// <param name="builder">The build process</param> /// <remarks>This will copy the code snippet file if specified, save /// token information to a shared content file called <b>_Tokens_.xml</b> /// in the build process's working folder, copy the image files to the /// <b>.\media</b> folder in the build process's working folder, save /// the media map to a file called <b>_MediaContent_.xml</b> in the /// build process's working folder, and save the topic files to the /// <b>.\ddueXml</b> folder in the build process's working folder. /// The topic files will have their content wrapped in a /// <c><topic></c> tag if needed and will be named using their /// <see cref="Topic.Id" /> value.</remarks> public void CopyContentFiles(BuildProcess builder) { string folder; bool missingFile = false; builder.ReportProgress("Copying standard token shared content file..."); builder.TransformTemplate("HelpFileBuilderTokens.tokens", builder.TemplateFolder, builder.WorkingFolder); builder.ReportProgress("Checking for other token files..."); foreach(FileItem tokenFile in this.tokenFiles) if(!File.Exists(tokenFile.FullPath)) { missingFile = true; builder.ReportProgress(" Missing token file: {0}", tokenFile.FullPath); } else { builder.ReportProgress(" {0} -> {1}", tokenFile.FullPath, Path.Combine(builder.WorkingFolder, Path.GetFileName(tokenFile.FullPath))); builder.TransformTemplate(Path.GetFileName(tokenFile.FullPath), Path.GetDirectoryName(tokenFile.FullPath), builder.WorkingFolder); } if(missingFile) throw new BuilderException("BE0052", "One or more token files could not be found"); builder.ReportProgress("Checking for code snippets files..."); foreach(FileItem snippetsFile in this.codeSnippetFiles) if(!File.Exists(snippetsFile.FullPath)) { missingFile = true; builder.ReportProgress(" Missing code snippets file: {0}", snippetsFile.FullPath); } else builder.ReportProgress(" Found {0}", snippetsFile.FullPath); if(missingFile) throw new BuilderException("BE0053", "One or more code snippets files could not be found"); // Save the image info to a shared content file and copy the // image files to the working folder. folder = builder.WorkingFolder + "Media"; if(!Directory.Exists(folder)) Directory.CreateDirectory(folder); // Create the build process's help format output folders too if needed builder.EnsureOutputFoldersExist("media"); builder.ReportProgress("Copying images and creating the media map file..."); // Copy all image project items and create the content file imageFiles.SaveAsSharedContent(builder.WorkingFolder + "_MediaContent_.xml", folder, builder); // Copy the topic files folder = builder.WorkingFolder + "ddueXml"; if(!Directory.Exists(folder)) Directory.CreateDirectory(folder); builder.ReportProgress("Generating conceptual topic files"); // Create topic files foreach(TopicCollection tc in topics) { tc.Load(); tc.GenerateConceptualTopics(folder, builder); } }
/// <summary> /// This method is used to initialize the plug-in at the start of the build process /// </summary> /// <param name="buildProcess">A reference to the current build process</param> /// <param name="configuration">The configuration data that the plug-in should use to initialize itself</param> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { XPathNavigator root, node; builder = buildProcess; var metadata = (HelpFileBuilderPlugInExportAttribute)this.GetType().GetCustomAttributes( typeof(HelpFileBuilderPlugInExportAttribute), false).First(); builder.ReportProgress("{0} Version {1}\r\n{2}", metadata.Id, metadata.Version, metadata.Copyright); root = configuration.SelectSingleNode("configuration"); if(root.IsEmptyElement) throw new BuilderException("DFP0001", "The DBCS Fix plug-in has not been configured yet"); node = root.SelectSingleNode("sbAppLocale"); if(node != null) sbAppLocalePath = node.GetAttribute("path", String.Empty).Trim(); if(String.IsNullOrWhiteSpace(sbAppLocalePath)) { builder.ReportWarning("DFP0002", "A path to the Steel Bytes App Locale tool was not specified " + "and it will not be used for this build."); } else { // If relative, the path is relative to the project folder sbAppLocalePath = FilePath.RelativeToAbsolutePath(builder.ProjectFolder, builder.TransformText(sbAppLocalePath)); if(!File.Exists(sbAppLocalePath)) throw new BuilderException("DFP0003", "Unable to locate SBAppLocale tool at " + sbAppLocalePath); } // If not building HTML Help 1, there's nothing to do if((builder.CurrentProject.HelpFileFormat & HelpFileFormats.HtmlHelp1) == 0) { executionPoints.Clear(); builder.ReportWarning("DFP0007", "An HTML Help 1 file is not being built. This plug-in will " + "not be ran."); } }
/// <summary> /// Create the content metadata file /// </summary> /// <param name="builder">The build process</param> /// <remarks>The content metadata file contains metadata information /// for each topic such as its title, table of contents title, help /// attributes, and index keywords. Help attributes are a combination /// of the project-level help attributes and any parsed from the topic /// file. Any replacement tags in the token values will be replaced /// with the appropriate project values.</remarks> private void CreateConceptualManifest(BuildProcess builder) { XmlWriterSettings settings = new XmlWriterSettings(); XmlWriter writer = null; builder.ReportProgress(" ConceptualManifest.xml"); try { settings.Indent = true; settings.CloseOutput = true; writer = XmlWriter.Create(builder.WorkingFolder + "ConceptualManifest.xml", settings); writer.WriteStartDocument(); writer.WriteStartElement("topics"); foreach(TopicCollection tc in topics) foreach(Topic t in tc) t.WriteManifest(writer, builder); writer.WriteEndElement(); // </topics> writer.WriteEndDocument(); } finally { if(writer != null) writer.Close(); } }
/// <summary> /// Write the image reference collection to a map file ready for use by <strong>BuildAssembler</strong> /// </summary> /// <param name="filename">The file to which the image reference collection is saved</param> /// <param name="imagePath">The path to which the image files should be copied</param> /// <param name="builder">The build process</param> /// <remarks>Images with their <see cref="ImageReference.CopyToMedia" /> property set to true are copied /// to the media folder immediately.</remarks> public void SaveImageSharedContent(string filename, string imagePath, BuildProcess builder) { XmlWriterSettings settings = new XmlWriterSettings(); XmlWriter writer = null; string destFile; builder.EnsureOutputFoldersExist("media"); try { settings.Indent = true; settings.CloseOutput = true; writer = XmlWriter.Create(filename, settings); writer.WriteStartDocument(); // There are normally some attributes on this element but they aren't used by Sandcastle so we'll // ignore them. writer.WriteStartElement("stockSharedContentDefinitions"); foreach(var ir in imageFiles) { writer.WriteStartElement("item"); writer.WriteAttributeString("id", ir.Id); writer.WriteStartElement("image"); // The art build component assumes everything is in a single, common folder. The build // process will ensure that happens. As such, we only need the filename here. writer.WriteAttributeString("file", ir.Filename); if(!String.IsNullOrEmpty(ir.AlternateText)) { writer.WriteStartElement("altText"); writer.WriteValue(ir.AlternateText); writer.WriteEndElement(); } writer.WriteEndElement(); // </image> writer.WriteEndElement(); // </item> destFile = Path.Combine(imagePath, ir.Filename); if(File.Exists(destFile)) builder.ReportWarning("BE0010", "Image file '{0}' already exists. It will be replaced " + "by '{1}'.", destFile, ir.FullPath); builder.ReportProgress(" {0} -> {1}", ir.FullPath, destFile); // The attributes are set to Normal so that it can be deleted after the build File.Copy(ir.FullPath, destFile, true); File.SetAttributes(destFile, FileAttributes.Normal); // Copy it to the output media folders if CopyToMedia is true if(ir.CopyToMedia) foreach(string baseFolder in builder.HelpFormatOutputFolders) { destFile = Path.Combine(baseFolder + "media", ir.Filename); builder.ReportProgress(" {0} -> {1} (Always copied)", ir.FullPath, destFile); File.Copy(ir.FullPath, destFile, true); File.SetAttributes(destFile, FileAttributes.Normal); } } writer.WriteEndElement(); // </stockSharedContentDefinitions> writer.WriteEndDocument(); } finally { if(writer != null) writer.Close(); } }
/// <summary> /// This method is used to initialize the plug-in at the start of the /// build process. /// </summary> /// <param name="buildProcess">A reference to the current build /// process.</param> /// <param name="configuration">The configuration data that the plug-in /// should use to initialize itself.</param> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { builder = buildProcess; builder.ReportProgress("{0} Version {1}\r\n{2}\r\n", this.Name, this.Version, this.Copyright); // TODO: Add your initialization code here such as reading the // configuration data. XPathNavigator gherkinFeaturesConfiguredPath = configuration.SelectSingleNode(xpathGherkinFeaturesPath) ; if (gherkinFeaturesConfiguredPath == null) throw new InvalidOperationException("Could not find on the configuration the path to the features to document. Please, check the " + Name + " plugin configuration."); gherkinFeaturesPath = gherkinFeaturesConfiguredPath.InnerXml; if (!Directory.Exists(gherkinFeaturesPath)) throw new InvalidOperationException("Could not find the configured path to the features to document. Please, check the " + Name + " plugin configuration."); XPathNavigator gherkinFeaturesConfiguredLanguage = configuration.SelectSingleNode(xpathGherkinFeaturesLanguage); if (gherkinFeaturesConfiguredLanguage == null) { builder.ReportProgress("Could not find on the configuration the language of the features to document. Using " + CultureInfo.CurrentCulture.Name + "."); gherkinFeaturesLanguage = CultureInfo.CurrentCulture.Name; } else gherkinFeaturesLanguage = gherkinFeaturesConfiguredLanguage.InnerXml; }
/// <summary> /// Create the content metadata file /// </summary> /// <param name="builder">The build process</param> /// <remarks>The content metadata file contains metadata information for each topic such as its title, /// table of contents title, help attributes, and index keywords. Help attributes are a combination /// of the project-level help attributes and any parsed from the topic file. Any replacement tags in /// the token values will be replaced with the appropriate project values. /// /// <p/>A true MAML version of this file contains several extra attributes. Since Sandcastle doesn't use /// them, I'm not going to waste time adding them. The only stuff written is what is required by /// Sandcastle. In addition, I'm putting the <c>title</c> and <c>PBM_FileVersion</c> item elements in /// here rather than use the separate companion files. They all end up in the metadata section of the /// topic being built so this saves having two extra components in the configuration that do the same /// thing with different files.</remarks> private void CreateContentMetadata(BuildProcess builder) { XmlWriterSettings settings = new XmlWriterSettings(); XmlWriter writer = null; builder.ReportProgress(" _ContentMetadata_.xml"); try { settings.Indent = true; settings.CloseOutput = true; writer = XmlWriter.Create(builder.WorkingFolder + "_ContentMetadata_.xml", settings); writer.WriteStartDocument(); writer.WriteStartElement("metadata"); // Write out each topic and all of its sub-topics foreach(TopicCollection tc in topics) foreach(Topic t in tc) t.WriteMetadata(writer, builder); writer.WriteEndElement(); // </metadata> writer.WriteEndDocument(); } finally { if(writer != null) writer.Close(); } }
/// <summary> /// This method is used to initialize the plug-in at the start of the build process /// </summary> /// <param name="buildProcess">A reference to the current build process</param> /// <param name="configuration">The configuration data that the plug-in should use to initialize itself</param> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { XPathNavigator root, node; builder = buildProcess; var metadata = (HelpFileBuilderPlugInExportAttribute)this.GetType().GetCustomAttributes( typeof(HelpFileBuilderPlugInExportAttribute), false).First(); builder.ReportProgress("{0} Version {1}\r\n{2}", metadata.Id, metadata.Version, metadata.Copyright); // If the file format is Open XML, this plug-in is not supported and will not run if((builder.CurrentProject.HelpFileFormat & HelpFileFormats.OpenXml) != 0) { builder.ReportWarning("BIP0005", "The bibliography plug-in is not supported in the Open XML " + "file format and will not run."); executionPoints = new List<ExecutionPoint>(); return; } root = configuration.SelectSingleNode("configuration"); if(root.IsEmptyElement) throw new BuilderException("BIP0001", "The Bibliography support plug-in has not been " + "configured yet"); node = root.SelectSingleNode("bibliography"); if(node != null) bibliographyFile = node.GetAttribute("path", String.Empty).Trim(); if(String.IsNullOrEmpty(bibliographyFile)) throw new BuilderException("BIP0002", "A path to the bibliography file is required"); // If relative, the path is relative to the project folder bibliographyFile = FilePath.RelativeToAbsolutePath(builder.ProjectFolder, builder.SubstitutionTags.TransformText(bibliographyFile)); if(!File.Exists(bibliographyFile)) throw new BuilderException("BIP0003", "Unable to locate bibliography file at " + bibliographyFile); }
/// <summary> /// This method is used to initialize the plug-in at the start of the build process /// </summary> /// <param name="buildProcess">A reference to the current build process</param> /// <param name="configuration">The configuration data that the plug-in should use to initialize itself</param> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { builder = buildProcess; var metadata = (HelpFileBuilderPlugInExportAttribute)this.GetType().GetCustomAttributes( typeof(HelpFileBuilderPlugInExportAttribute), false).First(); builder.ReportProgress("{0} Version {1}\r\n{2}", metadata.Id, metadata.Version, metadata.Copyright); expressions = new List<MemberIdMatchExpression>(); foreach(XPathNavigator nav in configuration.Select("configuration/expressions/expression")) expressions.Add(new MemberIdMatchExpression { MatchExpression = nav.GetAttribute("matchExpression", String.Empty), ReplacementValue = nav.GetAttribute("replacementValue", String.Empty), MatchAsRegEx = Convert.ToBoolean(nav.GetAttribute("matchAsRegEx", String.Empty)) }); if(expressions.Count == 0) throw new BuilderException("MNF0001", "No fix-up expressions have been defined for the Member " + "Name Fix-Up plug-in"); }
/// <summary> /// This method is used to initialize the plug-in at the start of the build process /// </summary> /// <param name="buildProcess">A reference to the current build process</param> /// <param name="configuration">The configuration data that the plug-in should use to initialize itself</param> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { builder = buildProcess; var metadata = (HelpFileBuilderPlugInExportAttribute)this.GetType().GetCustomAttributes( typeof(HelpFileBuilderPlugInExportAttribute), false).First(); builder.ReportProgress("{0} Version {1}\r\n{2}", metadata.Id, metadata.Version, metadata.Copyright); expressions = new List<string>(); foreach(XPathNavigator nav in configuration.Select("configuration/expressions/expression")) expressions.Add(nav.InnerXml); if(expressions.Count == 0) throw new BuilderException("XRF0001", "No queries have been defined for the XPath Reflection " + "File Filter plug-in"); }
/// <summary> /// This is used to run a step in the build process /// </summary> /// <param name="processFilename">The process to execute.</param> /// <param name="targetFile">An optional target file that the process will operate on such as an MSBuild /// project file.</param> /// <param name="arguments">An optional set of arguments to pass to the process</param> public void Run(string processFilename, string targetFile, string arguments) { Process currentProcess = null; if (processFilename == null) { throw new ArgumentNullException("process"); } currentBuild.ReportProgress("[{0}{1}]", processFilename, !String.IsNullOrWhiteSpace(targetFile) ? " - " + targetFile : String.Empty); if (arguments == null) { arguments = String.Empty; } if (!String.IsNullOrWhiteSpace(targetFile)) { arguments += " " + targetFile; } try { currentProcess = new Process(); errorDetected = false; ProcessStartInfo psi = currentProcess.StartInfo; // Set CreateNoWindow to true to suppress the window rather than setting WindowStyle to hidden as // WindowStyle has no effect on command prompt windows and they always appear. psi.CreateNoWindow = true; psi.FileName = processFilename; psi.Arguments = arguments.Trim(); psi.WorkingDirectory = currentBuild.WorkingFolder; psi.UseShellExecute = false; psi.RedirectStandardOutput = psi.RedirectStandardError = true; currentProcess.Start(); // Spawn two separate tasks so that we can capture both STDOUT and STDERR without the risk of a // deadlock. using (var stdOutReader = Task.Run(() => this.ReadOutputStream(currentProcess.StandardOutput))) using (var stdErrReader = Task.Run(() => this.ReadOutputStream(currentProcess.StandardError))) using (var processWaiter = Task.Run(() => { bool hasExited; do { hasExited = currentProcess.WaitForExit(1000); // Some processes run for a while without reporting any progress. This should allow // for faster cancellation in those situations. if (!hasExited && currentBuild.CancellationToken.IsCancellationRequested) { this.TerminateProcessAndChildren(currentProcess); throw new OperationCanceledException(); } } while(!hasExited); })) { Task.WaitAll(processWaiter, stdOutReader, stdErrReader); } // Stop the build if an error was detected in the process output if (errorDetected) { throw new BuilderException("BE0043", "Unexpected error detected in last build step. See " + "build log for details."); } } catch (AggregateException ex) { this.TerminateProcessAndChildren(currentProcess); var canceledEx = ex.InnerExceptions.FirstOrDefault(e => e is OperationCanceledException); if (canceledEx != null) { throw canceledEx; } throw; } catch { this.TerminateProcessAndChildren(currentProcess); throw; } finally { if (currentProcess != null) { currentProcess.Dispose(); } } }
/// <summary> /// This method is used to initialize the plug-in at the start of the build process /// </summary> /// <param name="buildProcess">A reference to the current build process</param> /// <param name="configuration">The configuration data that the plug-in should use to initialize itself</param> /// <exception cref="BuilderException">This is thrown if the plug-in configuration is not valid</exception> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { XPathNavigator root, node; string ripOld; builder = buildProcess; allVersions = new VersionSettingsCollection(); uniqueLabels = new List<string>(); var metadata = (HelpFileBuilderPlugInExportAttribute)this.GetType().GetCustomAttributes( typeof(HelpFileBuilderPlugInExportAttribute), false).First(); builder.ReportProgress("{0} Version {1}\r\n{2}", metadata.Id, metadata.Version, metadata.Copyright); root = configuration.SelectSingleNode("configuration"); if(root.IsEmptyElement) throw new BuilderException("VBP0001", "The Version Builder plug-in has not been configured yet"); // Add an element for the current project. This one won't have a project to build. currentVersion = new VersionSettings(); allVersions.Add(currentVersion); node = root.SelectSingleNode("currentProject"); if(node != null) { currentVersion.FrameworkLabel = node.GetAttribute("label", String.Empty).Trim(); currentVersion.Version = node.GetAttribute("version", String.Empty).Trim(); currentVersion.RipOldApis = false; ripOld = node.GetAttribute("ripOldApis", String.Empty); // This wasn't in older versions if(!String.IsNullOrEmpty(ripOld)) ripOldApis = Convert.ToBoolean(ripOld, CultureInfo.InvariantCulture); } allVersions.FromXml(builder.CurrentProject, root); // An empty label messes up the HTML so use a single space if it's blank if(String.IsNullOrEmpty(currentVersion.FrameworkLabel)) currentVersion.FrameworkLabel = " "; if(node == null) throw new BuilderException("VBP0002", "A version value is required for the Version Builder plug-in"); if(allVersions.Count == 1) builder.ReportProgress("No other version information was supplied. Only version information " + "for the documented assemblies will be included."); foreach(VersionSettings vs in allVersions) if(!uniqueLabels.Contains(vs.FrameworkLabel)) uniqueLabels.Add(vs.FrameworkLabel); uniqueLabels.Sort(); }
/// <summary> /// This method is used to initialize the plug-in at the start of the build process /// </summary> /// <param name="buildProcess">A reference to the current build process</param> /// <param name="configuration">The configuration data that the plug-in should use to initialize itself</param> /// <exception cref="BuilderException">This is thrown if the plug-in configuration is not valid</exception> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { XPathNavigator root, node; string value; builder = buildProcess; attachLogOnSuccess = false; attachLogOnFailure = true; smtpServer = successEMailAddress = failureEMailAddress = xslTransformFile = String.Empty; credentials = new UserCredentials(); smtpPort = 25; var metadata = (HelpFileBuilderPlugInExportAttribute)this.GetType().GetCustomAttributes( typeof(HelpFileBuilderPlugInExportAttribute), false).First(); builder.ReportProgress("{0} Version {1}\r\n{2}", metadata.Id, metadata.Version, metadata.Copyright); root = configuration.SelectSingleNode("configuration"); if(root.IsEmptyElement) throw new BuilderException("CNP0001", "The Completion Notification plug-in has not been " + "configured yet"); node = root.SelectSingleNode("smtpServer"); if(node != null) { smtpServer = node.GetAttribute("host", String.Empty).Trim(); value = node.GetAttribute("port", String.Empty); if(!Int32.TryParse(value, out smtpPort)) smtpPort = 25; } credentials = UserCredentials.FromXPathNavigator(root); node = root.SelectSingleNode("fromEMail"); if(node != null) fromEMailAddress = node.GetAttribute("address", String.Empty).Trim(); node = root.SelectSingleNode("successEMail"); if(node != null) { successEMailAddress = node.GetAttribute("address", String.Empty).Trim(); attachLogOnSuccess = Convert.ToBoolean(node.GetAttribute("attachLog", String.Empty), CultureInfo.InvariantCulture); } node = root.SelectSingleNode("failureEMail"); if(node != null) { failureEMailAddress = node.GetAttribute("address", String.Empty).Trim(); attachLogOnFailure = Convert.ToBoolean(node.GetAttribute("attachLog", String.Empty), CultureInfo.InvariantCulture); } node = root.SelectSingleNode("xslTransform"); if(node != null) xslTransformFile = builder.TransformText(node.GetAttribute("filename", String.Empty).Trim()); if((!credentials.UseDefaultCredentials && (credentials.UserName.Length == 0 || credentials.Password.Length == 0)) || failureEMailAddress.Length == 0) throw new BuilderException("CNP0002", "The Completion Notification plug-in has an invalid " + "configuration"); }
/// <summary> /// This method is used to initialize the plug-in at the start of the build process /// </summary> /// <param name="buildProcess">A reference to the current build process</param> /// <param name="configuration">The configuration data that the plug-in should use to initialize itself</param> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { builder = buildProcess; var metadata = (HelpFileBuilderPlugInExportAttribute)this.GetType().GetCustomAttributes( typeof(HelpFileBuilderPlugInExportAttribute), false).First(); builder.ReportProgress("{0} Version {1}\r\n{2}", metadata.Id, metadata.Version, metadata.Copyright); XElement root = XElement.Parse(configuration.OuterXml); if(root.IsEmpty) throw new BuilderException("WRP0001", "The Wildcard References plug-in has not been configured yet"); // Load the reference links settings referencePaths = new WildcardReferenceSettingsCollection(); referencePaths.FromXml(buildProcess.CurrentProject, root); if(referencePaths.Count == 0) throw new BuilderException("WRP0002", "At least one reference path is required for the " + "Wildcard References plug-in."); }
/// <summary> /// This method is used to initialize the plug-in at the start of the /// build process. /// </summary> /// <param name="buildProcess">A reference to the current build /// process.</param> /// <param name="configuration">The configuration data that the plug-in /// should use to initialize itself.</param> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { builder = buildProcess; builder.ReportProgress("{0} Version {1}\r\n{2}\r\n", this.Name, this.Version, this.Copyright); expressions = new List<string>(); foreach(XPathNavigator nav in configuration.Select( "configuration/expressions/expression")) expressions.Add(nav.InnerXml); if(expressions.Count == 0) throw new BuilderException("XRF0001", "No queries have been " + "defined for the XPath Reflection File Filter plug-in"); }
/// <summary> /// This method is used to initialize the plug-in at the start of the /// build process. /// </summary> /// <param name="buildProcess">A reference to the current build /// process.</param> /// <param name="configuration">The configuration data that the plug-in /// should use to initialize itself.</param> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { XPathNavigator root; string option; builder = buildProcess; minParts = 2; builder.ReportProgress("{0} Version {1}\r\n{2}", this.Name, this.Version, this.Copyright); // The Hierarchical TOC plug-in is not compatible with MS Help Viewer output // and there is currently no fix available. The problem is that the table of // contents is generated off of the help topics when the help viewer file is // installed and, since there are no physical topics for the namespace nodes // added to the intermediate TOC file by the plug-in, they do not appear in the // help file. Updating the plug-in to support help viewer output would have // required more work than time would allow for this release. If building // other output formats in which you want to use the plug-in, build them // separately from the MS Help Viewer output. if((builder.CurrentProject.HelpFileFormat & HelpFileFormat.MSHelpViewer) != 0) { this.ExecutionPoints.Clear(); builder.ReportWarning("HTP0002", "This build produces MS Help Viewer output with which the " + "Hierarchical TOC Plug-In is not compatible. It will not be used in this build."); } else { // Load the configuration root = configuration.SelectSingleNode("configuration/toc"); if(root != null) { option = root.GetAttribute("minParts", String.Empty); if(!String.IsNullOrEmpty(option)) minParts = Convert.ToInt32(option, CultureInfo.InvariantCulture); if(minParts < 1) minParts = 1; option = root.GetAttribute("insertBelow", String.Empty); if(!String.IsNullOrEmpty(option)) insertBelow = Convert.ToBoolean(option, CultureInfo.InvariantCulture); } } }
/// <summary> /// This method is used to initialize the plug-in at the start of the /// build process. /// </summary> /// <param name="buildProcess">A reference to the current build /// process.</param> /// <param name="configuration">The configuration data that the plug-in /// should use to initialize itself.</param> /// <exception cref="BuilderException">This is thrown if the plug-in /// configuration is not valid.</exception> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { XPathNavigator root, node; builder = buildProcess; ajaxDocUrl = projectName = String.Empty; userCreds = new UserCredentials(); proxyCreds = new ProxyCredentials(); builder.ReportProgress("{0} Version {1}\r\n{2}", this.Name, this.Version, this.Copyright); root = configuration.SelectSingleNode("configuration"); if(root.IsEmptyElement) throw new BuilderException("ADP0001", "The AjaxDoc plug-in " + "has not been configured yet"); node = root.SelectSingleNode("ajaxDoc"); if(node != null) { ajaxDocUrl = node.GetAttribute("url", String.Empty).Trim(); projectName = node.GetAttribute("project", String.Empty).Trim(); regenerateFiles = Convert.ToBoolean(node.GetAttribute( "regenerate", String.Empty), CultureInfo.InvariantCulture); } userCreds = UserCredentials.FromXPathNavigator(root); proxyCreds = ProxyCredentials.FromXPathNavigator(root); if(ajaxDocUrl.Length == 0 || projectName.Length == 0 || (!userCreds.UseDefaultCredentials && (userCreds.UserName.Length == 0 || userCreds.Password.Length == 0)) || (proxyCreds.UseProxyServer && (proxyCreds.ProxyServer == null || (!proxyCreds.Credentials.UseDefaultCredentials && (proxyCreds.Credentials.UserName.Length == 0 || proxyCreds.Credentials.Password.Length == 0))))) throw new BuilderException("ADP0002", "The AjaxDoc plug-in " + "has an invalid configuration"); }
/// <summary> /// This method is used to initialize the plug-in at the start of the /// build process. /// </summary> /// <param name="buildProcess">A reference to the current build /// process.</param> /// <param name="configuration">The configuration data that the plug-in /// should use to initialize itself.</param> public void Initialize(BuildProcess buildProcess, XPathNavigator configuration) { _configuration = XsdPlugInConfiguration.FromXml(buildProcess.CurrentProject, configuration); _buildProcess = buildProcess; _buildProcess.ReportProgress(Resources.PlugInVersionFormatted, Resources.PlugInName, XsdDocMetadata.Version, XsdDocMetadata.Copyright); _tempProject = new SandcastleProject(Path.Combine(_buildProcess.WorkingFolder, "XSDTemp.shfbproj"), false); }