summaryrefslogtreecommitdiff
path: root/src/ext/PowerShell/wixext/PSCompiler.cs
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-05-04 22:49:35 -0700
committerRob Mensching <rob@firegiant.com>2021-05-04 22:49:35 -0700
commit59765d27eb205b7b62a5057cfb631caee97f0af6 (patch)
tree0ef38df9efc55be04565b56eb9408d649862c16d /src/ext/PowerShell/wixext/PSCompiler.cs
parent0a7de80d773dba9d93b64015934a92fd908824d7 (diff)
downloadwix-59765d27eb205b7b62a5057cfb631caee97f0af6.tar.gz
wix-59765d27eb205b7b62a5057cfb631caee97f0af6.tar.bz2
wix-59765d27eb205b7b62a5057cfb631caee97f0af6.zip
Move PowerShell.wixext into ext
Diffstat (limited to 'src/ext/PowerShell/wixext/PSCompiler.cs')
-rw-r--r--src/ext/PowerShell/wixext/PSCompiler.cs285
1 files changed, 285 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}