aboutsummaryrefslogtreecommitdiff
path: root/src/wixext/PSCompiler.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/wixext/PSCompiler.cs')
-rw-r--r--src/wixext/PSCompiler.cs290
1 files changed, 290 insertions, 0 deletions
diff --git a/src/wixext/PSCompiler.cs b/src/wixext/PSCompiler.cs
new file mode 100644
index 00000000..61eb287c
--- /dev/null
+++ b/src/wixext/PSCompiler.cs
@@ -0,0 +1,290 @@
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.Extensions
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.Rows;
11 using WixToolset.Extensibility;
12
13 /// <summary>
14 /// The compiler for the WiX Toolset Internet Information Services Extension.
15 /// </summary>
16 public sealed class PSCompiler : CompilerExtension
17 {
18 private const string KeyFormat = @"SOFTWARE\Microsoft\PowerShell\{0}\PowerShellSnapIns\{1}";
19 private const string VarPrefix = "PSVersionMajor";
20
21 /// <summary>
22 /// Instantiate a new PSCompiler.
23 /// </summary>
24 public PSCompiler()
25 {
26 this.Namespace = "http://wixtoolset.org/schemas/v4/wxs/powershell";
27 }
28
29 /// <summary>
30 /// Processes an element for the Compiler.
31 /// </summary>
32 /// <param name="sourceLineNumbers">Source line number for the parent element.</param>
33 /// <param name="parentElement">Parent element of element to process.</param>
34 /// <param name="element">Element to process.</param>
35 /// <param name="contextValues">Extra information about the context in which this element is being parsed.</param>
36 public override void ParseElement(XElement parentElement, XElement element, IDictionary<string, string> context)
37 {
38 switch (parentElement.Name.LocalName)
39 {
40 case "File":
41 string fileId = context["FileId"];
42 string componentId = context["ComponentId"];
43
44 switch (element.Name.LocalName)
45 {
46 case "FormatsFile":
47 this.ParseExtensionsFile(element, "Formats", fileId, componentId);
48 break;
49
50 case "SnapIn":
51 this.ParseSnapInElement(element, fileId, componentId);
52 break;
53
54 case "TypesFile":
55 this.ParseExtensionsFile(element, "Types", fileId, componentId);
56 break;
57
58 default:
59 this.Core.UnexpectedElement(parentElement, element);
60 break;
61 }
62 break;
63
64 default:
65 this.Core.UnexpectedElement(parentElement, element);
66 break;
67 }
68 }
69
70 /// <summary>
71 /// Parses a SnapIn element.
72 /// </summary>
73 /// <param name="node">Element to parse.</param>
74 /// <param name="fileId">Identifier for parent file.</param>
75 /// <param name="componentId">Identifier for parent component.</param>
76 private void ParseSnapInElement(XElement node, string fileId, string componentId)
77 {
78 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
79 string id = null;
80 string assemblyName = null;
81 string customSnapInType = null;
82 string description = null;
83 string descriptionIndirect = null;
84 Version requiredPowerShellVersion = CompilerConstants.IllegalVersion;
85 string vendor = null;
86 string vendorIndirect = null;
87 string version = null;
88
89 foreach (XAttribute attrib in node.Attributes())
90 {
91 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
92 {
93 switch (attrib.Name.LocalName)
94 {
95 case "Id":
96 id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
97 break;
98
99 case "CustomSnapInType":
100 customSnapInType = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
101 break;
102
103 case "Description":
104 description = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
105 break;
106
107 case "DescriptionIndirect":
108 descriptionIndirect = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
109 break;
110
111 case "RequiredPowerShellVersion":
112 string ver = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
113 requiredPowerShellVersion = new Version(ver);
114 break;
115
116 case "Vendor":
117 vendor = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
118 break;
119
120 case "VendorIndirect":
121 vendorIndirect = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
122 break;
123
124 case "Version":
125 version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
126 break;
127
128 default:
129 this.Core.UnexpectedAttribute(node, attrib);
130 break;
131 }
132 }
133 else
134 {
135 this.Core.ParseExtensionAttribute(node, attrib);
136 }
137 }
138
139 if (null == id)
140 {
141 this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
142 }
143
144 // Default to require PowerShell 1.0.
145 if (CompilerConstants.IllegalVersion == requiredPowerShellVersion)
146 {
147 requiredPowerShellVersion = new Version(1, 0);
148 }
149
150 // If the snap-in version isn't explicitly specified, get it
151 // from the assembly version at bind time.
152 if (null == version)
153 {
154 version = String.Format("!(bind.assemblyVersion.{0})", fileId);
155 }
156
157 foreach (XElement child in node.Elements())
158 {
159 if (this.Namespace == child.Name.Namespace)
160 {
161 switch (child.Name.LocalName)
162 {
163 case "FormatsFile":
164 this.ParseExtensionsFile(child, "Formats", id, componentId);
165 break;
166 case "TypesFile":
167 this.ParseExtensionsFile(child, "Types", id, componentId);
168 break;
169 default:
170 this.Core.UnexpectedElement(node, child);
171 break;
172 }
173 }
174 else
175 {
176 this.Core.ParseExtensionElement(node, child);
177 }
178 }
179
180 // Get the major part of the required PowerShell version which is
181 // needed for the registry key, and put that into a WiX variable
182 // for use in Formats and Types files. PowerShell v2 still uses 1.
183 int major = (2 == requiredPowerShellVersion.Major) ? 1 : requiredPowerShellVersion.Major;
184
185 WixVariableRow wixVariableRow = (WixVariableRow)this.Core.CreateRow(sourceLineNumbers, "WixVariable");
186 wixVariableRow.Id = String.Format(CultureInfo.InvariantCulture, "{0}_{1}", VarPrefix, id);
187 wixVariableRow.Value = major.ToString(CultureInfo.InvariantCulture);
188 wixVariableRow.Overridable = false;
189
190 int registryRoot = 2; // HKLM
191 string registryKey = String.Format(CultureInfo.InvariantCulture, KeyFormat, major, id);
192
193 this.Core.CreateRegistryRow(sourceLineNumbers, registryRoot, registryKey, "ApplicationBase", String.Format(CultureInfo.InvariantCulture, "[${0}]", componentId), componentId, false);
194
195 // set the assembly name automatically when binding.
196 // processorArchitecture is not handled correctly by PowerShell v1.0
197 // so format the assembly name explicitly.
198 assemblyName = String.Format(CultureInfo.InvariantCulture, "!(bind.assemblyName.{0}), Version=!(bind.assemblyVersion.{0}), Culture=!(bind.assemblyCulture.{0}), PublicKeyToken=!(bind.assemblyPublicKeyToken.{0})", fileId);
199 this.Core.CreateRegistryRow(sourceLineNumbers, registryRoot, registryKey, "AssemblyName", assemblyName, componentId, false);
200
201 if (null != customSnapInType)
202 {
203 this.Core.CreateRegistryRow(sourceLineNumbers, registryRoot, registryKey, "CustomPSSnapInType", customSnapInType, componentId, false);
204 }
205
206 if (null != description)
207 {
208 this.Core.CreateRegistryRow(sourceLineNumbers, registryRoot, registryKey, "Description", description, componentId, false);
209 }
210
211 if (null != descriptionIndirect)
212 {
213 this.Core.CreateRegistryRow(sourceLineNumbers, registryRoot, registryKey, "DescriptionIndirect", descriptionIndirect, componentId, false);
214 }
215
216 this.Core.CreateRegistryRow(sourceLineNumbers, registryRoot, registryKey, "ModuleName", String.Format(CultureInfo.InvariantCulture, "[#{0}]", fileId), componentId, false);
217
218 this.Core.CreateRegistryRow(sourceLineNumbers, registryRoot, registryKey, "PowerShellVersion", requiredPowerShellVersion.ToString(2), componentId, false);
219
220 if (null != vendor)
221 {
222 this.Core.CreateRegistryRow(sourceLineNumbers, registryRoot, registryKey, "Vendor", vendor, componentId, false);
223 }
224
225 if (null != vendorIndirect)
226 {
227 this.Core.CreateRegistryRow(sourceLineNumbers, registryRoot, registryKey, "VendorIndirect", vendorIndirect, componentId, false);
228 }
229
230 if (null != version)
231 {
232 this.Core.CreateRegistryRow(sourceLineNumbers, registryRoot, registryKey, "Version", version, componentId, false);
233 }
234 }
235
236 /// <summary>
237 /// Parses a FormatsFile and TypesFile element.
238 /// </summary>
239 /// <param name="node">Element to parse.</param>
240 /// <param name="valueName">Registry value name.</param>
241 /// <param name="id">Idendifier for parent file or snap-in.</param>
242 /// <param name="componentId">Identifier for parent component.</param>
243 private void ParseExtensionsFile(XElement node, string valueName, string id, string componentId)
244 {
245 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
246 string fileId = null;
247 string snapIn = null;
248
249 foreach (XAttribute attrib in node.Attributes())
250 {
251 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
252 {
253 switch (attrib.Name.LocalName)
254 {
255 case "FileId":
256 fileId = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
257 snapIn = id;
258 break;
259
260 case "SnapIn":
261 fileId = id;
262 snapIn = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
263 break;
264
265 default:
266 this.Core.UnexpectedAttribute(node, attrib);
267 break;
268 }
269 }
270 else
271 {
272 this.Core.ParseExtensionAttribute(node, attrib);
273 }
274 }
275
276 if (null == fileId && null == snapIn)
277 {
278 this.Core.OnMessage(PSErrors.NeitherIdSpecified(sourceLineNumbers, valueName));
279 }
280
281 this.Core.ParseForExtensionElements(node);
282
283 int registryRoot = 2; // HKLM
284 string registryKey = String.Format(CultureInfo.InvariantCulture, KeyFormat, String.Format(CultureInfo.InvariantCulture, "!(wix.{0}_{1})", VarPrefix, snapIn), snapIn);
285
286 this.Core.CreateSimpleReference(sourceLineNumbers, "File", fileId);
287 this.Core.CreateRegistryRow(sourceLineNumbers, registryRoot, registryKey, valueName, String.Format(CultureInfo.InvariantCulture, "[~][#{0}]", fileId), componentId, false);
288 }
289 }
290}