/// <summary> /// Load the build components found in the Components and Plug-Ins /// folder and its subfolders. /// </summary> private static void LoadBuildComponents() { List <string> allFiles = new List <string>(); XPathDocument configFile; XPathNavigator navConfig; BuildComponentInfo info; string componentPath; SetPaths(); buildComponents = new Dictionary <string, BuildComponentInfo>(); // Give precedence to components in the optional SHFBCOMPONENTROOT // environment variable folder. componentPath = Environment.ExpandEnvironmentVariables("%SHFBCOMPONENTROOT%"); if (!String.IsNullOrEmpty(componentPath) && Directory.Exists(componentPath)) { allFiles.AddRange(Directory.GetFiles(componentPath, "*.components", SearchOption.AllDirectories)); } // Add the standard component config file and any third-party // component config files in the installation folder. This // allows for XCOPY deployments of SHFB to build servers. allFiles.AddRange(Directory.GetFiles(shfbFolder, "*.components", SearchOption.AllDirectories)); // Finally, check the common app data build components folder if (Directory.Exists(buildComponentFolder)) { allFiles.AddRange(Directory.GetFiles(buildComponentFolder, "*.components", SearchOption.AllDirectories)); } foreach (string file in allFiles) { configFile = new XPathDocument(file); navConfig = configFile.CreateNavigator(); foreach (XPathNavigator component in navConfig.Select("components/component")) { info = new BuildComponentInfo(component); // Ignore components with duplicate IDs if (!buildComponents.ContainsKey(info.Id)) { buildComponents.Add(info.Id, info); } } } }
/// <summary> /// This handles merging of the custom component configurations into /// the configuration file including dependencies. /// </summary> /// <param name="id">The ID of the component to merge</param> /// <param name="info">The build component definition</param> /// <param name="rootNode">The root container node</param> /// <param name="configNode">The configuration node to merge</param> /// <param name="isConceptualConfig">True if this is a conceptual /// content configuration file or false if it is a reference build /// configuration file.</param> private void MergeComponent(string id, BuildComponentInfo info, XmlNode rootNode, XmlNode configNode, bool isConceptualConfig) { BuildComponentInfo depInfo; ComponentPosition position; XmlNodeList matchingNodes; XmlNode node; string replaceId; // Merge dependent component configurations first if(info.Dependencies.Count != 0) foreach(string dependency in info.Dependencies) { node = rootNode.SelectSingleNode("component[@id='" + dependency + "']"); // If it's already there or would create a circular // dependency, ignore it. if(node != null || mergeStack.Contains(dependency)) continue; // Add the dependency with a default configuration if(!BuildComponentManager.BuildComponents.TryGetValue(dependency, out depInfo)) throw new BuilderException("BE0023", String.Format( CultureInfo.InvariantCulture, "The project contains " + "a reference to a custom build component '{0}' that " + "has a dependency '{1}' that could not be found.", id, dependency)); node = rootNode.OwnerDocument.CreateDocumentFragment(); node.InnerXml = reField.Replace(depInfo.DefaultConfiguration, fieldMatchEval); this.ReportProgress(" Merging '{0}' dependency for '{1}'", dependency, id); mergeStack.Push(dependency); this.MergeComponent(dependency, depInfo, rootNode, node, isConceptualConfig); mergeStack.Pop(); } position = (!isConceptualConfig) ? info.ReferenceBuildPosition : info.ConceptualBuildPosition; // Find all matching components by ID or type name if(!String.IsNullOrEmpty(position.Id)) { replaceId = position.Id; matchingNodes = rootNode.SelectNodes("component[@id='" + replaceId + "']"); } else { replaceId = position.TypeName; matchingNodes = rootNode.SelectNodes("component[@type='" + replaceId + "']"); } // If replacing another component, search for that by ID or // type and replace it if found. if(position.Place == ComponentPosition.Placement.Replace) { if(matchingNodes.Count < position.AdjustedInstance) { this.ReportProgress(" Could not find configuration '{0}' (instance {1}) to replace with " + "configuration for '{2}' so it will be omitted.", replaceId, position.AdjustedInstance, id); // If it's a dependency, that's a problem if(mergeStack.Count != 0) throw new BuilderException("BE0024", "Unable to add dependent configuration: " + id); return; } rootNode.ReplaceChild(configNode, matchingNodes[position.AdjustedInstance - 1]); this.ReportProgress(" Replaced configuration for '{0}' (instance {1}) with configuration " + "for '{2}'", replaceId, position.AdjustedInstance, id); // Adjust instance values on matching components foreach(BuildComponentInfo component in BuildComponentManager.BuildComponents.Values) if(!isConceptualConfig) { if(((!String.IsNullOrEmpty(component.ReferenceBuildPosition.Id) && component.ReferenceBuildPosition.Id == replaceId) || (String.IsNullOrEmpty(component.ReferenceBuildPosition.Id) && component.ReferenceBuildPosition.TypeName == replaceId)) && component.ReferenceBuildPosition.AdjustedInstance > position.AdjustedInstance) component.ReferenceBuildPosition.AdjustedInstance--; } else if(((!String.IsNullOrEmpty(component.ConceptualBuildPosition.Id) && component.ConceptualBuildPosition.Id == replaceId) || (String.IsNullOrEmpty(component.ConceptualBuildPosition.Id) && component.ConceptualBuildPosition.TypeName == replaceId)) && component.ConceptualBuildPosition.AdjustedInstance > position.AdjustedInstance) component.ConceptualBuildPosition.AdjustedInstance--; return; } // See if the configuration already exists. If so, replace it. // We'll assume it's already in the correct location. node = rootNode.SelectSingleNode("component[@id='" + id + "']"); if(node != null) { this.ReportProgress(" Replacing default configuration for '{0}' with the custom configuration", id); rootNode.ReplaceChild(configNode, node); return; } // Create the node and add it in the correct location switch(position.Place) { case ComponentPosition.Placement.Start: rootNode.InsertBefore(configNode, rootNode.ChildNodes[0]); this.ReportProgress(" Added configuration for '{0}' to the start of the configuration file", id); break; case ComponentPosition.Placement.End: rootNode.InsertAfter(configNode, rootNode.ChildNodes[rootNode.ChildNodes.Count - 1]); this.ReportProgress(" Added configuration for '{0}' to the end of the configuration file", id); break; case ComponentPosition.Placement.Before: if(matchingNodes.Count < position.AdjustedInstance) this.ReportProgress(" Could not find configuration '{0}' (instance {1}) to add " + "configuration for '{2}' so it will be omitted.", replaceId, position.AdjustedInstance, id); else { rootNode.InsertBefore(configNode, matchingNodes[position.AdjustedInstance - 1]); this.ReportProgress(" Added configuration for '{0}' before '{1}' (instance {2})", id, replaceId, position.AdjustedInstance); } break; default: // After if(matchingNodes.Count < position.AdjustedInstance) this.ReportProgress(" Could not find configuration '{0}' (instance {1}) to add " + "configuration for '{2}' so it will be omitted.", replaceId, position.AdjustedInstance, id); else { rootNode.InsertAfter(configNode, matchingNodes[position.AdjustedInstance - 1]); this.ReportProgress(" Added configuration for '{0}' after '{1}' (instance {2})", id, replaceId, position.AdjustedInstance); } break; } }
/// <summary> /// Load the build components found in the Components and Plug-Ins /// folder and its subfolders. /// </summary> private static void LoadBuildComponents() { List<string> allFiles = new List<string>(); XPathDocument configFile; XPathNavigator navConfig; BuildComponentInfo info; string componentPath; SetPaths(); buildComponents = new Dictionary<string, BuildComponentInfo>(); // Give precedence to components in the optional SHFBCOMPONENTROOT // environment variable folder. componentPath = Environment.ExpandEnvironmentVariables("%SHFBCOMPONENTROOT%"); if(!String.IsNullOrEmpty(componentPath) && Directory.Exists(componentPath)) allFiles.AddRange(Directory.GetFiles(componentPath, "*.components", SearchOption.AllDirectories)); // Add the standard component config file and any third-party // component config files in the installation folder. This // allows for XCOPY deployments of SHFB to build servers. allFiles.AddRange(Directory.GetFiles(shfbFolder, "*.components", SearchOption.AllDirectories)); // Finally, check the common app data build components folder if(Directory.Exists(buildComponentFolder)) allFiles.AddRange(Directory.GetFiles(buildComponentFolder, "*.components", SearchOption.AllDirectories)); foreach(string file in allFiles) { configFile = new XPathDocument(file); navConfig = configFile.CreateNavigator(); foreach(XPathNavigator component in navConfig.Select("components/component")) { info = new BuildComponentInfo(component); // Ignore components with duplicate IDs if(!buildComponents.ContainsKey(info.Id)) buildComponents.Add(info.Id, info); } } }