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 +++++++++++++++++++++ src/ext/PowerShell/wixext/PSErrors.cs | 30 +++ src/ext/PowerShell/wixext/PSExtensionData.cs | 30 +++ src/ext/PowerShell/wixext/PSWarnings.cs | 30 +++ .../wixext/PowerShellExtensionFactory.cs | 17 ++ .../wixext/WixToolset.PowerShell.wixext.csproj | 30 +++ .../wixext/WixToolset.PowerShell.wixext.targets | 11 + 7 files changed, 433 insertions(+) create mode 100644 src/ext/PowerShell/wixext/PSCompiler.cs create mode 100644 src/ext/PowerShell/wixext/PSErrors.cs create mode 100644 src/ext/PowerShell/wixext/PSExtensionData.cs create mode 100644 src/ext/PowerShell/wixext/PSWarnings.cs create mode 100644 src/ext/PowerShell/wixext/PowerShellExtensionFactory.cs create mode 100644 src/ext/PowerShell/wixext/WixToolset.PowerShell.wixext.csproj create mode 100644 src/ext/PowerShell/wixext/WixToolset.PowerShell.wixext.targets (limited to 'src/ext/PowerShell/wixext') 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); + } + } +} diff --git a/src/ext/PowerShell/wixext/PSErrors.cs b/src/ext/PowerShell/wixext/PSErrors.cs new file mode 100644 index 00000000..704cf5cd --- /dev/null +++ b/src/ext/PowerShell/wixext/PSErrors.cs @@ -0,0 +1,30 @@ +// 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.Resources; + using WixToolset.Data; + + public static class PSErrors + { + public static Message NeitherIdSpecified(SourceLineNumber sourceLineNumbers, string element) + { + return Message(sourceLineNumbers, Ids.NeitherIdSpecified, "Either the {0}/@FileId attribute must be specified if nested under a SnapIn element, or the {0}/@SnapIn attribute must be specified if nested under under a File element.", element); + } + + private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) + { + return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args); + } + + private static Message Message(SourceLineNumber sourceLineNumber, Ids id, ResourceManager resourceManager, string resourceName, params object[] args) + { + return new Message(sourceLineNumber, MessageLevel.Error, (int)id, resourceManager, resourceName, args); + } + + public enum Ids + { + NeitherIdSpecified = 5301, + } + } +} diff --git a/src/ext/PowerShell/wixext/PSExtensionData.cs b/src/ext/PowerShell/wixext/PSExtensionData.cs new file mode 100644 index 00000000..66627942 --- /dev/null +++ b/src/ext/PowerShell/wixext/PSExtensionData.cs @@ -0,0 +1,30 @@ +// 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 WixToolset.Data; + using WixToolset.Extensibility; + + /// + /// The WiX Toolset PowerShell Extension. + /// + public sealed class PSExtensionData : BaseExtensionData + { + /// + /// Gets the default culture. + /// + /// The default culture. + public override string DefaultCulture => "en-US"; + + public override Intermediate GetLibrary(ISymbolDefinitionCreator symbolDefinitions) + { + return Intermediate.Load(typeof(PSExtensionData).Assembly, "WixToolset.PowerShell.powershell.wixlib", symbolDefinitions); + } + + public override bool TryGetSymbolDefinitionByName(string name, out IntermediateSymbolDefinition symbolDefinition) + { + symbolDefinition = null; + return symbolDefinition != null; + } + } +} diff --git a/src/ext/PowerShell/wixext/PSWarnings.cs b/src/ext/PowerShell/wixext/PSWarnings.cs new file mode 100644 index 00000000..9be14948 --- /dev/null +++ b/src/ext/PowerShell/wixext/PSWarnings.cs @@ -0,0 +1,30 @@ +// 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.Resources; + using WixToolset.Data; + + public static class PSWarnings + { + public static Message DeprecatedAssemblyNameAttribute(SourceLineNumber sourceLineNumbers) + { + return Message(sourceLineNumbers, Ids.DeprecatedAssemblyNameAttribute, "The SnapIn/@AssemblyName attribute is deprecated. It is assigned automatically."); + } + + private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) + { + return new Message(sourceLineNumber, MessageLevel.Warning, (int)id, format, args); + } + + private static Message Message(SourceLineNumber sourceLineNumber, Ids id, ResourceManager resourceManager, string resourceName, params object[] args) + { + return new Message(sourceLineNumber, MessageLevel.Warning, (int)id, resourceManager, resourceName, args); + } + + public enum Ids + { + DeprecatedAssemblyNameAttribute = 5350, + } + } +} diff --git a/src/ext/PowerShell/wixext/PowerShellExtensionFactory.cs b/src/ext/PowerShell/wixext/PowerShellExtensionFactory.cs new file mode 100644 index 00000000..44f836e0 --- /dev/null +++ b/src/ext/PowerShell/wixext/PowerShellExtensionFactory.cs @@ -0,0 +1,17 @@ +// 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 WixToolset.Extensibility; + + public class PowerShellExtensionFactory : BaseExtensionFactory + { + protected override IReadOnlyCollection ExtensionTypes => new[] + { + typeof(PSCompiler), + typeof(PSExtensionData), + }; + } +} diff --git a/src/ext/PowerShell/wixext/WixToolset.PowerShell.wixext.csproj b/src/ext/PowerShell/wixext/WixToolset.PowerShell.wixext.csproj new file mode 100644 index 00000000..a89a574c --- /dev/null +++ b/src/ext/PowerShell/wixext/WixToolset.PowerShell.wixext.csproj @@ -0,0 +1,30 @@ + + + + + + netstandard2.0 + WixToolset.PowerShell + WiX Toolset PowerShell Extension + WiX Toolset PowerShell Extension + true + build + + + + + + + + + + + + + + + + + + + diff --git a/src/ext/PowerShell/wixext/WixToolset.PowerShell.wixext.targets b/src/ext/PowerShell/wixext/WixToolset.PowerShell.wixext.targets new file mode 100644 index 00000000..bf06e1e4 --- /dev/null +++ b/src/ext/PowerShell/wixext/WixToolset.PowerShell.wixext.targets @@ -0,0 +1,11 @@ + + + + + + $(MSBuildThisFileDirectory)..\tools\WixToolset.PowerShell.wixext.dll + + + + + -- cgit v1.2.3-55-g6feb