aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/Compiler_Patch.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core/Compiler_Patch.cs')
-rw-r--r--src/WixToolset.Core/Compiler_Patch.cs655
1 files changed, 655 insertions, 0 deletions
diff --git a/src/WixToolset.Core/Compiler_Patch.cs b/src/WixToolset.Core/Compiler_Patch.cs
new file mode 100644
index 00000000..42951543
--- /dev/null
+++ b/src/WixToolset.Core/Compiler_Patch.cs
@@ -0,0 +1,655 @@
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.Core
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.Globalization;
9 using System.Xml.Linq;
10 using WixToolset.Data;
11 using WixToolset.Data.Tuples;
12 using WixToolset.Extensibility;
13
14 /// <summary>
15 /// Compiler of the WiX toolset.
16 /// </summary>
17 internal partial class Compiler : ICompiler
18 {
19 /// <summary>
20 /// Parses an patch element.
21 /// </summary>
22 /// <param name="node">The element to parse.</param>
23 private void ParsePatchElement(XElement node)
24 {
25 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
26 string patchId = null;
27 var codepage = 0;
28 ////bool versionMismatches = false;
29 ////bool productMismatches = false;
30 var allowRemoval = false;
31 string classification = null;
32 string clientPatchId = null;
33 string description = null;
34 string displayName = null;
35 string comments = null;
36 string manufacturer = null;
37 var minorUpdateTargetRTM = YesNoType.NotSet;
38 string moreInfoUrl = null;
39 var optimizeCA = CompilerConstants.IntegerNotSet;
40 var optimizedInstallMode = YesNoType.NotSet;
41 string targetProductName = null;
42 // string replaceGuids = String.Empty;
43 var apiPatchingSymbolFlags = 0;
44 var optimizePatchSizeForLargeFiles = false;
45
46 foreach (var attrib in node.Attributes())
47 {
48 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
49 {
50 switch (attrib.Name.LocalName)
51 {
52 case "Id":
53 patchId = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, true);
54 break;
55 case "Codepage":
56 codepage = this.Core.GetAttributeCodePageValue(sourceLineNumbers, attrib);
57 break;
58 case "AllowMajorVersionMismatches":
59 ////versionMismatches = (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib));
60 break;
61 case "AllowProductCodeMismatches":
62 ////productMismatches = (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib));
63 break;
64 case "AllowRemoval":
65 allowRemoval = (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib));
66 break;
67 case "Classification":
68 classification = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
69 break;
70 case "ClientPatchId":
71 clientPatchId = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
72 break;
73 case "Description":
74 description = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
75 break;
76 case "DisplayName":
77 displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
78 break;
79 case "Comments":
80 comments = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
81 break;
82 case "Manufacturer":
83 manufacturer = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
84 break;
85 case "MinorUpdateTargetRTM":
86 minorUpdateTargetRTM = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
87 break;
88 case "MoreInfoURL":
89 moreInfoUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
90 break;
91 case "OptimizedInstallMode":
92 optimizedInstallMode = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
93 break;
94 case "TargetProductName":
95 targetProductName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
96 break;
97 case "ApiPatchingSymbolNoImagehlpFlag":
98 apiPatchingSymbolFlags |= (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) ? (int)PatchSymbolFlagsType.PATCH_SYMBOL_NO_IMAGEHLP : 0;
99 break;
100 case "ApiPatchingSymbolNoFailuresFlag":
101 apiPatchingSymbolFlags |= (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) ? (int)PatchSymbolFlagsType.PATCH_SYMBOL_NO_FAILURES : 0;
102 break;
103 case "ApiPatchingSymbolUndecoratedTooFlag":
104 apiPatchingSymbolFlags |= (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) ? (int)PatchSymbolFlagsType.PATCH_SYMBOL_UNDECORATED_TOO : 0;
105 break;
106 case "OptimizePatchSizeForLargeFiles":
107 optimizePatchSizeForLargeFiles = (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib));
108 break;
109 default:
110 this.Core.UnexpectedAttribute(node, attrib);
111 break;
112 }
113 }
114 else
115 {
116 this.Core.ParseExtensionAttribute(node, attrib);
117 }
118 }
119
120 if (patchId == null || patchId == "*")
121 {
122 // auto-generate at compile time, since this value gets dispersed to several locations
123 patchId = Common.GenerateGuid();
124 }
125 this.activeName = patchId;
126
127 if (null == this.activeName)
128 {
129 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
130 }
131 if (null == classification)
132 {
133 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Classification"));
134 }
135 if (null == clientPatchId)
136 {
137 clientPatchId = String.Concat("_", new Guid(patchId).ToString("N", CultureInfo.InvariantCulture).ToUpper(CultureInfo.InvariantCulture));
138 }
139 if (null == description)
140 {
141 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Description"));
142 }
143 if (null == displayName)
144 {
145 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DisplayName"));
146 }
147 if (null == manufacturer)
148 {
149 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Manufacturer"));
150 }
151
152 this.Core.CreateActiveSection(this.activeName, SectionType.Patch, codepage, this.Context.CompilationId);
153
154 foreach (var child in node.Elements())
155 {
156 if (CompilerCore.WixNamespace == child.Name.Namespace)
157 {
158 switch (child.Name.LocalName)
159 {
160 case "PatchInformation":
161 this.ParsePatchInformationElement(child);
162 break;
163 case "Media":
164 this.ParseMediaElement(child, patchId);
165 break;
166 case "OptimizeCustomActions":
167 optimizeCA = this.ParseOptimizeCustomActionsElement(child);
168 break;
169 case "PatchFamily":
170 this.ParsePatchFamilyElement(child, ComplexReferenceParentType.Patch, patchId);
171 break;
172 case "PatchFamilyRef":
173 this.ParsePatchFamilyRefElement(child, ComplexReferenceParentType.Patch, patchId);
174 break;
175 case "PatchFamilyGroup":
176 this.ParsePatchFamilyGroupElement(child, ComplexReferenceParentType.Patch, patchId);
177 break;
178 case "PatchFamilyGroupRef":
179 this.ParsePatchFamilyGroupRefElement(child, ComplexReferenceParentType.Patch, patchId);
180 break;
181 case "PatchProperty":
182 this.ParsePatchPropertyElement(child, true);
183 break;
184 case "TargetProductCodes":
185 this.ParseTargetProductCodesElement(child);
186 break;
187 default:
188 this.Core.UnexpectedElement(node, child);
189 break;
190 }
191 }
192 else
193 {
194 this.Core.ParseExtensionElement(node, child);
195 }
196 }
197
198 if (!this.Core.EncounteredError)
199 {
200 var tuple = new WixPatchIdTuple(sourceLineNumbers)
201 {
202 ProductCode = patchId,
203 ClientPatchId = clientPatchId,
204 OptimizePatchSizeForLargeFiles = optimizePatchSizeForLargeFiles,
205 ApiPatchingSymbolFlags = apiPatchingSymbolFlags
206 };
207
208 this.Core.AddTuple(tuple);
209
210 if (allowRemoval)
211 {
212 this.AddMsiPatchMetadata(sourceLineNumbers, null, "AllowRemoval", allowRemoval ? "1" : "0");
213 }
214
215 if (null != classification)
216 {
217 this.AddMsiPatchMetadata(sourceLineNumbers, null, "Classification", classification);
218 }
219
220 // always generate the CreationTimeUTC
221 {
222 this.AddMsiPatchMetadata(sourceLineNumbers, null, "CreationTimeUTC", DateTime.UtcNow.ToString("MM-dd-yy HH:mm", CultureInfo.InvariantCulture));
223 }
224
225 if (null != description)
226 {
227 this.AddMsiPatchMetadata(sourceLineNumbers, null, "Description", description);
228 }
229
230 if (null != displayName)
231 {
232 this.AddMsiPatchMetadata(sourceLineNumbers, null, "DisplayName", displayName);
233 }
234
235 if (null != manufacturer)
236 {
237 this.AddMsiPatchMetadata(sourceLineNumbers, null, "ManufacturerName", manufacturer);
238 }
239
240 if (YesNoType.NotSet != minorUpdateTargetRTM)
241 {
242 this.AddMsiPatchMetadata(sourceLineNumbers, null, "MinorUpdateTargetRTM", YesNoType.Yes == minorUpdateTargetRTM ? "1" : "0");
243 }
244
245 if (null != moreInfoUrl)
246 {
247 this.AddMsiPatchMetadata(sourceLineNumbers, null, "MoreInfoURL", moreInfoUrl);
248 }
249
250 if (CompilerConstants.IntegerNotSet != optimizeCA)
251 {
252 this.AddMsiPatchMetadata(sourceLineNumbers, null, "OptimizeCA", optimizeCA.ToString(CultureInfo.InvariantCulture));
253 }
254
255 if (YesNoType.NotSet != optimizedInstallMode)
256 {
257 this.AddMsiPatchMetadata(sourceLineNumbers, null, "OptimizedInstallMode", YesNoType.Yes == optimizedInstallMode ? "1" : "0");
258 }
259
260 if (null != targetProductName)
261 {
262 this.AddMsiPatchMetadata(sourceLineNumbers, null, "TargetProductName", targetProductName);
263 }
264
265 if (null != comments)
266 {
267 this.AddMsiPatchMetadata(sourceLineNumbers, null, "Comments", comments);
268 }
269 }
270 // TODO: do something with versionMismatches and productMismatches
271 }
272
273 /// <summary>
274 /// Parses the OptimizeCustomActions element.
275 /// </summary>
276 /// <param name="node">Element to parse.</param>
277 /// <returns>The combined integer value for callers to store as appropriate.</returns>
278 private int ParseOptimizeCustomActionsElement(XElement node)
279 {
280 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
281 var optimizeCA = OptimizeCA.None;
282
283 foreach (var attrib in node.Attributes())
284 {
285 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
286 {
287 switch (attrib.Name.LocalName)
288 {
289 case "SkipAssignment":
290 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
291 {
292 optimizeCA |= OptimizeCA.SkipAssignment;
293 }
294 break;
295 case "SkipImmediate":
296 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
297 {
298 optimizeCA |= OptimizeCA.SkipImmediate;
299 }
300 break;
301 case "SkipDeferred":
302 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
303 {
304 optimizeCA |= OptimizeCA.SkipDeferred;
305 }
306 break;
307 default:
308 this.Core.UnexpectedAttribute(node, attrib);
309 break;
310 }
311 }
312 else
313 {
314 this.Core.ParseExtensionAttribute(node, attrib);
315 }
316 }
317
318 return (int)optimizeCA;
319 }
320
321 /// <summary>
322 /// Parses a PatchFamily element.
323 /// </summary>
324 /// <param name="node">The element to parse.</param>
325 private void ParsePatchFamilyElement(XElement node, ComplexReferenceParentType parentType, string parentId)
326 {
327 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
328 Identifier id = null;
329 string productCode = null;
330 string version = null;
331 var attributes = 0;
332
333 foreach (var attrib in node.Attributes())
334 {
335 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
336 {
337 switch (attrib.Name.LocalName)
338 {
339 case "Id":
340 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
341 break;
342 case "ProductCode":
343 productCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
344 break;
345 case "Version":
346 version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
347 break;
348 case "Supersede":
349 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
350 {
351 attributes |= 0x1;
352 }
353 break;
354 default:
355 this.Core.UnexpectedAttribute(node, attrib);
356 break;
357 }
358 }
359 else
360 {
361 this.Core.ParseExtensionAttribute(node, attrib);
362 }
363 }
364
365 if (null == id)
366 {
367 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
368 id = Identifier.Invalid;
369 }
370
371 if (String.IsNullOrEmpty(version))
372 {
373 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
374 }
375 else if (!CompilerCore.IsValidProductVersion(version))
376 {
377 this.Core.Write(ErrorMessages.InvalidProductVersion(sourceLineNumbers, version));
378 }
379
380 // find unexpected child elements
381 foreach (var child in node.Elements())
382 {
383 if (CompilerCore.WixNamespace == child.Name.Namespace)
384 {
385 switch (child.Name.LocalName)
386 {
387 case "All":
388 this.ParseAllElement(child);
389 break;
390 case "BinaryRef":
391 this.ParsePatchChildRefElement(child, "Binary");
392 break;
393 case "ComponentRef":
394 this.ParsePatchChildRefElement(child, "Component");
395 break;
396 case "CustomActionRef":
397 this.ParsePatchChildRefElement(child, "CustomAction");
398 break;
399 case "DirectoryRef":
400 this.ParsePatchChildRefElement(child, "Directory");
401 break;
402 case "DigitalCertificateRef":
403 this.ParsePatchChildRefElement(child, "MsiDigitalCertificate");
404 break;
405 case "FeatureRef":
406 this.ParsePatchChildRefElement(child, "Feature");
407 break;
408 case "IconRef":
409 this.ParsePatchChildRefElement(child, "Icon");
410 break;
411 case "PropertyRef":
412 this.ParsePatchChildRefElement(child, "Property");
413 break;
414 case "UIRef":
415 this.ParsePatchChildRefElement(child, "WixUI");
416 break;
417 default:
418 this.Core.UnexpectedElement(node, child);
419 break;
420 }
421 }
422 else
423 {
424 this.Core.ParseExtensionElement(node, child);
425 }
426 }
427
428
429 if (!this.Core.EncounteredError)
430 {
431 var tuple = new MsiPatchSequenceTuple(sourceLineNumbers)
432 {
433 PatchFamily = id.Id,
434 ProductCode = productCode,
435 Sequence = version,
436 Attributes = attributes
437 };
438
439 this.Core.AddTuple(tuple);
440
441 if (ComplexReferenceParentType.Unknown != parentType)
442 {
443 this.Core.CreateComplexReference(sourceLineNumbers, parentType, parentId, null, ComplexReferenceChildType.PatchFamily, id.Id, ComplexReferenceParentType.Patch == parentType);
444 }
445 }
446 }
447
448 /// <summary>
449 /// Parses a PatchFamilyGroup element.
450 /// </summary>
451 /// <param name="node">Element to parse.</param>
452 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
453 private void ParsePatchFamilyGroupElement(XElement node, ComplexReferenceParentType parentType, string parentId)
454 {
455 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
456 Identifier id = null;
457
458 foreach (var attrib in node.Attributes())
459 {
460 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
461 {
462 switch (attrib.Name.LocalName)
463 {
464 case "Id":
465 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
466 break;
467 default:
468 this.Core.UnexpectedAttribute(node, attrib);
469 break;
470 }
471 }
472 else
473 {
474 this.Core.ParseExtensionAttribute(node, attrib);
475 }
476 }
477
478 if (null == id)
479 {
480 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
481 id = Identifier.Invalid;
482 }
483
484 foreach (var child in node.Elements())
485 {
486 if (CompilerCore.WixNamespace == child.Name.Namespace)
487 {
488 switch (child.Name.LocalName)
489 {
490 case "PatchFamily":
491 this.ParsePatchFamilyElement(child, ComplexReferenceParentType.PatchFamilyGroup, id.Id);
492 break;
493 case "PatchFamilyRef":
494 this.ParsePatchFamilyRefElement(child, ComplexReferenceParentType.PatchFamilyGroup, id.Id);
495 break;
496 case "PatchFamilyGroupRef":
497 this.ParsePatchFamilyGroupRefElement(child, ComplexReferenceParentType.PatchFamilyGroup, id.Id);
498 break;
499 default:
500 this.Core.UnexpectedElement(node, child);
501 break;
502 }
503 }
504 else
505 {
506 this.Core.ParseExtensionElement(node, child);
507 }
508 }
509
510 if (!this.Core.EncounteredError)
511 {
512 this.Core.AddTuple(new WixPatchFamilyGroupTuple(sourceLineNumbers, id));
513
514 //Add this PatchFamilyGroup and its parent in WixGroup.
515 this.Core.CreateWixGroupRow(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.PatchFamilyGroup, id.Id);
516 }
517 }
518
519 /// <summary>
520 /// Parses a PatchFamilyGroup reference element.
521 /// </summary>
522 /// <param name="node">Element to parse.</param>
523 /// <param name="parentType">The type of parent.</param>
524 /// <param name="parentId">Identifier of parent element.</param>
525 private void ParsePatchFamilyGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId)
526 {
527 Debug.Assert(ComplexReferenceParentType.PatchFamilyGroup == parentType || ComplexReferenceParentType.Patch == parentType);
528
529 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
530 string id = null;
531
532 foreach (var attrib in node.Attributes())
533 {
534 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
535 {
536 switch (attrib.Name.LocalName)
537 {
538 case "Id":
539 id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
540 this.Core.CreateSimpleReference(sourceLineNumbers, "WixPatchFamilyGroup", id);
541 break;
542 default:
543 this.Core.UnexpectedAttribute(node, attrib);
544 break;
545 }
546 }
547 else
548 {
549 this.Core.ParseExtensionAttribute(node, attrib);
550 }
551 }
552
553 if (null == id)
554 {
555 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
556 }
557
558 this.Core.ParseForExtensionElements(node);
559
560 if (!this.Core.EncounteredError)
561 {
562 this.Core.CreateComplexReference(sourceLineNumbers, parentType, parentId, null, ComplexReferenceChildType.PatchFamilyGroup, id, true);
563 }
564 }
565
566 /// <summary>
567 /// Parses a TargetProductCodes element.
568 /// </summary>
569 /// <param name="node">The element to parse.</param>
570 private void ParseTargetProductCodesElement(XElement node)
571 {
572 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
573 var replace = false;
574 var targetProductCodes = new List<string>();
575
576 foreach (var attrib in node.Attributes())
577 {
578 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
579 {
580 switch (attrib.Name.LocalName)
581 {
582 case "Replace":
583 replace = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
584 break;
585 default:
586 this.Core.UnexpectedAttribute(node, attrib);
587 break;
588 }
589 }
590 else
591 {
592 this.Core.ParseExtensionAttribute(node, attrib);
593 }
594 }
595
596 foreach (var child in node.Elements())
597 {
598 if (CompilerCore.WixNamespace == child.Name.Namespace)
599 {
600 switch (child.Name.LocalName)
601 {
602 case "TargetProductCode":
603 var id = this.ParseTargetProductCodeElement(child);
604 if (0 == String.CompareOrdinal("*", id))
605 {
606 this.Core.Write(ErrorMessages.IllegalAttributeValueWhenNested(sourceLineNumbers, child.Name.LocalName, "Id", id, node.Name.LocalName));
607 }
608 else
609 {
610 targetProductCodes.Add(id);
611 }
612 break;
613 default:
614 this.Core.UnexpectedElement(node, child);
615 break;
616 }
617 }
618 else
619 {
620 this.Core.ParseExtensionElement(node, child);
621 }
622 }
623
624 if (!this.Core.EncounteredError)
625 {
626 // By default, target ProductCodes should be added.
627 if (!replace)
628 {
629 this.Core.AddTuple(new WixPatchTargetTuple(sourceLineNumbers)
630 {
631 ProductCode = "*"
632 });
633 }
634
635 foreach (var targetProductCode in targetProductCodes)
636 {
637 this.Core.AddTuple(new WixPatchTargetTuple(sourceLineNumbers)
638 {
639 ProductCode = targetProductCode
640 });
641 }
642 }
643 }
644
645 private void AddMsiPatchMetadata(SourceLineNumber sourceLineNumbers, string company, string property, string value)
646 {
647 this.Core.AddTuple(new MsiPatchMetadataTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, company, property))
648 {
649 Company = company,
650 Property = property,
651 Value = value
652 });
653 }
654 }
655}