/// <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(ExecutionContext context) { XmlDocument toc; XPathNavigator root, navToc, tocEntry, tocParent; bool hasParent; // Scan the XML comments files. The files aren't available soon // after this step and by now everything that other plug-ins may // have added to the collection should be there. if(context.BuildStep == BuildStep.CopyStandardContent) { builder.ReportProgress("Searching for comment members containing <tocexclude />..."); foreach(XmlCommentsFile f in builder.CommentsFiles) foreach(XmlNode member in f.Members.SelectNodes("member[count(.//tocexclude) > 0]/@name")) exclusionList.Add(member.Value); builder.ReportProgress("Found {0} members to exclude from the TOC", exclusionList.Count); return; } if(exclusionList.Count == 0) { builder.ReportProgress("No members found to exclude"); return; } builder.ReportProgress("Removing members from the TOC"); toc = new XmlDocument(); toc.Load(builder.WorkingFolder + "toc.xml"); navToc = toc.CreateNavigator(); // If a root namespace container node is present, we need to look // in it rather than the document root node. root = navToc.SelectSingleNode("topics/topic[starts-with(@id, 'R:')]"); if(root == null) root = navToc.SelectSingleNode("topics"); foreach(string id in exclusionList) { tocEntry = root.SelectSingleNode("//topic[@id='" + id + "']"); // Ignore if null, it was probably excluded by the API filter if(tocEntry != null) { // Remove the entry. If this results in the parent // being an empty node, remove it too. do { tocParent = tocEntry.Clone(); hasParent = tocParent.MoveToParent(); builder.ReportProgress(" Removing '{0}'", tocEntry.GetAttribute("id", String.Empty)); tocEntry.DeleteSelf(); } while(hasParent && !tocParent.HasChildren); } } toc.Save(builder.WorkingFolder + "toc.xml"); }
/// <summary> /// Execute all plug-ins that need to execute in the given build step /// that have the given execution behavior. /// </summary> /// <param name="behavior">The execution behavior</param> /// <returns>True if at least one plug-in was executed or false if /// no plug-ins were executed.</returns> /// <remarks>Plug-ins will execute based on their execution priority. /// Those with a higher priority value will execute before those with /// a lower value. Plug-ins with identical priority values may execute /// in any order within their group.</remarks> private bool ExecutePlugIns(ExecutionBehaviors behavior) { List<IPlugIn> executeList; ExecutionContext context; BuildStep step; int numberExecuted = 0; if(loadedPlugIns == null) return false; executeList = new List<IPlugIn>(); step = progressArgs.BuildStep; // Find plug-ins that need to be executed foreach(IPlugIn plugIn in loadedPlugIns.Values) if(plugIn.ExecutionPoints.RunsAt(step, behavior)) executeList.Add(plugIn); if(executeList.Count == 0) return false; // Sort by execution priority in descending order executeList.Sort( delegate(IPlugIn x, IPlugIn y) { return y.ExecutionPoints.PriorityFor(step, behavior) - x.ExecutionPoints.PriorityFor(step, behavior); }); context = new ExecutionContext(step, behavior); foreach(IPlugIn plugIn in executeList) { try { // Wrap plug-in output in an element so that it // can be formatted differently. swLog.WriteLine("<plugIn name=\"{0}\" " + "behavior=\"{1}\" priority=\"{2}\">", plugIn.Name, behavior, plugIn.ExecutionPoints.PriorityFor(step, behavior)); context.Executed = true; plugIn.Execute(context); swLog.Write("</plugIn>"); } catch(Exception ex) { swLog.WriteLine("</plugIn>"); throw new BuilderException("BE0029", "Unexpected " + "error while executing plug-in '" + plugIn.Name + "': " + ex.ToString(), ex); } if(context.Executed) numberExecuted++; } return (numberExecuted != 0); }
/// <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(ExecutionContext context) { XmlDocument refInfo = new XmlDocument(); XmlNodeList nodes; refInfo.Load(builder.ReflectionInfoFilename); foreach(string expression in expressions) { builder.ReportProgress("Removing items matching '{0}'", expression); nodes = refInfo.SelectNodes(expression); foreach(XmlNode node in nodes) node.ParentNode.RemoveChild(node); builder.ReportProgress(" Removed {0} items", nodes.Count); } refInfo.Save(builder.ReflectionInfoFilename); }
/// <summary> /// This method is used to execute the plug-in during the build process /// </summary> /// <param name="context">The current execution context</param> /// <remarks>Since this runs after completion of the build and the /// log file is closed, any progress messages reported here will not /// appear in it, just in the output window on the main form.</remarks> public void Execute(ExecutionContext context) { MailMessage msg = null; string logFilename = null; // There is nothing to do on completion if there is no success // e-mail address. if(context.BuildStep == BuildStep.Completed && successEMailAddress.Length == 0) { context.Executed = false; return; } try { logFilename = builder.LogFilename; // Run the log file through an XSL transform first? if(!String.IsNullOrEmpty(xslTransformFile) && File.Exists(logFilename)) logFilename = this.TransformLogFile(); msg = new MailMessage(); msg.IsBodyHtml = false; msg.Subject = String.Format(CultureInfo.InvariantCulture, "Build {0}: {1}", context.BuildStep, builder.ProjectFilename); if(fromEMailAddress.Length != 0) msg.From = new MailAddress(fromEMailAddress); else msg.From = new MailAddress("*****@*****.**"); if(context.BuildStep == BuildStep.Completed) { msg.To.Add(successEMailAddress); if(attachLogOnSuccess && File.Exists(logFilename)) msg.Attachments.Add(new Attachment(logFilename)); } else { msg.To.Add(failureEMailAddress); if(attachLogOnFailure && File.Exists(logFilename)) msg.Attachments.Add(new Attachment(logFilename)); } msg.Body = String.Format(CultureInfo.InvariantCulture, "Build {0}: {1}{2}\r\nBuild output is located at {3}\r\n", context.BuildStep, builder.ProjectFolder, builder.ProjectFilename, builder.OutputFolder); if(context.BuildStep != BuildStep.Completed || builder.CurrentProject.KeepLogFile) msg.Body += "Build details can be found in the log file " + builder.LogFilename + "\r\n"; SmtpClient smtp = new SmtpClient(); if(smtpServer.Length != 0) { smtp.Host = smtpServer; smtp.Port = smtpPort; } if(!credentials.UseDefaultCredentials) smtp.Credentials = new NetworkCredential( credentials.UserName, credentials.Password); smtp.Send(msg); builder.ReportProgress("The build notification e-mail was " + "sent successfully to {0}", msg.To[0].Address); } catch(FormatException) { builder.ReportProgress("Failed to send build notification " + "e-mail! The e-mail addresses '{0}' appears to be " + "invalid.", msg.To[0]); } catch(SmtpFailedRecipientException recipEx) { builder.ReportProgress("Failed to send build notification " + "e-mail! A problem occurred trying to send the e-mail " + "to the recipient '{0}': {1}", recipEx.FailedRecipient, recipEx.Message); } catch(SmtpException smtpEx) { System.Diagnostics.Debug.WriteLine(smtpEx.ToString()); builder.ReportProgress("Failed to send build notification " + "e-mail! A problem occurred trying to connect to the " + "e-mail server. Details:\r\n{0}\r\n", smtpEx.ToString()); } finally { if(msg != null) msg.Dispose(); // Delete the transformed log file if it exists if(!String.IsNullOrEmpty(logFilename) && logFilename.EndsWith(".html", StringComparison.OrdinalIgnoreCase)) File.Delete(logFilename); } }
/// <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(ExecutionContext context) { builder.ReportProgress("Multilanguage Documentation Support ..."); if (targetLanguage == null || targetLanguage.Length == 0) { builder.ReportProgress("No target language was specified."); goto DONE; } builder.ReportProgress("Target language = {0}", targetLanguage); foreach (XmlCommentsFile commentsFile in builder.CommentsFiles) { // Collect elements which have language tags. List<XmlNode> docNodeList = new List<XmlNode>(); foreach (XmlNode docNode in commentsFile.Comments.SelectNodes("doc/members/member//*[en|ja|" + targetLanguage + "]")) { docNodeList.Add(docNode); } // Process each element. bool modified = false; foreach (XmlNode docNode in docNodeList) { // Collect language tags. List<XmlNode> langNodes = new List<XmlNode>(); bool hasEnglishDoc = false; bool hasTargetDoc = false; foreach (XmlNode langNode in docNode.ChildNodes) { if (IsLangTag(langNode)) { langNodes.Add(langNode); if (langNode.LocalName == ENGLISH_TAG_NAME) hasEnglishDoc = true; if (langNode.LocalName == targetLanguage) hasTargetDoc = true; } } // Determine which language to leave. string languageToLeave; if (hasTargetDoc) { languageToLeave = targetLanguage; } else if (hasEnglishDoc) { languageToLeave = ENGLISH_TAG_NAME; builder.ReportProgress("Missing target language ... use {0} : {1}", languageToLeave, GetMemberName(docNode)); } else if (langNodes.Count > 0) { languageToLeave = langNodes[0].LocalName; } else { languageToLeave = null; builder.ReportProgress("No language tag : {0}", GetMemberName(docNode)); } // Strip targeted language tag, and remove other language tags. if (languageToLeave != null) { foreach (XmlNode langNode in langNodes) { if (langNode.LocalName == languageToLeave) { StripLangTag(docNode, langNode); modified = true; } else { docNode.RemoveChild(langNode); modified = true; } } } } if (modified) { commentsFile.Save(); } } DONE: builder.ReportProgress("Multilanguage Documentation Support Done."); }
/// <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(ExecutionContext context) { builder.ReportProgress("Creating Features documentation..."); var contentGenerator = new ContentGenerator(builder, gherkinFeaturesPath, new CultureInfo(gherkinFeaturesLanguage)); contentGenerator.Generate(); AddLinkedItem(BuildAction.ContentLayout, contentGenerator.ContentFile); foreach (var topic in contentGenerator.Topics) { AddTopicItemToProject(BuildAction.None, topic); } var componentConfig = GetComponentConfiguration(contentGenerator.TopicIndexPath); builder.CurrentProject.ComponentConfigurations.Add(GetComponentId(), true, componentConfig); builder.CurrentProject.MSBuildProject.ReevaluateIfNecessary(); }
/// <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(ExecutionContext context) { List<string> namespaceList = new List<string>(); Dictionary<string, XmlNode> namespaceNodes = new Dictionary<string, XmlNode>(); XmlDocument toc; XPathNavigator root, navToc; XmlAttribute attr; XmlNode tocEntry, tocParent; string[] parts; string name, parent, topicTitle; int parentIdx, childIdx, entriesAdded; builder.ReportProgress("Retrieving namespace topic title from shared content..."); toc = new XmlDocument(); toc.Load(builder.PresentationStyleFolder + @"content\reference_content.xml"); tocEntry = toc.SelectSingleNode("content/item[@id='namespaceTopicTitle']"); if(tocEntry != null) topicTitle = tocEntry.InnerText; else { builder.ReportWarning("HTP0001", "Unable to locate namespace " + "topic title in reference content file. Using default."); topicTitle = "{0} Namespace"; } builder.ReportProgress("Creating namespace hierarchy..."); toc = new XmlDocument(); toc.Load(builder.WorkingFolder + "toc.xml"); navToc = toc.CreateNavigator(); // Get a list of the namespaces. If a root namespace container // node is present, we need to look in it rather than the document // root node. root = navToc.SelectSingleNode("topics/topic[starts-with(@id, 'R:')]"); if(root == null) root = navToc.SelectSingleNode("topics"); foreach(XPathNavigator ns in root.Select("topic[starts-with(@id, 'N:')]")) { name = ns.GetAttribute("id", String.Empty); namespaceList.Add(name); namespaceNodes.Add(name, ((IHasXmlNode)ns).GetNode()); } // See if any container nodes need to be created for namespaces // with a common root name. for(parentIdx = 0; parentIdx < namespaceList.Count; parentIdx++) { parts = namespaceList[parentIdx].Split('.'); // Only do it for namespaces with a minimum number of parts if(parts.Length > minParts) { for(childIdx = minParts; childIdx < parts.Length; childIdx++) { name = String.Join(".", parts, 0, childIdx); if(!namespaceList.Contains(name)) { if(namespaceList.FindAll( ns => ns.StartsWith(name + ".", StringComparison.Ordinal)).Count > 0) { // The nodes will be created later once // we know where to insert them. namespaceList.Add(name); namespaceNodes.Add(name, null); } } } } } // Sort them in reverse order namespaceList.Sort((n1, n2) => String.Compare(n2, n1, StringComparison.Ordinal)); // If any container namespaces were added, create nodes for them // and insert them before the namespace ahead of them in the list. foreach(string key in namespaceList) if(namespaceNodes[key] == null) { tocEntry = toc.CreateElement("topic"); attr = toc.CreateAttribute("id"); attr.Value = String.Format(CultureInfo.InvariantCulture, topicTitle, (key.Length > 2) ? key.Substring(2) : "Global"); tocEntry.Attributes.Append(attr); parentIdx = namespaceList.IndexOf(key); tocParent = namespaceNodes[namespaceList[parentIdx - 1]]; tocParent.ParentNode.InsertBefore(tocEntry, tocParent); namespaceNodes[key] = tocEntry; } for(parentIdx = 1; parentIdx < namespaceList.Count; parentIdx++) { parent = namespaceList[parentIdx]; entriesAdded = 0; // Check each preceding namespace. If it starts with the // parent's name, insert it as a child of that one. for(childIdx = 0; childIdx < parentIdx; childIdx++) { name = namespaceList[childIdx]; if(name.StartsWith(parent + ".", StringComparison.Ordinal)) { tocEntry = namespaceNodes[name]; tocParent = namespaceNodes[parent]; if(insertBelow && entriesAdded < tocParent.ChildNodes.Count) tocParent.InsertAfter(tocEntry, tocParent.ChildNodes[ tocParent.ChildNodes.Count - entriesAdded - 1]); else tocParent.InsertBefore(tocEntry, tocParent.ChildNodes[0]); namespaceList.RemoveAt(childIdx); entriesAdded++; parentIdx--; childIdx--; } } } toc.Save(builder.WorkingFolder + "toc.xml"); }
/// <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(ExecutionContext context) { XmlDocument config = new XmlDocument(); XmlAttribute attr; XmlNode resolver, ddue; config.Load(builder.WorkingFolder + "MRefBuilder.config"); resolver = config.SelectSingleNode("configuration/dduetools/resolver"); if(resolver == null) { ddue = config.SelectSingleNode("configuration/dduetools"); if(ddue == null) throw new BuilderException("ABR0002", "Unable to locate " + "configuration/dduetools or its child resolver " + "element in MRefBuilder.config"); builder.ReportProgress("Default resolver element not found, " + "adding new element"); resolver = config.CreateNode(XmlNodeType.Element, "resolver", null); ddue.AppendChild(resolver); attr = config.CreateAttribute("type"); resolver.Attributes.Append(attr); attr = config.CreateAttribute("assembly"); resolver.Attributes.Append(attr); attr = config.CreateAttribute("use-gac"); attr.Value = "false"; resolver.Attributes.Append(attr); } resolver.Attributes["type"].Value = "SandcastleBuilder.Components.BindingRedirectResolver"; resolver.Attributes["assembly"].Value = builder.TransformText( "{@SHFBFolder}SandcastleBuilder.MRefBuilder.dll"); builder.ReportProgress("Default resolver replaced with '{0}' in '{1}'", resolver.Attributes["type"].Value, resolver.Attributes["assembly"].Value); builder.ReportProgress("Adding redirects:"); foreach(BindingRedirectSettings brs in redirects) builder.ReportProgress(" {0}", brs); redirects.ToXml(config, resolver); config.Save(builder.WorkingFolder + "MRefBuilder.config"); }
/// <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(ExecutionContext context) { // TODO: Add your execution code here }
/// <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(ExecutionContext context) { XmlDocument config; XPathNavigator navConfig; List<XPathNavigator> deleteTargets = new List<XPathNavigator>(); // Create a dummy reflection.org and reflection.xml file if(context.BuildStep == BuildStep.GenerateReflectionInfo) { // Allow Before step plug-ins to run builder.ExecuteBeforeStepPlugIns(); using(StreamWriter sw = new StreamWriter(builder.ReflectionInfoFilename, false, Encoding.UTF8)) { sw.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); sw.WriteLine("<reflection>"); sw.WriteLine(" <assemblies/>"); sw.WriteLine(" <apis>"); sw.WriteLine(" <api id=\"N:\"/>"); sw.WriteLine(" </apis>"); sw.WriteLine("</reflection>"); } File.Copy(builder.ReflectionInfoFilename, Path.ChangeExtension(builder.ReflectionInfoFilename, ".xml"), true); // Allow After step plug-ins to run builder.ExecuteAfterStepPlugIns(); } else if(context.BuildStep == BuildStep.MergeCustomConfigs && builder.CurrentProject.HasItems(BuildAction.ContentLayout)) { // Remove the reflection.xml file from the conceptual // configuration file since it isn't valid. config = new XmlDocument(); config.Load(builder.WorkingFolder + "conceptual.config"); navConfig = config.CreateNavigator(); XPathNodeIterator allTargets = navConfig.Select("//targets[@files='reflection.xml']"); foreach(XPathNavigator target in allTargets) deleteTargets.Add(target); // Get rid of the framework targets too in preview build. // This can knock about 20 seconds off the build time. if(isPreviewBuild) { allTargets = navConfig.Select("//targets[contains(@base, 'Data\\Reflection')]"); foreach(XPathNavigator target in allTargets) deleteTargets.Add(target); } foreach(var t in deleteTargets) t.DeleteSelf(); config.Save(builder.WorkingFolder + "conceptual.config"); } // Ignore all other the steps }
/// <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(ExecutionContext context) { // Deploy each of the selected help file formats if(builder.CurrentFormat == HelpFileFormat.HtmlHelp1) { builder.ReportProgress("Deploying HTML Help 1 file"); this.DeployOutput(builder.Help1Files, deployHelp1); } if(builder.CurrentFormat == HelpFileFormat.MSHelp2) { builder.ReportProgress("Deploying MS Help 2 files"); this.DeployOutput(builder.Help2Files, deployHelp2); } if(builder.CurrentFormat == HelpFileFormat.MSHelpViewer) { builder.ReportProgress("Deploying MS Help Viewer files"); this.DeployOutput(builder.HelpViewerFiles, deployHelpViewer); } if(builder.CurrentFormat == HelpFileFormat.Website) { builder.ReportProgress("Deploying website files"); this.DeployOutput(builder.WebsiteFiles, deployWebsite); } }
/// <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(ExecutionContext context) { string configFilename; // Merge the reflection file info into conceptual.config configFilename = builder.WorkingFolder + "conceptual.config"; if(File.Exists(configFilename)) this.AddBibliographyParameter(configFilename); // Merge the reflection file info into sancastle.config configFilename = builder.WorkingFolder + "sandcastle.config"; if(File.Exists(configFilename)) this.AddBibliographyParameter(configFilename); }