public IComponentKeyPath ParsePossibleKeyPathExtensionElement(IEnumerable <ICompilerExtension> extensions, Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary <string, string> context) { IComponentKeyPath keyPath = null; if (ParseHelper.TryFindExtension(extensions, element.Name.Namespace, out var extension)) { keyPath = extension.ParsePossibleKeyPathElement(intermediate, section, parentElement, element, context); } else { var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(element); this.Messaging.Write(ErrorMessages.UnhandledExtensionElement(childSourceLineNumbers, parentElement.Name.LocalName, element.Name.LocalName, element.Name.NamespaceName)); } return(keyPath); }
/// <summary> /// Processes a child element of a Component for the Compiler. /// </summary> /// <param name="parentElement">Parent element of element to process.</param> /// <param name="element">Element to process.</param> /// <param name="context">Extra information about the context in which this element is being parsed.</param> /// <returns>The component key path type if set.</returns> public override IComponentKeyPath ParsePossibleKeyPathElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary <string, string> context) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(parentElement); IComponentKeyPath keyPath = null; switch (parentElement.Name.LocalName) { case "Component": var componentId = context["ComponentId"]; // 64-bit components may cause issues downlevel. Boolean.TryParse(context["Win64"], out var win64); switch (element.Name.LocalName) { case "Provides": if (win64) { this.Messaging.Write(DependencyWarnings.Win64Component(sourceLineNumbers, componentId)); } keyPath = this.ParseProvidesElement(intermediate, section, element, PackageType.None, componentId); break; default: this.ParseHelper.UnexpectedElement(parentElement, element); break; } break; default: this.ParseHelper.UnexpectedElement(parentElement, element); break; } return(keyPath); }
/// <summary> /// Processes the Provides element. /// </summary> /// <param name="node">The XML node for the Provides element.</param> /// <param name="packageType">The type of the package being chained into a bundle, or "None" if building an MSI package.</param> /// <param name="keyPath">Explicit key path.</param> /// <param name="parentId">The identifier of the parent component or package.</param> /// <returns>The type of key path if set.</returns> private IComponentKeyPath ParseProvidesElement(Intermediate intermediate, IntermediateSection section, XElement node, PackageType packageType, string parentId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); IComponentKeyPath keyPath = null; Identifier id = null; string key = null; string version = null; string displayName = null; int illegalChar = -1; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "Key": key = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Version": version = this.ParseHelper.GetAttributeVersionValue(sourceLineNumbers, attrib); break; case "DisplayName": displayName = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.ParseHelper.UnexpectedAttribute(node, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib); } } // Make sure the key is valid. The key will default to the ProductCode for MSI packages // and the package code for MSP packages in the binder if not specified. if (!String.IsNullOrEmpty(key)) { // Make sure the key does not contain any illegal characters or values. if (0 <= (illegalChar = key.IndexOfAny(DependencyCommon.InvalidCharacters))) { var sb = new StringBuilder(DependencyCommon.InvalidCharacters.Length * 2); Array.ForEach <char>(DependencyCommon.InvalidCharacters, c => sb.Append(c).Append(" ")); this.Messaging.Write(DependencyErrors.IllegalCharactersInProvider(sourceLineNumbers, "Key", key[illegalChar], sb.ToString())); } else if ("ALL" == key) { this.Messaging.Write(DependencyErrors.ReservedValue(sourceLineNumbers, node.Name.LocalName, "Key", key)); } } else if (PackageType.ExePackage == packageType || PackageType.MsuPackage == packageType) { // Must specify the provider key when authored for a package. this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); } else if (PackageType.None == packageType) { // Make sure the ProductCode is authored and set the key. this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.Property, "ProductCode"); key = "!(bind.property.ProductCode)"; } // The Version attribute should not be authored in or for an MSI package. if (!String.IsNullOrEmpty(version)) { switch (packageType) { case PackageType.None: this.Messaging.Write(DependencyWarnings.DiscouragedVersionAttribute(sourceLineNumbers)); break; case PackageType.MsiPackage: this.Messaging.Write(DependencyWarnings.DiscouragedVersionAttribute(sourceLineNumbers, parentId)); break; } } else if (PackageType.MspPackage == packageType || PackageType.MsuPackage == packageType) { // Must specify the Version when authored for packages that do not contain a version. this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version")); } // Need the element ID for child element processing, so generate now if not authored. if (null == id) { id = this.ParseHelper.CreateIdentifier("dep", node.Name.LocalName, parentId, key); } foreach (var child in node.Elements()) { if (this.Namespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "Requires": this.ParseRequiresElement(intermediate, section, child, id.Id, PackageType.None == packageType); break; case "RequiresRef": this.ParseRequiresRefElement(intermediate, section, child, id.Id, PackageType.None == packageType); break; default: this.ParseHelper.UnexpectedElement(node, child); break; } } else { this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, node, child); } } if (!this.Messaging.EncounteredError) { var symbol = section.AddSymbol(new WixDependencyProviderSymbol(sourceLineNumbers, id) { ComponentRef = parentId, ProviderKey = key, }); if (!String.IsNullOrEmpty(version)) { symbol.Version = version; } if (!String.IsNullOrEmpty(displayName)) { symbol.DisplayName = displayName; } if (PackageType.None == packageType) { this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "DependencyCheck", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); // Generate registry rows for the provider using binder properties. var keyProvides = String.Concat(DependencyCommon.RegistryRoot, key); var root = RegistryRootType.MachineUser; var value = "[ProductCode]"; this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, root, keyProvides, null, value, parentId, false); value = !String.IsNullOrEmpty(version) ? version : "[ProductVersion]"; var versionRegistrySymbol = this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, root, keyProvides, "Version", value, parentId, false); value = !String.IsNullOrEmpty(displayName) ? displayName : "[ProductName]"; this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, root, keyProvides, "DisplayName", value, parentId, false); // Use the Version registry value and use that as a potential key path. keyPath = this.CreateComponentKeyPath(); keyPath.Id = versionRegistrySymbol.Id; keyPath.Explicit = false; keyPath.Type = PossibleKeyPathType.Registry; } } return(keyPath); }