From 59765d27eb205b7b62a5057cfb631caee97f0af6 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 4 May 2021 22:49:35 -0700 Subject: Move PowerShell.wixext into ext --- src/ext/PowerShell/wixext/PSCompiler.cs | 285 ++++++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 src/ext/PowerShell/wixext/PSCompiler.cs (limited to 'src/ext/PowerShell/wixext/PSCompiler.cs') diff --git a/src/ext/PowerShell/wixext/PSCompiler.cs b/src/ext/PowerShell/wixext/PSCompiler.cs new file mode 100644 index 00000000..37591282 --- /dev/null +++ b/src/ext/PowerShell/wixext/PSCompiler.cs @@ -0,0 +1,285 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.PowerShell +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Xml.Linq; + using WixToolset.Data; + using WixToolset.Data.Symbols; + using WixToolset.Extensibility; + + /// + /// The compiler for the WiX Toolset PowerShell Extension. + /// + public sealed class PSCompiler : BaseCompilerExtension + { + private const string KeyFormat = @"SOFTWARE\Microsoft\PowerShell\{0}\PowerShellSnapIns\{1}"; + private const string VarPrefix = "PSVersionMajor"; + + public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/powershell"; + + /// + /// Processes an element for the Compiler. + /// + /// Source line number for the parent element. + /// Parent element of element to process. + /// Element to process. + /// Extra information about the context in which this element is being parsed. + public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary context) + { + switch (parentElement.Name.LocalName) + { + case "File": + var fileId = context["FileId"]; + var componentId = context["ComponentId"]; + + switch (element.Name.LocalName) + { + case "FormatsFile": + this.ParseExtensionsFile(intermediate, section, element, "Formats", fileId, componentId); + break; + + case "SnapIn": + this.ParseSnapInElement(intermediate, section, element, fileId, componentId); + break; + + case "TypesFile": + this.ParseExtensionsFile(intermediate, section, element, "Types", fileId, componentId); + break; + + default: + this.ParseHelper.UnexpectedElement(parentElement, element); + break; + } + break; + + default: + this.ParseHelper.UnexpectedElement(parentElement, element); + break; + } + } + + /// + /// Parses a SnapIn element. + /// + /// Element to parse. + /// Identifier for parent file. + /// Identifier for parent component. + private void ParseSnapInElement(Intermediate intermediate, IntermediateSection section, XElement node, string fileId, string componentId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); + string id = null; + string customSnapInType = null; + string description = null; + string descriptionIndirect = null; + var requiredPowerShellVersion = CompilerConstants.IllegalVersion; + string vendor = null; + string vendorIndirect = null; + string version = null; + + 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.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + + case "CustomSnapInType": + customSnapInType = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + + case "Description": + description = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + + case "DescriptionIndirect": + descriptionIndirect = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + + case "RequiredPowerShellVersion": + var ver = this.ParseHelper.GetAttributeVersionValue(sourceLineNumbers, attrib); + requiredPowerShellVersion = new Version(ver); + break; + + case "Vendor": + vendor = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + + case "VendorIndirect": + vendorIndirect = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + + case "Version": + version = this.ParseHelper.GetAttributeVersionValue(sourceLineNumbers, attrib); + break; + + default: + this.ParseHelper.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib); + } + } + + if (null == id) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); + } + + // Default to require PowerShell 1.0. + if (CompilerConstants.IllegalVersion == requiredPowerShellVersion) + { + requiredPowerShellVersion = new Version(1, 0); + } + + // If the snap-in version isn't explicitly specified, get it + // from the assembly version at bind time. + if (null == version) + { + version = String.Format("!(bind.assemblyVersion.{0})", fileId); + } + + foreach (var child in node.Elements()) + { + if (this.Namespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "FormatsFile": + this.ParseExtensionsFile(intermediate, section, child, "Formats", id, componentId); + break; + case "TypesFile": + this.ParseExtensionsFile(intermediate, section, child, "Types", id, componentId); + break; + default: + this.ParseHelper.UnexpectedElement(node, child); + break; + } + } + else + { + this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, node, child); + } + } + + // Get the major part of the required PowerShell version which is + // needed for the registry key, and put that into a WiX variable + // for use in Formats and Types files. PowerShell v2 still uses 1. + var major = (2 == requiredPowerShellVersion.Major) ? 1 : requiredPowerShellVersion.Major; + + var variableId = new Identifier(AccessModifier.Global, String.Format(CultureInfo.InvariantCulture, "{0}_{1}", VarPrefix, id)); + section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, variableId) + { + Value = major.ToString(CultureInfo.InvariantCulture), + Overridable = false, + }); + + var registryRoot = RegistryRootType.LocalMachine; // HKLM + var registryKey = String.Format(CultureInfo.InvariantCulture, KeyFormat, major, id); + + this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "ApplicationBase", String.Format(CultureInfo.InvariantCulture, "[${0}]", componentId), componentId, false); + + // set the assembly name automatically when binding. + // processorArchitecture is not handled correctly by PowerShell v1.0 + // so format the assembly name explicitly. + var assemblyName = String.Format(CultureInfo.InvariantCulture, "!(bind.assemblyName.{0}), Version=!(bind.assemblyVersion.{0}), Culture=!(bind.assemblyCulture.{0}), PublicKeyToken=!(bind.assemblyPublicKeyToken.{0})", fileId); + this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "AssemblyName", assemblyName, componentId, false); + + if (null != customSnapInType) + { + this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "CustomPSSnapInType", customSnapInType, componentId, false); + } + + if (null != description) + { + this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "Description", description, componentId, false); + } + + if (null != descriptionIndirect) + { + this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "DescriptionIndirect", descriptionIndirect, componentId, false); + } + + this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "ModuleName", String.Format(CultureInfo.InvariantCulture, "[#{0}]", fileId), componentId, false); + + this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "PowerShellVersion", requiredPowerShellVersion.ToString(2), componentId, false); + + if (null != vendor) + { + this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "Vendor", vendor, componentId, false); + } + + if (null != vendorIndirect) + { + this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "VendorIndirect", vendorIndirect, componentId, false); + } + + if (null != version) + { + this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "Version", version, componentId, false); + } + } + + /// + /// Parses a FormatsFile and TypesFile element. + /// + /// Element to parse. + /// Registry value name. + /// Idendifier for parent file or snap-in. + /// Identifier for parent component. + private void ParseExtensionsFile(Intermediate intermediate, IntermediateSection section, XElement node, string valueName, string id, string componentId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); + string fileId = null; + string snapIn = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "FileId": + fileId = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + snapIn = id; + break; + + case "SnapIn": + fileId = id; + snapIn = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + + default: + this.ParseHelper.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib); + } + } + + if (null == fileId && null == snapIn) + { + this.Messaging.Write(PSErrors.NeitherIdSpecified(sourceLineNumbers, valueName)); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node); + + var registryRoot = RegistryRootType.LocalMachine; // HKLM + var registryKey = String.Format(CultureInfo.InvariantCulture, KeyFormat, String.Format(CultureInfo.InvariantCulture, "!(wix.{0}_{1})", VarPrefix, snapIn), snapIn); + + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.File, fileId); + this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, valueName, String.Format(CultureInfo.InvariantCulture, "[~][#{0}]", fileId), componentId, false); + } + } +} -- cgit v1.2.3-55-g6feb