main
cs 657 lines 27.6 KB
Raw
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.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.Symbols;
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 string codepage = null;
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.GetAttributeLocalizableCodePageValue(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)PatchSymbolFlags.PatchSymbolNoImagehlp : 0;
99 break;
100 case "ApiPatchingSymbolNoFailuresFlag":
101 apiPatchingSymbolFlags |= (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) ? (int)PatchSymbolFlags.PatchSymbolNoFailures : 0;
102 break;
103 case "ApiPatchingSymbolUndecoratedTooFlag":
104 apiPatchingSymbolFlags |= (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) ? (int)PatchSymbolFlags.PatchSymbolUndecoratedToo : 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, 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 this.Core.AddSymbol(new WixPatchSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, patchId))
201 {
202 Codepage = codepage,
203 ClientPatchId = clientPatchId,
204 OptimizePatchSizeForLargeFiles = optimizePatchSizeForLargeFiles,
205 ApiPatchingSymbolFlags = apiPatchingSymbolFlags,
206 });
207
208 if (allowRemoval)
209 {
210 this.AddMsiPatchMetadata(sourceLineNumbers, null, "AllowRemoval", allowRemoval ? "1" : "0");
211 }
212
213 if (null != classification)
214 {
215 this.AddMsiPatchMetadata(sourceLineNumbers, null, "Classification", classification);
216 }
217
218 // always generate the CreationTimeUTC
219 {
220 this.AddMsiPatchMetadata(sourceLineNumbers, null, "CreationTimeUTC", DateTime.UtcNow.ToString("MM-dd-yy HH:mm", CultureInfo.InvariantCulture));
221 }
222
223 if (null != description)
224 {
225 this.AddMsiPatchMetadata(sourceLineNumbers, null, "Description", description);
226 }
227
228 if (null != displayName)
229 {
230 this.AddMsiPatchMetadata(sourceLineNumbers, null, "DisplayName", displayName);
231 }
232
233 if (null != manufacturer)
234 {
235 this.AddMsiPatchMetadata(sourceLineNumbers, null, "ManufacturerName", manufacturer);
236 }
237
238 if (YesNoType.NotSet != minorUpdateTargetRTM)
239 {
240 this.AddMsiPatchMetadata(sourceLineNumbers, null, "MinorUpdateTargetRTM", YesNoType.Yes == minorUpdateTargetRTM ? "1" : "0");
241 }
242
243 if (null != moreInfoUrl)
244 {
245 this.AddMsiPatchMetadata(sourceLineNumbers, null, "MoreInfoURL", moreInfoUrl);
246 }
247
248 if (CompilerConstants.IntegerNotSet != optimizeCA)
249 {
250 this.AddMsiPatchMetadata(sourceLineNumbers, null, "OptimizeCA", optimizeCA.ToString(CultureInfo.InvariantCulture));
251 }
252
253 if (YesNoType.NotSet != optimizedInstallMode)
254 {
255 this.AddMsiPatchMetadata(sourceLineNumbers, null, "OptimizedInstallMode", YesNoType.Yes == optimizedInstallMode ? "1" : "0");
256 }
257
258 if (null != targetProductName)
259 {
260 this.AddMsiPatchMetadata(sourceLineNumbers, null, "TargetProductName", targetProductName);
261 }
262
263 if (null != comments)
264 {
265 this.AddMsiPatchMetadata(sourceLineNumbers, null, "Comments", comments);
266 }
267 }
268 // TODO: do something with versionMismatches and productMismatches
269 }
270
271 /// <summary>
272 /// Parses the OptimizeCustomActions element.
273 /// </summary>
274 /// <param name="node">Element to parse.</param>
275 /// <returns>The combined integer value for callers to store as appropriate.</returns>
276 private int ParseOptimizeCustomActionsElement(XElement node)
277 {
278 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
279 var optimizeCA = OptimizeCAFlags.None;
280
281 foreach (var attrib in node.Attributes())
282 {
283 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
284 {
285 switch (attrib.Name.LocalName)
286 {
287 case "SkipAssignment":
288 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
289 {
290 optimizeCA |= OptimizeCAFlags.SkipAssignment;
291 }
292 break;
293 case "SkipImmediate":
294 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
295 {
296 optimizeCA |= OptimizeCAFlags.SkipImmediate;
297 }
298 break;
299 case "SkipDeferred":
300 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
301 {
302 optimizeCA |= OptimizeCAFlags.SkipDeferred;
303 }
304 break;
305 default:
306 this.Core.UnexpectedAttribute(node, attrib);
307 break;
308 }
309 }
310 else
311 {
312 this.Core.ParseExtensionAttribute(node, attrib);
313 }
314 }
315
316 return (int)optimizeCA;
317 }
318
319 /// <summary>
320 /// Parses a PatchFamily element.
321 /// </summary>
322 /// <param name="node">The element to parse.</param>
323 /// <param name="parentType"></param>
324 /// <param name="parentId"></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(WarningMessages.InvalidMsiProductVersion(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 "SoftwareTagRef":
415 this.ParseSoftwareTagRefElement(child);
416 break;
417 case "UIRef":
418 this.ParsePatchChildRefElement(child, "WixUI");
419 break;
420 default:
421 this.Core.UnexpectedElement(node, child);
422 break;
423 }
424 }
425 else
426 {
427 this.Core.ParseExtensionElement(node, child);
428 }
429 }
430
431
432 if (!this.Core.EncounteredError)
433 {
434 this.Core.AddSymbol(new MsiPatchFamilySymbol(sourceLineNumbers, new Identifier(id.Access, id.Id, productCode))
435 {
436 PatchFamily = id.Id,
437 ProductCode = productCode,
438 Sequence = version,
439 Attributes = attributes
440 });
441
442 if (ComplexReferenceParentType.Unknown != parentType)
443 {
444 this.Core.CreateComplexReference(sourceLineNumbers, parentType, parentId, null, ComplexReferenceChildType.PatchFamily, id.Id, ComplexReferenceParentType.Patch == parentType);
445 }
446 }
447 }
448
449 /// <summary>
450 /// Parses a PatchFamilyGroup element.
451 /// </summary>
452 /// <param name="node">Element to parse.</param>
453 /// <param name="parentType"></param>
454 /// <param name="parentId"></param>
455 private void ParsePatchFamilyGroupElement(XElement node, ComplexReferenceParentType parentType, string parentId)
456 {
457 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
458 Identifier id = null;
459
460 foreach (var attrib in node.Attributes())
461 {
462 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
463 {
464 switch (attrib.Name.LocalName)
465 {
466 case "Id":
467 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
468 break;
469 default:
470 this.Core.UnexpectedAttribute(node, attrib);
471 break;
472 }
473 }
474 else
475 {
476 this.Core.ParseExtensionAttribute(node, attrib);
477 }
478 }
479
480 if (null == id)
481 {
482 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
483 id = Identifier.Invalid;
484 }
485
486 foreach (var child in node.Elements())
487 {
488 if (CompilerCore.WixNamespace == child.Name.Namespace)
489 {
490 switch (child.Name.LocalName)
491 {
492 case "PatchFamily":
493 this.ParsePatchFamilyElement(child, ComplexReferenceParentType.PatchFamilyGroup, id.Id);
494 break;
495 case "PatchFamilyRef":
496 this.ParsePatchFamilyRefElement(child, ComplexReferenceParentType.PatchFamilyGroup, id.Id);
497 break;
498 case "PatchFamilyGroupRef":
499 this.ParsePatchFamilyGroupRefElement(child, ComplexReferenceParentType.PatchFamilyGroup, id.Id);
500 break;
501 default:
502 this.Core.UnexpectedElement(node, child);
503 break;
504 }
505 }
506 else
507 {
508 this.Core.ParseExtensionElement(node, child);
509 }
510 }
511
512 if (!this.Core.EncounteredError)
513 {
514 this.Core.AddSymbol(new WixPatchFamilyGroupSymbol(sourceLineNumbers, id));
515
516 //Add this PatchFamilyGroup and its parent in WixGroup.
517 this.Core.CreateWixGroupRow(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.PatchFamilyGroup, id.Id);
518 }
519 }
520
521 /// <summary>
522 /// Parses a PatchFamilyGroup reference element.
523 /// </summary>
524 /// <param name="node">Element to parse.</param>
525 /// <param name="parentType">The type of parent.</param>
526 /// <param name="parentId">Identifier of parent element.</param>
527 private void ParsePatchFamilyGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId)
528 {
529 Debug.Assert(ComplexReferenceParentType.PatchFamilyGroup == parentType || ComplexReferenceParentType.Patch == parentType);
530
531 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
532 string id = null;
533
534 foreach (var attrib in node.Attributes())
535 {
536 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
537 {
538 switch (attrib.Name.LocalName)
539 {
540 case "Id":
541 id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
542 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixPatchFamilyGroup, id);
543 break;
544 default:
545 this.Core.UnexpectedAttribute(node, attrib);
546 break;
547 }
548 }
549 else
550 {
551 this.Core.ParseExtensionAttribute(node, attrib);
552 }
553 }
554
555 if (null == id)
556 {
557 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
558 }
559
560 this.Core.ParseForExtensionElements(node);
561
562 if (!this.Core.EncounteredError)
563 {
564 this.Core.CreateComplexReference(sourceLineNumbers, parentType, parentId, null, ComplexReferenceChildType.PatchFamilyGroup, id, true);
565 }
566 }
567
568 /// <summary>
569 /// Parses a TargetProductCodes element.
570 /// </summary>
571 /// <param name="node">The element to parse.</param>
572 private void ParseTargetProductCodesElement(XElement node)
573 {
574 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
575 var replace = false;
576 var targetProductCodes = new List<string>();
577
578 foreach (var attrib in node.Attributes())
579 {
580 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
581 {
582 switch (attrib.Name.LocalName)
583 {
584 case "Replace":
585 replace = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
586 break;
587 default:
588 this.Core.UnexpectedAttribute(node, attrib);
589 break;
590 }
591 }
592 else
593 {
594 this.Core.ParseExtensionAttribute(node, attrib);
595 }
596 }
597
598 foreach (var child in node.Elements())
599 {
600 if (CompilerCore.WixNamespace == child.Name.Namespace)
601 {
602 switch (child.Name.LocalName)
603 {
604 case "TargetProductCode":
605 var id = this.ParseTargetProductCodeElement(child);
606 if (0 == String.CompareOrdinal("*", id))
607 {
608 this.Core.Write(ErrorMessages.IllegalAttributeValueWhenNested(sourceLineNumbers, child.Name.LocalName, "Id", id, node.Name.LocalName));
609 }
610 else
611 {
612 targetProductCodes.Add(id);
613 }
614 break;
615 default:
616 this.Core.UnexpectedElement(node, child);
617 break;
618 }
619 }
620 else
621 {
622 this.Core.ParseExtensionElement(node, child);
623 }
624 }
625
626 if (!this.Core.EncounteredError)
627 {
628 // By default, target ProductCodes should be added.
629 if (!replace)
630 {
631 this.Core.AddSymbol(new WixPatchTargetSymbol(sourceLineNumbers)
632 {
633 ProductCode = "*"
634 });
635 }
636
637 foreach (var targetProductCode in targetProductCodes)
638 {
639 this.Core.AddSymbol(new WixPatchTargetSymbol(sourceLineNumbers)
640 {
641 ProductCode = targetProductCode
642 });
643 }
644 }
645 }
646
647 private void AddMsiPatchMetadata(SourceLineNumber sourceLineNumbers, string company, string property, string value)
648 {
649 this.Core.AddSymbol(new MsiPatchMetadataSymbol(sourceLineNumbers, new Identifier(AccessModifier.Section, company, property))
650 {
651 Company = company,
652 Property = property,
653 Value = value
654 });
655 }
656 }
657 }