aboutsummaryrefslogtreecommitdiff
path: root/src/tools/heat/UtilMutator.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/heat/UtilMutator.cs')
-rw-r--r--src/tools/heat/UtilMutator.cs628
1 files changed, 0 insertions, 628 deletions
diff --git a/src/tools/heat/UtilMutator.cs b/src/tools/heat/UtilMutator.cs
deleted file mode 100644
index 80fd470c..00000000
--- a/src/tools/heat/UtilMutator.cs
+++ /dev/null
@@ -1,628 +0,0 @@
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.Harvesters
4{
5 using System;
6 using System.Collections;
7 using System.Diagnostics;
8 using System.Globalization;
9 using System.IO;
10 using WixToolset.Harvesters.Extensibility;
11 using Wix = WixToolset.Harvesters.Serialize;
12
13 /// <summary>
14 /// The template type.
15 /// </summary>
16 public enum TemplateType
17 {
18 /// <summary>
19 /// A fragment template.
20 /// </summary>
21 Fragment,
22
23 /// <summary>
24 /// A module template.
25 /// </summary>
26 Module,
27
28 /// <summary>
29 /// A product template.
30 /// </summary>
31 Package
32 }
33
34 /// <summary>
35 /// The mutator for the WiX Toolset Internet Information Services Extension.
36 /// </summary>
37 public sealed class UtilMutator : BaseMutatorExtension
38 {
39 private ArrayList components;
40 private ArrayList componentGroups;
41 private string componentGroupName;
42 private bool createFragments;
43 private ArrayList directories;
44 private ArrayList directoryRefs;
45 private ArrayList files;
46 private ArrayList features;
47 private SortedList fragments;
48 private bool autogenerateGuids;
49 private bool generateGuids;
50 private string guidFormat = "B"; // Defaults to guid in {}
51 private Wix.IParentElement rootElement;
52 private bool setUniqueIdentifiers;
53 private TemplateType templateType;
54
55 /// <summary>
56 /// Instantiate a new UtilMutator.
57 /// </summary>
58 public UtilMutator()
59 {
60 this.components = new ArrayList();
61 this.componentGroups = new ArrayList();
62 this.directories = new ArrayList();
63 this.directoryRefs = new ArrayList();
64 this.features = new ArrayList();
65 this.files = new ArrayList();
66 this.fragments = new SortedList();
67 }
68
69 /// <summary>
70 /// Gets or sets the value of the component group name.
71 /// </summary>
72 /// <value>The component group name.</value>
73 public string ComponentGroupName
74 {
75 get { return this.componentGroupName; }
76 set { this.componentGroupName = value; }
77 }
78
79 /// <summary>
80 /// Gets or sets the option to create fragments.
81 /// </summary>
82 /// <value>The option to create fragments.</value>
83 public bool CreateFragments
84 {
85 get { return this.createFragments; }
86 set { this.createFragments = value; }
87 }
88
89 /// <summary>
90 /// Gets or sets the option to autogenerate component guids at compile time.
91 /// </summary>
92 /// <value>The option to autogenerate component guids.</value>
93 public bool AutogenerateGuids
94 {
95 get { return this.autogenerateGuids; }
96 set { this.autogenerateGuids = value; }
97 }
98
99 /// <summary>
100 /// Gets or sets the option to generate missing guids.
101 /// </summary>
102 /// <value>The option to generate missing guids.</value>
103 public bool GenerateGuids
104 {
105 get { return this.generateGuids; }
106 set { this.generateGuids = value; }
107 }
108
109 /// <summary>
110 /// Gets or sets the option to set the format of guids.
111 /// D - 32 digits separated by hyphens:
112 /// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
113 /// B - 32 digits separated by hyphens, enclosed in brackets:
114 /// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
115 /// </summary>
116 /// <value>Guid format either B or D.</value>
117 public string GuidFormat
118 {
119 get { return this.guidFormat; }
120 set { this.guidFormat = value; }
121 }
122
123 /// <summary>
124 /// Gets the sequence of the extension.
125 /// </summary>
126 /// <value>The sequence of the extension.</value>
127 public override int Sequence
128 {
129 get { return 1000; }
130 }
131
132 /// <summary>
133 /// Gets of sets the option to set unique identifiers.
134 /// </summary>
135 /// <value>The option to set unique identifiers.</value>
136 public bool SetUniqueIdentifiers
137 {
138 get { return this.setUniqueIdentifiers; }
139 set { this.setUniqueIdentifiers = value; }
140 }
141
142 /// <summary>
143 /// Gets or sets the template type.
144 /// </summary>
145 /// <value>The template type.</value>
146 public TemplateType TemplateType
147 {
148 get { return this.templateType; }
149 set { this.templateType = value; }
150 }
151
152 /// <summary>
153 /// Mutate a WiX document.
154 /// </summary>
155 /// <param name="wix">The Wix document element.</param>
156 public override void Mutate(Wix.Wix wix)
157 {
158 this.components.Clear();
159 this.directories.Clear();
160 this.directoryRefs.Clear();
161 this.features.Clear();
162 this.files.Clear();
163 this.fragments.Clear();
164 this.rootElement = null;
165
166 // index elements in this wix document
167 this.IndexElement(wix);
168
169 this.MutateWix(wix);
170
171 this.MutateFiles();
172
173 this.MutateDirectories();
174
175 this.MutateComponents();
176
177 if (null != this.componentGroupName)
178 {
179 this.CreateComponentGroup(wix);
180 }
181
182 // add the components to the product feature after all the identifiers have been set
183 if (TemplateType.Package == this.templateType)
184 {
185 var feature = (Wix.Feature)this.features[0];
186
187 foreach (Wix.ComponentGroup group in this.componentGroups)
188 {
189 var componentGroupRef = new Wix.ComponentGroupRef();
190 componentGroupRef.Id = group.Id;
191
192 feature.AddChild(componentGroupRef);
193 }
194 }
195 else if (TemplateType.Module == this.templateType)
196 {
197 foreach (Wix.ISchemaElement element in wix.Children)
198 {
199 if (element is Wix.Module)
200 {
201 foreach (Wix.ComponentGroup group in this.componentGroups)
202 {
203 var componentGroupRef = new Wix.ComponentGroupRef();
204 componentGroupRef.Id = group.Id;
205
206 ((Wix.IParentElement)element).AddChild(componentGroupRef);
207 }
208 break;
209 }
210 }
211 }
212
213 //if(!this.createFragments && TemplateType.Package
214 foreach (Wix.Fragment fragment in this.fragments.Values)
215 {
216 wix.AddChild(fragment);
217 }
218 }
219
220 /// <summary>
221 /// Creates a component group with a given name.
222 /// </summary>
223 /// <param name="wix">The Wix document element.</param>
224 private void CreateComponentGroup(Wix.Wix wix)
225 {
226 var componentGroup = new Wix.ComponentGroup();
227 componentGroup.Id = this.componentGroupName;
228 this.componentGroups.Add(componentGroup);
229
230 var cgFragment = new Wix.Fragment();
231 cgFragment.AddChild(componentGroup);
232 wix.AddChild(cgFragment);
233
234 var componentCount = 0;
235 for (; componentCount < this.components.Count; componentCount++)
236 {
237 var c = this.components[componentCount] as Wix.Component;
238
239 if (this.createFragments)
240 {
241 if (c.ParentElement is Wix.Directory)
242 {
243 var parentDirectory = c.ParentElement as Wix.Directory;
244
245 componentGroup.AddChild(c);
246 c.Directory = parentDirectory.Id;
247 parentDirectory.RemoveChild(c);
248 }
249 else if (c.ParentElement is Wix.DirectoryRef || c.ParentElement is Wix.StandardDirectory)
250 {
251 var parentDirectory = c.ParentElement as Wix.DirectoryBase;
252
253 componentGroup.AddChild(c);
254 c.Directory = parentDirectory.Id;
255 parentDirectory.RemoveChild(c);
256
257 // Remove whole fragment if moving the component to the component group just leaves an empty DirectoryRef
258 if (0 < this.fragments.Count && parentDirectory.ParentElement is Wix.Fragment)
259 {
260 var parentFragment = parentDirectory.ParentElement as Wix.Fragment;
261 var childCount = 0;
262 foreach (Wix.ISchemaElement element in parentFragment.Children)
263 {
264 childCount++;
265 }
266
267 // Component should always have an Id but the SortedList creation allows for null and bases the name on the fragment count which we cannot reverse engineer here.
268 if (1 == childCount && !String.IsNullOrEmpty(c.Id))
269 {
270 var removeIndex = this.fragments.IndexOfKey(String.Concat("Component:", c.Id));
271 if (0 <= removeIndex)
272 {
273 this.fragments.RemoveAt(removeIndex);
274 }
275 }
276 }
277 }
278 }
279 else
280 {
281 var componentRef = new Wix.ComponentRef();
282 componentRef.Id = c.Id;
283 componentGroup.AddChild(componentRef);
284 }
285 }
286 }
287
288 /// <summary>
289 /// Index an element.
290 /// </summary>
291 /// <param name="element">The element to index.</param>
292 private void IndexElement(Wix.ISchemaElement element)
293 {
294 if (element is Wix.Component)
295 {
296 this.components.Add(element);
297 }
298 else if (element is Wix.ComponentGroup)
299 {
300 this.componentGroups.Add(element);
301 }
302 else if (element is Wix.Directory)
303 {
304 this.directories.Add(element);
305 }
306 else if (element is Wix.DirectoryRef)
307 {
308 this.directoryRefs.Add(element);
309 }
310 else if (element is Wix.Feature)
311 {
312 this.features.Add(element);
313 }
314 else if (element is Wix.File)
315 {
316 this.files.Add(element);
317 }
318 else if (element is Wix.Module || element is Wix.PatchCreation || element is Wix.Package)
319 {
320 Debug.Assert(null == this.rootElement);
321 this.rootElement = (Wix.IParentElement)element;
322 }
323
324 // index the child elements
325 if (element is Wix.IParentElement parentElement)
326 {
327 foreach (Wix.ISchemaElement childElement in parentElement.Children)
328 {
329 this.IndexElement(childElement);
330 }
331 }
332 }
333
334 /// <summary>
335 /// Mutate the components.
336 /// </summary>
337 private void MutateComponents()
338 {
339 var identifierGenerator = new IdentifierGenerator("Component", this.Core);
340 if (TemplateType.Module == this.templateType)
341 {
342 identifierGenerator.MaxIdentifierLength = IdentifierGenerator.MaxModuleIdentifierLength;
343 }
344
345 foreach (Wix.Component component in this.components)
346 {
347 if (null == component.Id)
348 {
349 var firstFileId = String.Empty;
350
351 // attempt to create a possible identifier from the first file identifier in the component
352 foreach (Wix.File file in component[typeof(Wix.File)])
353 {
354 firstFileId = file.Id;
355 break;
356 }
357
358 if (String.IsNullOrEmpty(firstFileId))
359 {
360 firstFileId = this.GetGuid();
361 }
362
363 component.Id = identifierGenerator.GetIdentifier(firstFileId);
364 }
365
366 if (null == component.Guid)
367 {
368 if (this.AutogenerateGuids)
369 {
370 component.Guid = "*";
371 }
372 else
373 {
374 component.Guid = this.GetGuid();
375 }
376 }
377
378 if (this.createFragments && component.ParentElement is Wix.Directory directory)
379 {
380 // parent directory must have an identifier to create a reference to it
381 if (null == directory.Id)
382 {
383 break;
384 }
385
386 if (this.rootElement is Wix.Module)
387 {
388 // add a ComponentRef for the Component
389 var componentRef = new Wix.ComponentRef();
390 componentRef.Id = component.Id;
391 this.rootElement.AddChild(componentRef);
392 }
393
394 // create a new Fragment
395 var fragment = new Wix.Fragment();
396 this.fragments.Add(String.Concat("Component:", component.Id ?? this.fragments.Count.ToString()), fragment);
397
398 // create a new DirectoryRef
399 var directoryRef = DirectoryHelper.CreateDirectoryReference(directory.Id);
400 fragment.AddChild(directoryRef);
401
402 // move the Component from the the Directory to the DirectoryRef
403 directory.RemoveChild(component);
404 directoryRef.AddChild(component);
405 }
406 }
407 }
408
409 /// <summary>
410 /// Mutate the directories.
411 /// </summary>
412 private void MutateDirectories()
413 {
414 if (!this.setUniqueIdentifiers)
415 {
416 // assign all identifiers before fragmenting (because fragmenting requires them all to be present)
417 var identifierGenerator = new IdentifierGenerator("Directory", this.Core);
418 if (TemplateType.Module == this.templateType)
419 {
420 identifierGenerator.MaxIdentifierLength = IdentifierGenerator.MaxModuleIdentifierLength;
421 }
422
423 foreach (Wix.Directory directory in this.directories)
424 {
425 if (null == directory.Id)
426 {
427 directory.Id = identifierGenerator.GetIdentifier(directory.Name);
428 }
429 }
430 }
431
432 if (this.createFragments)
433 {
434 foreach (Wix.Directory directory in this.directories)
435 {
436 if (directory.ParentElement is Wix.Directory)
437 {
438 var parentDirectory = (Wix.DirectoryBase)directory.ParentElement;
439
440 // parent directory must have an identifier to create a reference to it
441 if (null == parentDirectory.Id)
442 {
443 return;
444 }
445
446 // create a new Fragment
447 var fragment = new Wix.Fragment();
448 this.fragments.Add(String.Concat("Directory:", ("TARGETDIR" == directory.Id ? null : (null != directory.Id ? directory.Id : this.fragments.Count.ToString()))), fragment);
449
450 // create a new DirectoryRef
451 var directoryRef = DirectoryHelper.CreateDirectoryReference(parentDirectory.Id);
452 fragment.AddChild(directoryRef);
453
454 // move the Directory from the parent Directory to DirectoryRef
455 parentDirectory.RemoveChild(directory);
456 directoryRef.AddChild(directory);
457 }
458 else if (directory.ParentElement is Wix.Fragment parent)
459 {
460 // When creating fragments, remove any top-level Directory elements;
461 // the fragments should be pulled in by their DirectoryRefs instead.
462 parent.RemoveChild(directory);
463
464 // Remove the fragment if it is empty.
465 if (parent.Children.GetEnumerator().Current == null && parent.ParentElement != null)
466 {
467 ((Wix.IParentElement)parent.ParentElement).RemoveChild(parent);
468 }
469 }
470 else if (directory.ParentElement == this.rootElement)
471 {
472 // create a new Fragment
473 var fragment = new Wix.Fragment();
474 this.fragments.Add(String.Concat("Directory:", ("TARGETDIR" == directory.Id ? null : (null != directory.Id ? directory.Id : this.fragments.Count.ToString()))), fragment);
475
476 // move the Directory from the root element to the Fragment
477 this.rootElement.RemoveChild(directory);
478 fragment.AddChild(directory);
479 }
480 }
481 }
482 }
483
484 /// <summary>
485 /// Mutate the files.
486 /// </summary>
487 private void MutateFiles()
488 {
489 var identifierGenerator = new IdentifierGenerator("File", this.Core);
490 if (TemplateType.Module == this.templateType)
491 {
492 identifierGenerator.MaxIdentifierLength = IdentifierGenerator.MaxModuleIdentifierLength;
493 }
494
495 foreach (Wix.File file in this.files)
496 {
497 if (null == file.Id)
498 {
499 file.Id = identifierGenerator.GetIdentifier(Path.GetFileName(file.Source));
500 }
501 }
502 }
503
504 /// <summary>
505 /// Mutate a Wix element.
506 /// </summary>
507 /// <param name="wix">The Wix element to mutate.</param>
508 private void MutateWix(Wix.Wix wix)
509 {
510 if (TemplateType.Fragment != this.templateType)
511 {
512 if (null != this.rootElement || 0 != this.features.Count)
513 {
514 throw new Exception("The template option cannot be used with Feature, Package, or Module elements present.");
515 }
516
517 // create a package element although it won't always be used
518 var package = new Wix.SummaryInformation();
519 if (TemplateType.Module == this.templateType)
520 {
521 package.Id = this.GetGuid();
522 }
523 else
524 {
525 package.Compressed = Wix.YesNoType.yes;
526 }
527
528 package.InstallerVersion = 200;
529
530 var targetDir = new Wix.Directory();
531 targetDir.Id = "TARGETDIR";
532 targetDir.Name = "SourceDir";
533
534 foreach (Wix.DirectoryRef directoryRef in this.directoryRefs)
535 {
536 if (String.Equals(directoryRef.Id, "TARGETDIR", StringComparison.OrdinalIgnoreCase))
537 {
538 var parent = directoryRef.ParentElement as Wix.IParentElement;
539
540 foreach (Wix.ISchemaElement element in directoryRef.Children)
541 {
542 targetDir.AddChild(element);
543 }
544
545 parent.RemoveChild(directoryRef);
546
547 if (null != ((Wix.ISchemaElement)parent).ParentElement)
548 {
549 var i = 0;
550
551 foreach (Wix.ISchemaElement element in parent.Children)
552 {
553 i++;
554 }
555
556 if (0 == i)
557 {
558 var supParent = (Wix.IParentElement)((Wix.ISchemaElement)parent).ParentElement;
559 supParent.RemoveChild((Wix.ISchemaElement)parent);
560 }
561 }
562
563 break;
564 }
565 }
566
567 if (TemplateType.Module == this.templateType)
568 {
569 var module = new Wix.Module();
570 module.Id = "PUT-MODULE-NAME-HERE";
571 module.Language = "1033";
572 module.Version = "1.0.0.0";
573
574 package.Manufacturer = "PUT-COMPANY-NAME-HERE";
575 module.AddChild(package);
576 module.AddChild(targetDir);
577
578 wix.AddChild(module);
579 this.rootElement = module;
580 }
581 else // product
582 {
583 var product = new Wix.Package();
584 product.Id = this.GetGuid();
585 product.Language = "1033";
586 product.Manufacturer = "PUT-COMPANY-NAME-HERE";
587 product.Name = "PUT-PRODUCT-NAME-HERE";
588 product.UpgradeCode = this.GetGuid();
589 product.Version = "1.0.0.0";
590 product.AddChild(package);
591 product.AddChild(targetDir);
592
593 var media = new Wix.Media();
594 media.Id = "1";
595 media.Cabinet = "product.cab";
596 media.EmbedCab = Wix.YesNoType.yes;
597 product.AddChild(media);
598
599 var feature = new Wix.Feature();
600 feature.Id = "ProductFeature";
601 feature.Title = "PUT-FEATURE-TITLE-HERE";
602 feature.Level = 1;
603 product.AddChild(feature);
604 this.features.Add(feature);
605
606 wix.AddChild(product);
607 this.rootElement = product;
608 }
609 }
610 }
611
612 /// <summary>
613 /// Get a generated guid or a placeholder for a guid.
614 /// </summary>
615 /// <returns>A generated guid or placeholder.</returns>
616 private string GetGuid()
617 {
618 if (this.generateGuids)
619 {
620 return Guid.NewGuid().ToString(this.guidFormat, CultureInfo.InvariantCulture).ToUpper(CultureInfo.InvariantCulture);
621 }
622 else
623 {
624 return "PUT-GUID-HERE";
625 }
626 }
627 }
628}