summaryrefslogtreecommitdiff
path: root/src/ext/PowerShell/wixext
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext/PowerShell/wixext')
-rw-r--r--src/ext/PowerShell/wixext/PSCompiler.cs285
-rw-r--r--src/ext/PowerShell/wixext/PSErrors.cs30
-rw-r--r--src/ext/PowerShell/wixext/PSExtensionData.cs30
-rw-r--r--src/ext/PowerShell/wixext/PSWarnings.cs30
-rw-r--r--src/ext/PowerShell/wixext/PowerShellExtensionFactory.cs17
-rw-r--r--src/ext/PowerShell/wixext/WixToolset.PowerShell.wixext.csproj30
-rw-r--r--src/ext/PowerShell/wixext/WixToolset.PowerShell.wixext.targets11
7 files changed, 433 insertions, 0 deletions
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 @@
1// 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.
2
3namespace WixToolset.PowerShell
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Globalization;
8 using System.Xml.Linq;
9 using WixToolset.Data;
10 using WixToolset.Data.Symbols;
11 using WixToolset.Extensibility;
12
13 /// <summary>
14 /// The compiler for the WiX Toolset PowerShell Extension.
15 /// </summary>
16 public sealed class PSCompiler : BaseCompilerExtension
17 {
18 private const string KeyFormat = @"SOFTWARE\Microsoft\PowerShell\{0}\PowerShellSnapIns\{1}";
19 private const string VarPrefix = "PSVersionMajor";
20
21 public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/powershell";
22
23 /// <summary>
24 /// Processes an element for the Compiler.
25 /// </summary>
26 /// <param name="sourceLineNumbers">Source line number for the parent element.</param>
27 /// <param name="parentElement">Parent element of element to process.</param>
28 /// <param name="element">Element to process.</param>
29 /// <param name="contextValues">Extra information about the context in which this element is being parsed.</param>
30 public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary<string, string> context)
31 {
32 switch (parentElement.Name.LocalName)
33 {
34 case "File":
35 var fileId = context["FileId"];
36 var componentId = context["ComponentId"];
37
38 switch (element.Name.LocalName)
39 {
40 case "FormatsFile":
41 this.ParseExtensionsFile(intermediate, section, element, "Formats", fileId, componentId);
42 break;
43
44 case "SnapIn":
45 this.ParseSnapInElement(intermediate, section, element, fileId, componentId);
46 break;
47
48 case "TypesFile":
49 this.ParseExtensionsFile(intermediate, section, element, "Types", fileId, componentId);
50 break;
51
52 default:
53 this.ParseHelper.UnexpectedElement(parentElement, element);
54 break;
55 }
56 break;
57
58 default:
59 this.ParseHelper.UnexpectedElement(parentElement, element);
60 break;
61 }
62 }
63
64 /// <summary>
65 /// Parses a SnapIn element.
66 /// </summary>
67 /// <param name="node">Element to parse.</param>
68 /// <param name="fileId">Identifier for parent file.</param>
69 /// <param name="componentId">Identifier for parent component.</param>
70 private void ParseSnapInElement(Intermediate intermediate, IntermediateSection section, XElement node, string fileId, string componentId)
71 {
72 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node);
73 string id = null;
74 string customSnapInType = null;
75 string description = null;
76 string descriptionIndirect = null;
77 var requiredPowerShellVersion = CompilerConstants.IllegalVersion;
78 string vendor = null;
79 string vendorIndirect = null;
80 string version = null;
81
82 foreach (var attrib in node.Attributes())
83 {
84 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
85 {
86 switch (attrib.Name.LocalName)
87 {
88 case "Id":
89 id = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
90 break;
91
92 case "CustomSnapInType":
93 customSnapInType = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
94 break;
95
96 case "Description":
97 description = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
98 break;
99
100 case "DescriptionIndirect":
101 descriptionIndirect = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
102 break;
103
104 case "RequiredPowerShellVersion":
105 var ver = this.ParseHelper.GetAttributeVersionValue(sourceLineNumbers, attrib);
106 requiredPowerShellVersion = new Version(ver);
107 break;
108
109 case "Vendor":
110 vendor = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
111 break;
112
113 case "VendorIndirect":
114 vendorIndirect = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
115 break;
116
117 case "Version":
118 version = this.ParseHelper.GetAttributeVersionValue(sourceLineNumbers, attrib);
119 break;
120
121 default:
122 this.ParseHelper.UnexpectedAttribute(node, attrib);
123 break;
124 }
125 }
126 else
127 {
128 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib);
129 }
130 }
131
132 if (null == id)
133 {
134 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
135 }
136
137 // Default to require PowerShell 1.0.
138 if (CompilerConstants.IllegalVersion == requiredPowerShellVersion)
139 {
140 requiredPowerShellVersion = new Version(1, 0);
141 }
142
143 // If the snap-in version isn't explicitly specified, get it
144 // from the assembly version at bind time.
145 if (null == version)
146 {
147 version = String.Format("!(bind.assemblyVersion.{0})", fileId);
148 }
149
150 foreach (var child in node.Elements())
151 {
152 if (this.Namespace == child.Name.Namespace)
153 {
154 switch (child.Name.LocalName)
155 {
156 case "FormatsFile":
157 this.ParseExtensionsFile(intermediate, section, child, "Formats", id, componentId);
158 break;
159 case "TypesFile":
160 this.ParseExtensionsFile(intermediate, section, child, "Types", id, componentId);
161 break;
162 default:
163 this.ParseHelper.UnexpectedElement(node, child);
164 break;
165 }
166 }
167 else
168 {
169 this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, node, child);
170 }
171 }
172
173 // Get the major part of the required PowerShell version which is
174 // needed for the registry key, and put that into a WiX variable
175 // for use in Formats and Types files. PowerShell v2 still uses 1.
176 var major = (2 == requiredPowerShellVersion.Major) ? 1 : requiredPowerShellVersion.Major;
177
178 var variableId = new Identifier(AccessModifier.Global, String.Format(CultureInfo.InvariantCulture, "{0}_{1}", VarPrefix, id));
179 section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, variableId)
180 {
181 Value = major.ToString(CultureInfo.InvariantCulture),
182 Overridable = false,
183 });
184
185 var registryRoot = RegistryRootType.LocalMachine; // HKLM
186 var registryKey = String.Format(CultureInfo.InvariantCulture, KeyFormat, major, id);
187
188 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "ApplicationBase", String.Format(CultureInfo.InvariantCulture, "[${0}]", componentId), componentId, false);
189
190 // set the assembly name automatically when binding.
191 // processorArchitecture is not handled correctly by PowerShell v1.0
192 // so format the assembly name explicitly.
193 var assemblyName = String.Format(CultureInfo.InvariantCulture, "!(bind.assemblyName.{0}), Version=!(bind.assemblyVersion.{0}), Culture=!(bind.assemblyCulture.{0}), PublicKeyToken=!(bind.assemblyPublicKeyToken.{0})", fileId);
194 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "AssemblyName", assemblyName, componentId, false);
195
196 if (null != customSnapInType)
197 {
198 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "CustomPSSnapInType", customSnapInType, componentId, false);
199 }
200
201 if (null != description)
202 {
203 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "Description", description, componentId, false);
204 }
205
206 if (null != descriptionIndirect)
207 {
208 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "DescriptionIndirect", descriptionIndirect, componentId, false);
209 }
210
211 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "ModuleName", String.Format(CultureInfo.InvariantCulture, "[#{0}]", fileId), componentId, false);
212
213 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "PowerShellVersion", requiredPowerShellVersion.ToString(2), componentId, false);
214
215 if (null != vendor)
216 {
217 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "Vendor", vendor, componentId, false);
218 }
219
220 if (null != vendorIndirect)
221 {
222 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "VendorIndirect", vendorIndirect, componentId, false);
223 }
224
225 if (null != version)
226 {
227 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, "Version", version, componentId, false);
228 }
229 }
230
231 /// <summary>
232 /// Parses a FormatsFile and TypesFile element.
233 /// </summary>
234 /// <param name="node">Element to parse.</param>
235 /// <param name="valueName">Registry value name.</param>
236 /// <param name="id">Idendifier for parent file or snap-in.</param>
237 /// <param name="componentId">Identifier for parent component.</param>
238 private void ParseExtensionsFile(Intermediate intermediate, IntermediateSection section, XElement node, string valueName, string id, string componentId)
239 {
240 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node);
241 string fileId = null;
242 string snapIn = null;
243
244 foreach (var attrib in node.Attributes())
245 {
246 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
247 {
248 switch (attrib.Name.LocalName)
249 {
250 case "FileId":
251 fileId = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
252 snapIn = id;
253 break;
254
255 case "SnapIn":
256 fileId = id;
257 snapIn = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
258 break;
259
260 default:
261 this.ParseHelper.UnexpectedAttribute(node, attrib);
262 break;
263 }
264 }
265 else
266 {
267 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib);
268 }
269 }
270
271 if (null == fileId && null == snapIn)
272 {
273 this.Messaging.Write(PSErrors.NeitherIdSpecified(sourceLineNumbers, valueName));
274 }
275
276 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node);
277
278 var registryRoot = RegistryRootType.LocalMachine; // HKLM
279 var registryKey = String.Format(CultureInfo.InvariantCulture, KeyFormat, String.Format(CultureInfo.InvariantCulture, "!(wix.{0}_{1})", VarPrefix, snapIn), snapIn);
280
281 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.File, fileId);
282 this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, registryRoot, registryKey, valueName, String.Format(CultureInfo.InvariantCulture, "[~][#{0}]", fileId), componentId, false);
283 }
284 }
285}
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 @@
1// 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.
2
3namespace WixToolset.PowerShell
4{
5 using System.Resources;
6 using WixToolset.Data;
7
8 public static class PSErrors
9 {
10 public static Message NeitherIdSpecified(SourceLineNumber sourceLineNumbers, string element)
11 {
12 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);
13 }
14
15 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
16 {
17 return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args);
18 }
19
20 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, ResourceManager resourceManager, string resourceName, params object[] args)
21 {
22 return new Message(sourceLineNumber, MessageLevel.Error, (int)id, resourceManager, resourceName, args);
23 }
24
25 public enum Ids
26 {
27 NeitherIdSpecified = 5301,
28 }
29 }
30}
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 @@
1// 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.
2
3namespace WixToolset.PowerShell
4{
5 using WixToolset.Data;
6 using WixToolset.Extensibility;
7
8 /// <summary>
9 /// The WiX Toolset PowerShell Extension.
10 /// </summary>
11 public sealed class PSExtensionData : BaseExtensionData
12 {
13 /// <summary>
14 /// Gets the default culture.
15 /// </summary>
16 /// <value>The default culture.</value>
17 public override string DefaultCulture => "en-US";
18
19 public override Intermediate GetLibrary(ISymbolDefinitionCreator symbolDefinitions)
20 {
21 return Intermediate.Load(typeof(PSExtensionData).Assembly, "WixToolset.PowerShell.powershell.wixlib", symbolDefinitions);
22 }
23
24 public override bool TryGetSymbolDefinitionByName(string name, out IntermediateSymbolDefinition symbolDefinition)
25 {
26 symbolDefinition = null;
27 return symbolDefinition != null;
28 }
29 }
30}
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 @@
1// 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.
2
3namespace WixToolset.PowerShell
4{
5 using System.Resources;
6 using WixToolset.Data;
7
8 public static class PSWarnings
9 {
10 public static Message DeprecatedAssemblyNameAttribute(SourceLineNumber sourceLineNumbers)
11 {
12 return Message(sourceLineNumbers, Ids.DeprecatedAssemblyNameAttribute, "The SnapIn/@AssemblyName attribute is deprecated. It is assigned automatically.");
13 }
14
15 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
16 {
17 return new Message(sourceLineNumber, MessageLevel.Warning, (int)id, format, args);
18 }
19
20 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, ResourceManager resourceManager, string resourceName, params object[] args)
21 {
22 return new Message(sourceLineNumber, MessageLevel.Warning, (int)id, resourceManager, resourceName, args);
23 }
24
25 public enum Ids
26 {
27 DeprecatedAssemblyNameAttribute = 5350,
28 }
29 }
30}
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 @@
1// 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.
2
3namespace WixToolset.PowerShell
4{
5 using System;
6 using System.Collections.Generic;
7 using WixToolset.Extensibility;
8
9 public class PowerShellExtensionFactory : BaseExtensionFactory
10 {
11 protected override IReadOnlyCollection<Type> ExtensionTypes => new[]
12 {
13 typeof(PSCompiler),
14 typeof(PSExtensionData),
15 };
16 }
17}
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 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- 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. -->
3
4<Project Sdk="Microsoft.NET.Sdk">
5 <PropertyGroup>
6 <TargetFramework>netstandard2.0</TargetFramework>
7 <RootNamespace>WixToolset.PowerShell</RootNamespace>
8 <Description>WiX Toolset PowerShell Extension</Description>
9 <Title>WiX Toolset PowerShell Extension</Title>
10 <IsTool>true</IsTool>
11 <ContentTargetFolders>build</ContentTargetFolders>
12 </PropertyGroup>
13
14 <ItemGroup>
15 <Content Include="$(MSBuildThisFileName).targets" />
16 <EmbeddedResource Include="$(OutputPath)..\powershell.wixlib" />
17 </ItemGroup>
18
19 <ItemGroup>
20 <PackageReference Include="WixToolset.Extensibility" Version="4.0.*" PrivateAssets="all" />
21 </ItemGroup>
22
23 <ItemGroup>
24 <ProjectReference Include="..\wixlib\powershell.wixproj" ReferenceOutputAssembly="false" Condition=" '$(NCrunch)'=='' " />
25 </ItemGroup>
26
27 <ItemGroup>
28 <PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37" PrivateAssets="all" />
29 </ItemGroup>
30</Project>
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 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- 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. -->
3
4<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
5 <PropertyGroup>
6 <WixToolsetPowerShellWixextPath Condition=" '$(WixToolsetPowerShellWixextPath)' == '' ">$(MSBuildThisFileDirectory)..\tools\WixToolset.PowerShell.wixext.dll</WixToolsetPowerShellWixextPath>
7 </PropertyGroup>
8 <ItemGroup>
9 <WixExtension Include="$(WixToolsetPowerShellWixextPath)" />
10 </ItemGroup>
11</Project>