internal static void ExpandCustomAttributes(XDocument doc, WixProject project) { foreach (XAttribute instructionAttr in doc.Root.Descendants().Select(x => x.Attribute("WixSharpCustomAttributes")).Where(x => x != null)) { XElement sourceElement = instructionAttr.Parent; foreach (string item in instructionAttr.Value.Split(';')) { if (item.IsNotEmpty()) { if (!ExpandCustomAttribute(sourceElement, item, project)) { var message = "Cannot resolve custom attribute definition:" + item; if (item.StartsWith("Component:")) { message += "\nNote, some Wix elements may not be contained by 'Component' elements (e.g. 'CloseApplication'). " + "Thus attempt to set parent component attribute will always fail.\n"; } throw new ApplicationException(message); } } } instructionAttr.Remove(); } }
internal static void ExpandCustomAttributes(XDocument doc, WixProject project) { foreach (XAttribute instructionAttr in doc.Root.Descendants().Select(x => x.Attribute("WixSharpCustomAttributes")).Where(x => x != null)) { XElement sourceElement = instructionAttr.Parent; foreach (string item in instructionAttr.Value.Split(';')) { if (item.IsNotEmpty()) { if (!ExpandCustomAttribute(sourceElement, item, project)) { throw new ApplicationException("Cannot resolve custom attribute definition:" + item); } } } instructionAttr.Remove(); } }
static bool DefaultExpandCustomAttribute(XElement source, string item, WixProject project) { var attrParts = item.Split(new[] { '=' }, 2, StringSplitOptions.None); // {dep}ProductKey=12345 vs // Component:{dep}ProductKey=12345 vs // {http://schemas.microsoft.com/wix/BalExtension}Overridable=yes" // Component:{dep}elementm_Condition=base64:edr34r34r43 // Note the syntax below is not supported: // Component:{http://schemas.microsoft.com/wix/BalExtension}Overridable=yes" if (item.Contains(":{http:") || item.Contains(":{https:")) { throw new Exception("Syntax `" + item + "` is not supported.\n" + "Use `parent:{alias}attribute=value` instead and add XML namespace with the alias.\n" + "For example: `project.IncludeWixExtension(\"WixDependencyExtension.dll\", \"dep\", expectedNamespace);`"); } string[] keyParts; if (item.StartsWith("{")) { item = ":" + item; } var nameSpec = attrParts.First(); if (nameSpec.StartsWith("{")) { keyParts = new[] { nameSpec } } ; // name specification does not have any `parent prefix` else { keyParts = nameSpec.Split(':'); // here it does } string element = keyParts.First(); string key = keyParts.Last(); string value = attrParts.Last(); if (element == "Component") { XElement destElement = source.Parent("Component"); if (destElement != null) { if (key == "element_Condition") { var name = "Condition"; var data = value; if (data.StartsWith("base64_")) { data = data.Replace("base64_", "").Base64Decode(); } destElement.AddElement(name, null, data); } else { destElement.SetAttribute(key, value); } return(true); } } if (element == "Custom" && source.Name.LocalName == "CustomAction") { string id = source.Attribute("Id").Value; var elements = source.Document.Descendants("Custom").Where(e => e.Attribute("Action").Value == id); if (elements.Any()) { elements.ForEach(e => e.SetAttribute(key, value)); return(true); } } if (key.StartsWith("{")) { source.SetAttribute(key, value); return(true); } if (key.StartsWith("xml_include")) { var parts = value.Split('|'); string parentName = parts[0]; string xmlFile = parts[1]; var placement = source; if (!parentName.IsEmpty()) { placement = source.Parent(parentName); } if (placement != null) { string xmlFilePath; // Strangely enough all relative paths in the wxs are resolved with respect to the // process CurrrentDir but includes it is resolved with respect to the location of the // wxs file containing the include statement. Thus if there is any discrepancy between // source and output dirs then it is safer to use absolute paths instead of relative. if (!xmlFile.IsAbsolutePath() && project.SourceBaseDir != project.OutDir) { xmlFilePath = project.SourceBaseDir.ToAbsolutePath().PathCombine(xmlFile); } else { xmlFilePath = xmlFile; } placement.Add(new XProcessingInstruction("include", xmlFilePath)); return(true); } } return(false); }
static bool DefaultExpandCustomAttribute(XElement source, string item, WixProject project) { var attrParts = item.Split('='); // {dep}ProductKey=12345 vs // Component:{dep}ProductKey=12345 vs // {http://schemas.microsoft.com/wix/BalExtension}Overridable=yes" string[] keyParts; if (item.StartsWith("{")) { item = ":" + item; } var nameSpec = attrParts.First(); if (nameSpec.StartsWith("{")) { keyParts = new[] { nameSpec } } ; // name specification does not have any `parent prefix` else { keyParts = nameSpec.Split(':'); // here it does } string element = keyParts.First(); string key = keyParts.Last(); string value = attrParts.Last(); if (element == "Component") { XElement destElement = source.Parent("Component"); if (destElement != null) { destElement.SetAttribute(key, value); return(true); } } if (element == "Custom" && source.Name.LocalName == "CustomAction") { string id = source.Attribute("Id").Value; var elements = source.Document.Descendants("Custom").Where(e => e.Attribute("Action").Value == id); if (elements.Any()) { elements.ForEach(e => e.SetAttribute(key, value)); return(true); } } if (key.StartsWith("{")) { source.SetAttribute(key, value); return(true); } if (key.StartsWith("xml_include")) { var parts = value.Split('|'); string parentName = parts[0]; string xmlFile = parts[1]; var placement = source; if (!parentName.IsEmpty()) { placement = source.Parent(parentName); } if (placement != null) { string xmlFilePath; // Strangely enough all relative paths in the wxs are resolved with respect to the // process CurrrentDir but includes it is resolved with respect to the location of the // wxs file containing the include statement. Thus if there is any discrepancy between // source and output dirs then it is safer to use absolute paths instead of relative. if (!xmlFile.IsAbsolutePath() && project.SourceBaseDir != project.OutDir) { xmlFilePath = project.SourceBaseDir.ToAbsolutePath().PathCombine(xmlFile); } else { xmlFilePath = xmlFile; } placement.Add(new XProcessingInstruction("include", xmlFilePath)); return(true); } } return(false); }