diff options
Diffstat (limited to 'src/heat/UtilHeatExtension.cs')
-rw-r--r-- | src/heat/UtilHeatExtension.cs | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/src/heat/UtilHeatExtension.cs b/src/heat/UtilHeatExtension.cs new file mode 100644 index 00000000..7296a3b3 --- /dev/null +++ b/src/heat/UtilHeatExtension.cs | |||
@@ -0,0 +1,405 @@ | |||
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 | |||
3 | namespace WixToolset.Harvesters | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.IO; | ||
8 | using WixToolset.Core.Burn.Interfaces; | ||
9 | using WixToolset.Data; | ||
10 | using WixToolset.Data.Symbols; | ||
11 | using WixToolset.Extensibility.Services; | ||
12 | using WixToolset.Harvesters.Data; | ||
13 | using WixToolset.Harvesters.Extensibility; | ||
14 | |||
15 | /// <summary> | ||
16 | /// A utility heat extension for the WiX Toolset Harvester application. | ||
17 | /// </summary> | ||
18 | internal class UtilHeatExtension : BaseHeatExtension | ||
19 | { | ||
20 | public UtilHeatExtension(IServiceProvider serviceProvider) | ||
21 | { | ||
22 | this.PayloadHarvester = serviceProvider.GetService<IPayloadHarvester>(); | ||
23 | } | ||
24 | |||
25 | private IPayloadHarvester PayloadHarvester { get; } | ||
26 | |||
27 | /// <summary> | ||
28 | /// Gets the supported command line types for this extension. | ||
29 | /// </summary> | ||
30 | /// <value>The supported command line types for this extension.</value> | ||
31 | public override HeatCommandLineOption[] CommandLineTypes | ||
32 | { | ||
33 | get | ||
34 | { | ||
35 | return new HeatCommandLineOption[] | ||
36 | { | ||
37 | new HeatCommandLineOption("dir", "harvest a directory"), | ||
38 | new HeatCommandLineOption("file", "harvest a file"), | ||
39 | new HeatCommandLineOption("exepackagepayload", "harvest a bundle payload as ExePackagePayload"), | ||
40 | new HeatCommandLineOption("msupackagepayload", "harvest a bundle payload as MsuPackagePayload"), | ||
41 | new HeatCommandLineOption("perf", "harvest performance counters"), | ||
42 | new HeatCommandLineOption("reg", "harvest a .reg file"), | ||
43 | new HeatCommandLineOption("-ag", "autogenerate component guids at compile time"), | ||
44 | new HeatCommandLineOption("-cg <ComponentGroupName>", "component group name (cannot contain spaces e.g -cg MyComponentGroup)"), | ||
45 | new HeatCommandLineOption("-dr <DirectoryName>", "directory reference to root directories (cannot contain spaces e.g. -dr MyAppDirRef)"), | ||
46 | new HeatCommandLineOption("-var <VariableName>", "substitute File/@Source=\"SourceDir\" with a preprocessor or a wix variable" + Environment.NewLine + | ||
47 | "(e.g. -var var.MySource will become File/@Source=\"$(var.MySource)\\myfile.txt\" and " + Environment.NewLine + | ||
48 | "-var wix.MySource will become File/@Source=\"!(wix.MySource)\\myfile.txt\""), | ||
49 | new HeatCommandLineOption("-gg", "generate guids now"), | ||
50 | new HeatCommandLineOption("-g1", "generated guids are not in brackets"), | ||
51 | new HeatCommandLineOption("-ke", "keep empty directories"), | ||
52 | new HeatCommandLineOption("-scom", "suppress COM elements"), | ||
53 | new HeatCommandLineOption("-sfrag", "suppress fragments"), | ||
54 | new HeatCommandLineOption("-srd", "suppress harvesting the root directory as an element"), | ||
55 | new HeatCommandLineOption("-svb6", "suppress VB6 COM elements"), | ||
56 | new HeatCommandLineOption("-sreg", "suppress registry harvesting"), | ||
57 | new HeatCommandLineOption("-suid", "suppress unique identifiers for files, components, & directories"), | ||
58 | new HeatCommandLineOption("-t", "transform harvested output with XSL file"), | ||
59 | new HeatCommandLineOption("-template", "use template, one of: fragment,module,product"), | ||
60 | }; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | /// <summary> | ||
65 | /// Parse the command line options for this extension. | ||
66 | /// </summary> | ||
67 | /// <param name="type">The active harvester type.</param> | ||
68 | /// <param name="args">The option arguments.</param> | ||
69 | public override void ParseOptions(string type, string[] args) | ||
70 | { | ||
71 | bool active = false; | ||
72 | IHarvesterExtension harvesterExtension = null; | ||
73 | bool suppressHarvestingRegistryValues = false; | ||
74 | UtilFinalizeHarvesterMutator utilFinalizeHarvesterMutator = new UtilFinalizeHarvesterMutator(); | ||
75 | UtilMutator utilMutator = new UtilMutator(); | ||
76 | List<UtilTransformMutator> transformMutators = new List<UtilTransformMutator>(); | ||
77 | GenerateType generateType = GenerateType.Components; | ||
78 | |||
79 | // select the harvester | ||
80 | switch (type) | ||
81 | { | ||
82 | case "dir": | ||
83 | harvesterExtension = new DirectoryHarvester(); | ||
84 | active = true; | ||
85 | break; | ||
86 | case "file": | ||
87 | harvesterExtension = new FileHarvester(); | ||
88 | active = true; | ||
89 | break; | ||
90 | case "exepackagepayload": | ||
91 | harvesterExtension = new PayloadHarvester(this.PayloadHarvester, WixBundlePackageType.Exe); | ||
92 | active = true; | ||
93 | break; | ||
94 | case "msupackagepayload": | ||
95 | harvesterExtension = new PayloadHarvester(this.PayloadHarvester, WixBundlePackageType.Msu); | ||
96 | active = true; | ||
97 | break; | ||
98 | case "perf": | ||
99 | harvesterExtension = new PerformanceCategoryHarvester(); | ||
100 | active = true; | ||
101 | break; | ||
102 | case "reg": | ||
103 | harvesterExtension = new RegFileHarvester(); | ||
104 | active = true; | ||
105 | break; | ||
106 | } | ||
107 | |||
108 | // set default settings | ||
109 | utilMutator.CreateFragments = true; | ||
110 | utilMutator.SetUniqueIdentifiers = true; | ||
111 | |||
112 | // parse the options | ||
113 | for (int i = 0; i < args.Length; i++) | ||
114 | { | ||
115 | string commandSwitch = args[i]; | ||
116 | |||
117 | if (null == commandSwitch || 0 == commandSwitch.Length) // skip blank arguments | ||
118 | { | ||
119 | continue; | ||
120 | } | ||
121 | |||
122 | if ('-' == commandSwitch[0] || '/' == commandSwitch[0]) | ||
123 | { | ||
124 | string truncatedCommandSwitch = commandSwitch.Substring(1); | ||
125 | |||
126 | if ("ag" == truncatedCommandSwitch) | ||
127 | { | ||
128 | utilMutator.AutogenerateGuids = true; | ||
129 | } | ||
130 | else if ("cg" == truncatedCommandSwitch) | ||
131 | { | ||
132 | utilMutator.ComponentGroupName = this.GetArgumentParameter(args, i); | ||
133 | |||
134 | if (this.Core.Messaging.EncounteredError) | ||
135 | { | ||
136 | return; | ||
137 | } | ||
138 | } | ||
139 | else if ("dr" == truncatedCommandSwitch) | ||
140 | { | ||
141 | string dr = this.GetArgumentParameter(args, i); | ||
142 | |||
143 | if (this.Core.Messaging.EncounteredError) | ||
144 | { | ||
145 | return; | ||
146 | } | ||
147 | |||
148 | if (harvesterExtension is DirectoryHarvester) | ||
149 | { | ||
150 | ((DirectoryHarvester)harvesterExtension).RootedDirectoryRef = dr; | ||
151 | } | ||
152 | else if (harvesterExtension is FileHarvester) | ||
153 | { | ||
154 | ((FileHarvester)harvesterExtension).RootedDirectoryRef = dr; | ||
155 | } | ||
156 | } | ||
157 | else if ("gg" == truncatedCommandSwitch) | ||
158 | { | ||
159 | utilMutator.GenerateGuids = true; | ||
160 | } | ||
161 | else if ("g1" == truncatedCommandSwitch) | ||
162 | { | ||
163 | utilMutator.GuidFormat = "D"; | ||
164 | } | ||
165 | else if ("ke" == truncatedCommandSwitch) | ||
166 | { | ||
167 | if (harvesterExtension is DirectoryHarvester) | ||
168 | { | ||
169 | ((DirectoryHarvester)harvesterExtension).KeepEmptyDirectories = true; | ||
170 | } | ||
171 | else if (active) | ||
172 | { | ||
173 | // TODO: error message - not applicable to file harvester | ||
174 | } | ||
175 | } | ||
176 | else if ("scom" == truncatedCommandSwitch) | ||
177 | { | ||
178 | if (active) | ||
179 | { | ||
180 | utilFinalizeHarvesterMutator.SuppressCOMElements = true; | ||
181 | } | ||
182 | else | ||
183 | { | ||
184 | // TODO: error message - not applicable | ||
185 | } | ||
186 | } | ||
187 | else if ("svb6" == truncatedCommandSwitch) | ||
188 | { | ||
189 | if (active) | ||
190 | { | ||
191 | utilFinalizeHarvesterMutator.SuppressVB6COMElements = true; | ||
192 | } | ||
193 | else | ||
194 | { | ||
195 | // TODO: error message - not applicable | ||
196 | } | ||
197 | } | ||
198 | else if ("sfrag" == truncatedCommandSwitch) | ||
199 | { | ||
200 | utilMutator.CreateFragments = false; | ||
201 | } | ||
202 | else if ("srd" == truncatedCommandSwitch) | ||
203 | { | ||
204 | if (harvesterExtension is DirectoryHarvester) | ||
205 | { | ||
206 | ((DirectoryHarvester)harvesterExtension).SuppressRootDirectory = true; | ||
207 | } | ||
208 | else if (harvesterExtension is FileHarvester) | ||
209 | { | ||
210 | ((FileHarvester)harvesterExtension).SuppressRootDirectory = true; | ||
211 | } | ||
212 | } | ||
213 | else if ("sreg" == truncatedCommandSwitch) | ||
214 | { | ||
215 | suppressHarvestingRegistryValues = true; | ||
216 | } | ||
217 | else if ("suid" == truncatedCommandSwitch) | ||
218 | { | ||
219 | utilMutator.SetUniqueIdentifiers = false; | ||
220 | |||
221 | if (harvesterExtension is DirectoryHarvester) | ||
222 | { | ||
223 | ((DirectoryHarvester)harvesterExtension).SetUniqueIdentifiers = false; | ||
224 | } | ||
225 | else if (harvesterExtension is FileHarvester) | ||
226 | { | ||
227 | ((FileHarvester)harvesterExtension).SetUniqueIdentifiers = false; | ||
228 | } | ||
229 | } | ||
230 | else if (truncatedCommandSwitch.StartsWith("t:", StringComparison.Ordinal) || "t" == truncatedCommandSwitch) | ||
231 | { | ||
232 | string xslFile; | ||
233 | if (truncatedCommandSwitch.StartsWith("t:", StringComparison.Ordinal)) | ||
234 | { | ||
235 | this.Core.Messaging.Write(WarningMessages.DeprecatedCommandLineSwitch("t:", "t")); | ||
236 | xslFile = truncatedCommandSwitch.Substring(2); | ||
237 | } | ||
238 | else | ||
239 | { | ||
240 | xslFile = this.GetArgumentParameter(args, i, true); | ||
241 | } | ||
242 | |||
243 | if (0 <= xslFile.IndexOf('\"')) | ||
244 | { | ||
245 | this.Core.Messaging.Write(ErrorMessages.PathCannotContainQuote(xslFile)); | ||
246 | return; | ||
247 | } | ||
248 | |||
249 | try | ||
250 | { | ||
251 | xslFile = Path.GetFullPath(xslFile); | ||
252 | } | ||
253 | catch (Exception e) | ||
254 | { | ||
255 | this.Core.Messaging.Write(ErrorMessages.InvalidCommandLineFileName(xslFile, e.Message)); | ||
256 | return; | ||
257 | } | ||
258 | |||
259 | transformMutators.Add(new UtilTransformMutator(xslFile, transformMutators.Count)); | ||
260 | } | ||
261 | else if (truncatedCommandSwitch.StartsWith("template:", StringComparison.Ordinal) || "template" == truncatedCommandSwitch) | ||
262 | { | ||
263 | string template; | ||
264 | if(truncatedCommandSwitch.StartsWith("template:", StringComparison.Ordinal)) | ||
265 | { | ||
266 | this.Core.Messaging.Write(WarningMessages.DeprecatedCommandLineSwitch("template:", "template")); | ||
267 | template = truncatedCommandSwitch.Substring(9); | ||
268 | } | ||
269 | else | ||
270 | { | ||
271 | template = this.GetArgumentParameter(args, i); | ||
272 | } | ||
273 | |||
274 | switch (template) | ||
275 | { | ||
276 | case "fragment": | ||
277 | utilMutator.TemplateType = TemplateType.Fragment; | ||
278 | break; | ||
279 | case "module": | ||
280 | utilMutator.TemplateType = TemplateType.Module; | ||
281 | break; | ||
282 | case "product": | ||
283 | utilMutator.TemplateType = TemplateType.Package ; | ||
284 | break; | ||
285 | default: | ||
286 | // TODO: error | ||
287 | break; | ||
288 | } | ||
289 | } | ||
290 | else if ("var" == truncatedCommandSwitch) | ||
291 | { | ||
292 | if (active) | ||
293 | { | ||
294 | utilFinalizeHarvesterMutator.PreprocessorVariable = this.GetArgumentParameter(args, i); | ||
295 | |||
296 | if (this.Core.Messaging.EncounteredError) | ||
297 | { | ||
298 | return; | ||
299 | } | ||
300 | } | ||
301 | } | ||
302 | else if ("generate" == truncatedCommandSwitch) | ||
303 | { | ||
304 | if (harvesterExtension is DirectoryHarvester) | ||
305 | { | ||
306 | string genType = this.GetArgumentParameter(args, i).ToUpperInvariant(); | ||
307 | switch (genType) | ||
308 | { | ||
309 | case "COMPONENTS": | ||
310 | generateType = GenerateType.Components; | ||
311 | break; | ||
312 | case "PAYLOADGROUP": | ||
313 | generateType = GenerateType.PayloadGroup; | ||
314 | break; | ||
315 | default: | ||
316 | throw new WixException(HarvesterErrors.InvalidDirectoryOutputType(genType)); | ||
317 | } | ||
318 | } | ||
319 | else | ||
320 | { | ||
321 | // TODO: error message - not applicable | ||
322 | } | ||
323 | } | ||
324 | } | ||
325 | } | ||
326 | |||
327 | // set the appropriate harvester extension | ||
328 | if (active) | ||
329 | { | ||
330 | this.Core.Harvester.Extension = harvesterExtension; | ||
331 | |||
332 | if (!suppressHarvestingRegistryValues) | ||
333 | { | ||
334 | this.Core.Mutator.AddExtension(new UtilHarvesterMutator()); | ||
335 | } | ||
336 | |||
337 | this.Core.Mutator.AddExtension(utilFinalizeHarvesterMutator); | ||
338 | |||
339 | if (harvesterExtension is DirectoryHarvester directoryHarvester) | ||
340 | { | ||
341 | directoryHarvester.GenerateType = generateType; | ||
342 | this.Core.Harvester.Core.RootDirectory = this.Core.Harvester.Core.ExtensionArgument; | ||
343 | } | ||
344 | else if (harvesterExtension is FileHarvester) | ||
345 | { | ||
346 | if (((FileHarvester)harvesterExtension).SuppressRootDirectory) | ||
347 | { | ||
348 | this.Core.Harvester.Core.RootDirectory = Path.GetDirectoryName(Path.GetFullPath(this.Core.Harvester.Core.ExtensionArgument)); | ||
349 | } | ||
350 | else | ||
351 | { | ||
352 | this.Core.Harvester.Core.RootDirectory = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetFullPath(this.Core.Harvester.Core.ExtensionArgument))); | ||
353 | |||
354 | // GetDirectoryName() returns null for root paths such as "c:\", so make sure to support that as well | ||
355 | if (null == this.Core.Harvester.Core.RootDirectory) | ||
356 | { | ||
357 | this.Core.Harvester.Core.RootDirectory = Path.GetPathRoot(Path.GetDirectoryName(Path.GetFullPath(this.Core.Harvester.Core.ExtensionArgument))); | ||
358 | } | ||
359 | } | ||
360 | } | ||
361 | } | ||
362 | |||
363 | // set the mutator | ||
364 | this.Core.Mutator.AddExtension(utilMutator); | ||
365 | |||
366 | // add the transforms | ||
367 | foreach (UtilTransformMutator transformMutator in transformMutators) | ||
368 | { | ||
369 | this.Core.Mutator.AddExtension(transformMutator); | ||
370 | } | ||
371 | } | ||
372 | |||
373 | private string GetArgumentParameter(string[] args, int index) | ||
374 | { | ||
375 | return this.GetArgumentParameter(args, index, false); | ||
376 | } | ||
377 | |||
378 | private string GetArgumentParameter(string[] args, int index, bool allowSpaces) | ||
379 | { | ||
380 | string truncatedCommandSwitch = args[index]; | ||
381 | string commandSwitchValue = args[index + 1]; | ||
382 | |||
383 | //increment the index to the switch value | ||
384 | index++; | ||
385 | |||
386 | if (IsValidArg(args, index) && !String.IsNullOrEmpty(commandSwitchValue.Trim())) | ||
387 | { | ||
388 | if (!allowSpaces && commandSwitchValue.Contains(" ")) | ||
389 | { | ||
390 | this.Core.Messaging.Write(HarvesterErrors.SpacesNotAllowedInArgumentValue(truncatedCommandSwitch, commandSwitchValue)); | ||
391 | } | ||
392 | else | ||
393 | { | ||
394 | return commandSwitchValue; | ||
395 | } | ||
396 | } | ||
397 | else | ||
398 | { | ||
399 | this.Core.Messaging.Write(HarvesterErrors.ArgumentRequiresValue(truncatedCommandSwitch)); | ||
400 | } | ||
401 | |||
402 | return null; | ||
403 | } | ||
404 | } | ||
405 | } | ||