main
cs 1,265 lines 49.5 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.Globalization;
8 using System.Xml.Linq;
9 using WixToolset.Data;
10 using WixToolset.Data.Symbols;
11 using WixToolset.Extensibility;
12
13 /// <summary>
14 /// Compiler of the WiX toolset.
15 /// </summary>
16 internal partial class Compiler : ICompiler
17 {
18 /// <summary>
19 /// Parses a patch creation element.
20 /// </summary>
21 /// <param name="node">The element to parse.</param>
22 private void ParsePatchCreationElement(XElement node)
23 {
24 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
25 var clean = true; // Default is to clean
26 var codepage = 0;
27 string outputPath = null;
28 var productMismatches = false;
29 var replaceGuids = String.Empty;
30 string sourceList = null;
31 string symbolFlags = null;
32 var targetProducts = String.Empty;
33 var versionMismatches = false;
34 var wholeFiles = false;
35
36 foreach (var attrib in node.Attributes())
37 {
38 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
39 {
40 switch (attrib.Name.LocalName)
41 {
42 case "Id":
43 this.activeName = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
44 break;
45 case "AllowMajorVersionMismatches":
46 versionMismatches = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
47 break;
48 case "AllowProductCodeMismatches":
49 productMismatches = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
50 break;
51 case "CleanWorkingFolder":
52 clean = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
53 break;
54 case "Codepage":
55 codepage = this.Core.GetAttributeCodePageValue(sourceLineNumbers, attrib);
56 break;
57 case "OutputPath":
58 outputPath = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
59 break;
60 case "SourceList":
61 sourceList = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
62 break;
63 case "SymbolFlags":
64 symbolFlags = String.Format(CultureInfo.InvariantCulture, "0x{0:x8}", this.Core.GetAttributeLongValue(sourceLineNumbers, attrib, 0, UInt32.MaxValue));
65 break;
66 case "WholeFilesOnly":
67 wholeFiles = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
68 break;
69 default:
70 this.Core.UnexpectedAttribute(node, attrib);
71 break;
72 }
73 }
74 else
75 {
76 this.Core.ParseExtensionAttribute(node, attrib);
77 }
78 }
79
80 if (null == this.activeName)
81 {
82 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
83 }
84
85 this.Core.CreateActiveSection(this.activeName, SectionType.PatchCreation, this.Context.CompilationId);
86
87 foreach (var child in node.Elements())
88 {
89 if (CompilerCore.WixNamespace == child.Name.Namespace)
90 {
91 switch (child.Name.LocalName)
92 {
93 case "Family":
94 this.ParseFamilyElement(child);
95 break;
96 case "PatchInformation":
97 this.ParsePatchInformationElement(child);
98 break;
99 case "PatchMetadata":
100 this.ParsePatchMetadataElement(child);
101 break;
102 case "PatchProperty":
103 this.ParsePatchPropertyElement(child, false);
104 break;
105 case "PatchSequence":
106 this.ParsePatchSequenceElement(child);
107 break;
108 case "ReplacePatch":
109 replaceGuids = String.Concat(replaceGuids, this.ParseReplacePatchElement(child));
110 break;
111 case "TargetProductCode":
112 var targetProduct = this.ParseTargetProductCodeElement(child);
113 if (0 < targetProducts.Length)
114 {
115 targetProducts = String.Concat(targetProducts, ";");
116 }
117 targetProducts = String.Concat(targetProducts, targetProduct);
118 break;
119 default:
120 this.Core.UnexpectedElement(node, child);
121 break;
122 }
123 }
124 else
125 {
126 this.Core.ParseExtensionElement(node, child);
127 }
128 }
129
130 this.AddPrivateProperty(sourceLineNumbers, "PatchGUID", this.activeName);
131 this.AddPrivateProperty(sourceLineNumbers, "AllowProductCodeMismatches", productMismatches ? "1" : "0");
132 this.AddPrivateProperty(sourceLineNumbers, "AllowProductVersionMajorMismatches", versionMismatches ? "1" : "0");
133 this.AddPrivateProperty(sourceLineNumbers, "DontRemoveTempFolderWhenFinished", clean ? "0" : "1");
134 this.AddPrivateProperty(sourceLineNumbers, "IncludeWholeFilesOnly", wholeFiles ? "1" : "0");
135
136 if (null != symbolFlags)
137 {
138 this.AddPrivateProperty(sourceLineNumbers, "ApiPatchingSymbolFlags", symbolFlags);
139 }
140
141 if (0 < replaceGuids.Length)
142 {
143 this.AddPrivateProperty(sourceLineNumbers, "ListOfPatchGUIDsToReplace", replaceGuids);
144 }
145
146 if (0 < targetProducts.Length)
147 {
148 this.AddPrivateProperty(sourceLineNumbers, "ListOfTargetProductCodes", targetProducts);
149 }
150
151 if (null != outputPath)
152 {
153 this.AddPrivateProperty(sourceLineNumbers, "PatchOutputPath", outputPath);
154 }
155
156 if (null != sourceList)
157 {
158 this.AddPrivateProperty(sourceLineNumbers, "PatchSourceList", sourceList);
159 }
160 }
161
162 /// <summary>
163 /// Parses a family element.
164 /// </summary>
165 /// <param name="node">The element to parse.</param>
166 private void ParseFamilyElement(XElement node)
167 {
168 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
169 var diskId = CompilerConstants.IntegerNotSet;
170 string diskPrompt = null;
171 string mediaSrcProp = null;
172 string name = null;
173 var sequenceStart = CompilerConstants.IntegerNotSet;
174 string volumeLabel = null;
175
176 foreach (var attrib in node.Attributes())
177 {
178 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
179 {
180 switch (attrib.Name.LocalName)
181 {
182 case "DiskId":
183 diskId = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int16.MaxValue);
184 break;
185 case "DiskPrompt":
186 diskPrompt = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
187 break;
188 case "MediaSrcProp":
189 mediaSrcProp = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
190 break;
191 case "Name":
192 name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
193 break;
194 case "SequenceStart":
195 sequenceStart = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int32.MaxValue);
196 break;
197 case "VolumeLabel":
198 volumeLabel = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
199 break;
200 default:
201 this.Core.UnexpectedAttribute(node, attrib);
202 break;
203 }
204 }
205 else
206 {
207 this.Core.ParseExtensionAttribute(node, attrib);
208 }
209 }
210
211 if (null == name)
212 {
213 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
214 }
215 else if (0 < name.Length)
216 {
217 if (8 < name.Length) // check the length
218 {
219 this.Core.Write(ErrorMessages.FamilyNameTooLong(sourceLineNumbers, node.Name.LocalName, "Name", name, name.Length));
220 }
221 else // check for illegal characters
222 {
223 foreach (var character in name)
224 {
225 if (!Char.IsLetterOrDigit(character) && '_' != character)
226 {
227 this.Core.Write(ErrorMessages.IllegalFamilyName(sourceLineNumbers, node.Name.LocalName, "Name", name));
228 }
229 }
230 }
231 }
232
233 foreach (var child in node.Elements())
234 {
235 if (CompilerCore.WixNamespace == child.Name.Namespace)
236 {
237 switch (child.Name.LocalName)
238 {
239 case "UpgradeImage":
240 this.ParseUpgradeImageElement(child, name);
241 break;
242 case "ExternalFile":
243 this.ParseExternalFileElement(child, name);
244 break;
245 case "ProtectFile":
246 this.ParseProtectFileElement(child, name);
247 break;
248 default:
249 this.Core.UnexpectedElement(node, child);
250 break;
251 }
252 }
253 else
254 {
255 this.Core.ParseExtensionElement(node, child);
256 }
257 }
258
259 if (!this.Core.EncounteredError)
260 {
261 var symbol = this.Core.AddSymbol(new ImageFamiliesSymbol(sourceLineNumbers)
262 {
263 Family = name,
264 MediaSrcPropName = mediaSrcProp,
265 DiskPrompt = diskPrompt,
266 VolumeLabel = volumeLabel
267 });
268
269 if (CompilerConstants.IntegerNotSet != diskId)
270 {
271 symbol.MediaDiskId = diskId;
272 }
273
274 if (CompilerConstants.IntegerNotSet != sequenceStart)
275 {
276 symbol.FileSequenceStart = sequenceStart;
277 }
278 }
279 }
280
281 /// <summary>
282 /// Parses an upgrade image element.
283 /// </summary>
284 /// <param name="node">The element to parse.</param>
285 /// <param name="family">The family for this element.</param>
286 private void ParseUpgradeImageElement(XElement node, string family)
287 {
288 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
289 string sourceFile = null;
290 string sourcePatch = null;
291 var symbols = new List<string>();
292 string upgrade = null;
293
294 foreach (var attrib in node.Attributes())
295 {
296 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
297 {
298 switch (attrib.Name.LocalName)
299 {
300 case "Id":
301 upgrade = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
302 if (13 < upgrade.Length)
303 {
304 this.Core.Write(ErrorMessages.IdentifierTooLongError(sourceLineNumbers, node.Name.LocalName, "Id", upgrade, 13));
305 }
306 break;
307 case "SourceFile":
308 sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
309 break;
310 case "SourcePatch":
311 sourcePatch = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
312 break;
313 default:
314 this.Core.UnexpectedAttribute(node, attrib);
315 break;
316 }
317 }
318 else
319 {
320 this.Core.ParseExtensionAttribute(node, attrib);
321 }
322 }
323
324 if (null == upgrade)
325 {
326 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
327 }
328
329 if (null == sourceFile)
330 {
331 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
332 }
333
334 foreach (var child in node.Elements())
335 {
336 if (CompilerCore.WixNamespace == child.Name.Namespace)
337 {
338 switch (child.Name.LocalName)
339 {
340 case "SymbolPath":
341 symbols.Add(this.ParseSymbolPathElement(child));
342 break;
343 case "TargetImage":
344 this.ParseTargetImageElement(child, upgrade, family);
345 break;
346 case "UpgradeFile":
347 this.ParseUpgradeFileElement(child, upgrade);
348 break;
349 default:
350 this.Core.UnexpectedElement(node, child);
351 break;
352 }
353 }
354 else
355 {
356 this.Core.ParseExtensionElement(node, child);
357 }
358 }
359
360 if (!this.Core.EncounteredError)
361 {
362 this.Core.AddSymbol(new UpgradedImagesSymbol(sourceLineNumbers)
363 {
364 Upgraded = upgrade,
365 MsiPath = sourceFile,
366 PatchMsiPath = sourcePatch,
367 SymbolPaths = String.Join(";", symbols),
368 Family = family
369 });
370 }
371 }
372
373 /// <summary>
374 /// Parses an upgrade file element.
375 /// </summary>
376 /// <param name="node">The element to parse.</param>
377 /// <param name="upgrade">The upgrade key for this element.</param>
378 private void ParseUpgradeFileElement(XElement node, string upgrade)
379 {
380 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
381 var allowIgnoreOnError = false;
382 string file = null;
383 var ignore = false;
384 var symbols = new List<string>();
385 var wholeFile = false;
386
387 foreach (var attrib in node.Attributes())
388 {
389 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
390 {
391 switch (attrib.Name.LocalName)
392 {
393 case "AllowIgnoreOnError":
394 allowIgnoreOnError = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
395 break;
396 case "File":
397 file = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
398 break;
399 case "Ignore":
400 ignore = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
401 break;
402 case "WholeFile":
403 wholeFile = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
404 break;
405 default:
406 this.Core.UnexpectedAttribute(node, attrib);
407 break;
408 }
409 }
410 else
411 {
412 this.Core.ParseExtensionAttribute(node, attrib);
413 }
414 }
415
416 if (null == file)
417 {
418 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "File"));
419 }
420
421 foreach (var child in node.Elements())
422 {
423 if (CompilerCore.WixNamespace == child.Name.Namespace)
424 {
425 switch (child.Name.LocalName)
426 {
427 case "SymbolPath":
428 symbols.Add(this.ParseSymbolPathElement(child));
429 break;
430 default:
431 this.Core.UnexpectedElement(node, child);
432 break;
433 }
434 }
435 else
436 {
437 this.Core.ParseExtensionElement(node, child);
438 }
439 }
440
441 if (!this.Core.EncounteredError)
442 {
443 if (ignore)
444 {
445 this.Core.AddSymbol(new UpgradedFilesToIgnoreSymbol(sourceLineNumbers)
446 {
447 Upgraded = upgrade,
448 FTK = file
449 });
450 }
451 else
452 {
453 this.Core.AddSymbol(new UpgradedFilesOptionalDataSymbol(sourceLineNumbers)
454 {
455 Upgraded = upgrade,
456 FTK = file,
457 SymbolPaths = String.Join(";", symbols),
458 AllowIgnoreOnPatchError = allowIgnoreOnError,
459 IncludeWholeFile = wholeFile
460 });
461 }
462 }
463 }
464
465 /// <summary>
466 /// Parses a target image element.
467 /// </summary>
468 /// <param name="node">The element to parse.</param>
469 /// <param name="upgrade">The upgrade key for this element.</param>
470 /// <param name="family">The family key for this element.</param>
471 private void ParseTargetImageElement(XElement node, string upgrade, string family)
472 {
473 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
474 var ignore = false;
475 var order = CompilerConstants.IntegerNotSet;
476 string sourceFile = null;
477 string symbols = null;
478 string target = null;
479 string validation = null;
480
481 foreach (var attrib in node.Attributes())
482 {
483 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
484 {
485 switch (attrib.Name.LocalName)
486 {
487 case "Id":
488 target = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
489 if (target.Length > 13)
490 {
491 this.Core.Write(ErrorMessages.IdentifierTooLongError(sourceLineNumbers, node.Name.LocalName, "Id", target, 13));
492 }
493 break;
494 case "IgnoreMissingFiles":
495 ignore = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
496 break;
497 case "Order":
498 order = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, Int32.MinValue + 2, Int32.MaxValue);
499 break;
500 case "SourceFile":
501 sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
502 break;
503 case "Validation":
504 validation = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
505 break;
506 default:
507 this.Core.UnexpectedAttribute(node, attrib);
508 break;
509 }
510 }
511 else
512 {
513 this.Core.ParseExtensionAttribute(node, attrib);
514 }
515 }
516
517 if (null == target)
518 {
519 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
520 }
521
522 if (null == sourceFile)
523 {
524 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
525 }
526
527 if (CompilerConstants.IntegerNotSet == order)
528 {
529 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Order"));
530 }
531
532 foreach (var child in node.Elements())
533 {
534 if (CompilerCore.WixNamespace == child.Name.Namespace)
535 {
536 switch (child.Name.LocalName)
537 {
538 case "SymbolPath":
539 if (null != symbols)
540 {
541 symbols = String.Concat(symbols, ";", this.ParseSymbolPathElement(child));
542 }
543 else
544 {
545 symbols = this.ParseSymbolPathElement(child);
546 }
547 break;
548 case "TargetFile":
549 this.ParseTargetFileElement(child, target, family);
550 break;
551 default:
552 this.Core.UnexpectedElement(node, child);
553 break;
554 }
555 }
556 else
557 {
558 this.Core.ParseExtensionElement(node, child);
559 }
560 }
561
562 if (!this.Core.EncounteredError)
563 {
564 this.Core.AddSymbol(new TargetImagesSymbol(sourceLineNumbers)
565 {
566 Target = target,
567 MsiPath = sourceFile,
568 SymbolPaths = symbols,
569 Upgraded = upgrade,
570 Order = order,
571 ProductValidateFlags = validation,
572 IgnoreMissingSrcFiles = ignore
573 });
574 }
575 }
576
577 /// <summary>
578 /// Parses an upgrade file element.
579 /// </summary>
580 /// <param name="node">The element to parse.</param>
581 /// <param name="target">The upgrade key for this element.</param>
582 /// <param name="family">The family key for this element.</param>
583 private void ParseTargetFileElement(XElement node, string target, string family)
584 {
585 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
586 string file = null;
587 string ignoreLengths = null;
588 string ignoreOffsets = null;
589 string protectLengths = null;
590 string protectOffsets = null;
591 string symbols = null;
592
593 foreach (var attrib in node.Attributes())
594 {
595 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
596 {
597 switch (attrib.Name.LocalName)
598 {
599 case "Id":
600 file = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
601 break;
602 default:
603 this.Core.UnexpectedAttribute(node, attrib);
604 break;
605 }
606 }
607 else
608 {
609 this.Core.ParseExtensionAttribute(node, attrib);
610 }
611 }
612
613 if (null == file)
614 {
615 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
616 }
617
618 foreach (var child in node.Elements())
619 {
620 if (CompilerCore.WixNamespace == child.Name.Namespace)
621 {
622 switch (child.Name.LocalName)
623 {
624 case "IgnoreRange":
625 this.ParseRangeElement(child, ref ignoreOffsets, ref ignoreLengths);
626 break;
627 case "ProtectRange":
628 this.ParseRangeElement(child, ref protectOffsets, ref protectLengths);
629 break;
630 case "SymbolPath":
631 symbols = this.ParseSymbolPathElement(child);
632 break;
633 default:
634 this.Core.UnexpectedElement(node, child);
635 break;
636 }
637 }
638 else
639 {
640 this.Core.ParseExtensionElement(node, child);
641 }
642 }
643
644 if (!this.Core.EncounteredError)
645 {
646 var symbol = this.Core.AddSymbol(new TargetFilesOptionalDataSymbol(sourceLineNumbers)
647 {
648 Target = target,
649 FTK = file,
650 SymbolPaths = symbols,
651 IgnoreOffsets = ignoreOffsets,
652 IgnoreLengths = ignoreLengths,
653 });
654
655 if (null != protectOffsets)
656 {
657 symbol.RetainOffsets = protectOffsets;
658
659 this.Core.AddSymbol(new FamilyFileRangesSymbol(sourceLineNumbers)
660 {
661 Family = family,
662 FTK = file,
663 RetainOffsets = protectOffsets,
664 RetainLengths = protectLengths,
665 });
666 }
667 }
668 }
669
670 /// <summary>
671 /// Parses an external file element.
672 /// </summary>
673 /// <param name="node">The element to parse.</param>
674 /// <param name="family">The family for this element.</param>
675 private void ParseExternalFileElement(XElement node, string family)
676 {
677 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
678 string file = null;
679 string ignoreLengths = null;
680 string ignoreOffsets = null;
681 var order = CompilerConstants.IntegerNotSet;
682 string protectLengths = null;
683 string protectOffsets = null;
684 string source = null;
685 string symbols = null;
686
687 foreach (var attrib in node.Attributes())
688 {
689 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
690 {
691 switch (attrib.Name.LocalName)
692 {
693 case "File":
694 file = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
695 break;
696 case "Order":
697 order = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, Int32.MinValue + 2, Int32.MaxValue);
698 break;
699 case "Source":
700 source = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
701 break;
702 default:
703 this.Core.UnexpectedAttribute(node, attrib);
704 break;
705 }
706 }
707 else
708 {
709 this.Core.ParseExtensionAttribute(node, attrib);
710 }
711 }
712
713 if (null == file)
714 {
715 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "File"));
716 }
717
718 if (null == source)
719 {
720 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Source"));
721 }
722
723 if (CompilerConstants.IntegerNotSet == order)
724 {
725 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Order"));
726 }
727
728 foreach (var child in node.Elements())
729 {
730 if (CompilerCore.WixNamespace == child.Name.Namespace)
731 {
732 switch (child.Name.LocalName)
733 {
734 case "IgnoreRange":
735 this.ParseRangeElement(child, ref ignoreOffsets, ref ignoreLengths);
736 break;
737 case "ProtectRange":
738 this.ParseRangeElement(child, ref protectOffsets, ref protectLengths);
739 break;
740 case "SymbolPath":
741 symbols = this.ParseSymbolPathElement(child);
742 break;
743 default:
744 this.Core.UnexpectedElement(node, child);
745 break;
746 }
747 }
748 else
749 {
750 this.Core.ParseExtensionElement(node, child);
751 }
752 }
753
754 if (!this.Core.EncounteredError)
755 {
756 var symbol = this.Core.AddSymbol(new ExternalFilesSymbol(sourceLineNumbers)
757 {
758 Family = family,
759 FTK = file,
760 FilePath = source,
761 SymbolPaths = symbols,
762 IgnoreOffsets = ignoreOffsets,
763 IgnoreLengths = ignoreLengths,
764 });
765
766 if (null != protectOffsets)
767 {
768 symbol.RetainOffsets = protectOffsets;
769 }
770
771 if (CompilerConstants.IntegerNotSet != order)
772 {
773 symbol.Order = order;
774 }
775
776 if (null != protectOffsets)
777 {
778 this.Core.AddSymbol(new FamilyFileRangesSymbol(sourceLineNumbers)
779 {
780 Family = family,
781 FTK = file,
782 RetainOffsets = protectOffsets,
783 RetainLengths = protectLengths,
784 });
785 }
786 }
787 }
788
789 /// <summary>
790 /// Parses a protect file element.
791 /// </summary>
792 /// <param name="node">The element to parse.</param>
793 /// <param name="family">The family for this element.</param>
794 private void ParseProtectFileElement(XElement node, string family)
795 {
796 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
797 string file = null;
798 string protectLengths = null;
799 string protectOffsets = null;
800
801 foreach (var attrib in node.Attributes())
802 {
803 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
804 {
805 switch (attrib.Name.LocalName)
806 {
807 case "File":
808 file = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
809 break;
810 default:
811 this.Core.UnexpectedAttribute(node, attrib);
812 break;
813 }
814 }
815 else
816 {
817 this.Core.ParseExtensionAttribute(node, attrib);
818 }
819 }
820
821 if (null == file)
822 {
823 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "File"));
824 }
825
826 foreach (var child in node.Elements())
827 {
828 if (CompilerCore.WixNamespace == child.Name.Namespace)
829 {
830 switch (child.Name.LocalName)
831 {
832 case "ProtectRange":
833 this.ParseRangeElement(child, ref protectOffsets, ref protectLengths);
834 break;
835 default:
836 this.Core.UnexpectedElement(node, child);
837 break;
838 }
839 }
840 else
841 {
842 this.Core.ParseExtensionElement(node, child);
843 }
844 }
845
846 if (null == protectOffsets || null == protectLengths)
847 {
848 this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "ProtectRange"));
849 }
850
851 if (!this.Core.EncounteredError)
852 {
853 this.Core.AddSymbol(new FamilyFileRangesSymbol(sourceLineNumbers)
854 {
855 Family = family,
856 FTK = file,
857 RetainOffsets = protectOffsets,
858 RetainLengths = protectLengths
859 });
860 }
861 }
862
863 /// <summary>
864 /// Parses a range element (ProtectRange, IgnoreRange, etc).
865 /// </summary>
866 /// <param name="node">The element to parse.</param>
867 /// <param name="offsets">Reference to the offsets string.</param>
868 /// <param name="lengths">Reference to the lengths string.</param>
869 private void ParseRangeElement(XElement node, ref string offsets, ref string lengths)
870 {
871 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
872 string length = null;
873 string offset = null;
874
875 foreach (var attrib in node.Attributes())
876 {
877 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
878 {
879 switch (attrib.Name.LocalName)
880 {
881 case "Length":
882 length = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
883 break;
884 case "Offset":
885 offset = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
886 break;
887 default:
888 this.Core.UnexpectedAttribute(node, attrib);
889 break;
890 }
891 }
892 else
893 {
894 this.Core.ParseExtensionAttribute(node, attrib);
895 }
896 }
897
898 if (null == length)
899 {
900 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Length"));
901 }
902
903 if (null == offset)
904 {
905 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Offset"));
906 }
907
908 this.Core.ParseForExtensionElements(node);
909
910 if (null != lengths)
911 {
912 lengths = String.Concat(lengths, ",", length);
913 }
914 else
915 {
916 lengths = length;
917 }
918
919 if (null != offsets)
920 {
921 offsets = String.Concat(offsets, ",", offset);
922 }
923 else
924 {
925 offsets = offset;
926 }
927 }
928
929 /// <summary>
930 /// Parses a patch metadata element.
931 /// </summary>
932 /// <param name="node">Element to parse.</param>
933 private void ParsePatchMetadataElement(XElement node)
934 {
935 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
936 var allowRemoval = YesNoType.NotSet;
937 string classification = null;
938 string creationTimeUtc = null;
939 string description = null;
940 string displayName = null;
941 string manufacturerName = null;
942 string minorUpdateTargetRTM = null;
943 string moreInfoUrl = null;
944 var optimizeCA = CompilerConstants.IntegerNotSet;
945 var optimizedInstallMode = YesNoType.NotSet;
946 string targetProductName = null;
947
948 foreach (var attrib in node.Attributes())
949 {
950 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
951 {
952 switch (attrib.Name.LocalName)
953 {
954 case "AllowRemoval":
955 allowRemoval = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
956 break;
957 case "Classification":
958 classification = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
959 break;
960 case "CreationTimeUTC":
961 creationTimeUtc = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
962 break;
963 case "Description":
964 description = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
965 break;
966 case "DisplayName":
967 displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
968 break;
969 case "ManufacturerName":
970 manufacturerName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
971 break;
972 case "MinorUpdateTargetRTM":
973 minorUpdateTargetRTM = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
974 break;
975 case "MoreInfoURL":
976 moreInfoUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
977 break;
978 case "OptimizedInstallMode":
979 optimizedInstallMode = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
980 break;
981 case "TargetProductName":
982 targetProductName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
983 break;
984 default:
985 this.Core.UnexpectedAttribute(node, attrib);
986 break;
987 }
988 }
989 else
990 {
991 this.Core.ParseExtensionAttribute(node, attrib);
992 }
993 }
994
995 if (YesNoType.NotSet == allowRemoval)
996 {
997 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "AllowRemoval"));
998 }
999
1000 if (null == classification)
1001 {
1002 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Classification"));
1003 }
1004
1005 if (null == description)
1006 {
1007 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Description"));
1008 }
1009
1010 if (null == displayName)
1011 {
1012 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DisplayName"));
1013 }
1014
1015 if (null == manufacturerName)
1016 {
1017 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ManufacturerName"));
1018 }
1019
1020 if (null == moreInfoUrl)
1021 {
1022 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "MoreInfoURL"));
1023 }
1024
1025 if (null == targetProductName)
1026 {
1027 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "TargetProductName"));
1028 }
1029
1030 foreach (var child in node.Elements())
1031 {
1032 if (CompilerCore.WixNamespace == child.Name.Namespace)
1033 {
1034 switch (child.Name.LocalName)
1035 {
1036 case "CustomProperty":
1037 this.ParseCustomPropertyElement(child);
1038 break;
1039 case "OptimizeCustomActions":
1040 optimizeCA = this.ParseOptimizeCustomActionsElement(child);
1041 break;
1042 default:
1043 this.Core.UnexpectedElement(node, child);
1044 break;
1045 }
1046 }
1047 else
1048 {
1049 this.Core.ParseExtensionElement(node, child);
1050 }
1051 }
1052
1053 if (!this.Core.EncounteredError)
1054 {
1055 if (YesNoType.NotSet != allowRemoval)
1056 {
1057 this.AddPatchMetadata(sourceLineNumbers, null, "AllowRemoval", YesNoType.Yes == allowRemoval ? "1" : "0");
1058 }
1059
1060 if (null != classification)
1061 {
1062 this.AddPatchMetadata(sourceLineNumbers, null, "Classification", classification);
1063 }
1064
1065 if (null != creationTimeUtc)
1066 {
1067 this.AddPatchMetadata(sourceLineNumbers, null, "CreationTimeUTC", creationTimeUtc);
1068 }
1069
1070 if (null != description)
1071 {
1072 this.AddPatchMetadata(sourceLineNumbers, null, "Description", description);
1073 }
1074
1075 if (null != displayName)
1076 {
1077 this.AddPatchMetadata(sourceLineNumbers, null, "DisplayName", displayName);
1078 }
1079
1080 if (null != manufacturerName)
1081 {
1082 this.AddPatchMetadata(sourceLineNumbers, null, "ManufacturerName", manufacturerName);
1083 }
1084
1085 if (null != minorUpdateTargetRTM)
1086 {
1087 this.AddPatchMetadata(sourceLineNumbers, null, "MinorUpdateTargetRTM", minorUpdateTargetRTM);
1088 }
1089
1090 if (null != moreInfoUrl)
1091 {
1092 this.AddPatchMetadata(sourceLineNumbers, null, "MoreInfoURL", moreInfoUrl);
1093 }
1094
1095 if (CompilerConstants.IntegerNotSet != optimizeCA)
1096 {
1097 this.AddPatchMetadata(sourceLineNumbers, null, "OptimizeCA", optimizeCA.ToString(CultureInfo.InvariantCulture));
1098 }
1099
1100 if (YesNoType.NotSet != optimizedInstallMode)
1101 {
1102 this.AddPatchMetadata(sourceLineNumbers, null, "OptimizedInstallMode", YesNoType.Yes == optimizedInstallMode ? "1" : "0");
1103 }
1104
1105 if (null != targetProductName)
1106 {
1107 this.AddPatchMetadata(sourceLineNumbers, null, "TargetProductName", targetProductName);
1108 }
1109 }
1110 }
1111
1112 /// <summary>
1113 /// Parses a custom property element for the PatchMetadata table.
1114 /// </summary>
1115 /// <param name="node">Element to parse.</param>
1116 private void ParseCustomPropertyElement(XElement node)
1117 {
1118 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1119 string company = null;
1120 string property = null;
1121 string value = null;
1122
1123 foreach (var attrib in node.Attributes())
1124 {
1125 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1126 {
1127 switch (attrib.Name.LocalName)
1128 {
1129 case "Company":
1130 company = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1131 break;
1132 case "Property":
1133 property = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1134 break;
1135 case "Value":
1136 value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1137 break;
1138 default:
1139 this.Core.UnexpectedAttribute(node, attrib);
1140 break;
1141 }
1142 }
1143 else
1144 {
1145 this.Core.ParseExtensionAttribute(node, attrib);
1146 }
1147 }
1148
1149 if (null == company)
1150 {
1151 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Company"));
1152 }
1153
1154 if (null == property)
1155 {
1156 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property"));
1157 }
1158
1159 if (null == value)
1160 {
1161 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
1162 }
1163
1164 this.Core.ParseForExtensionElements(node);
1165
1166 if (!this.Core.EncounteredError)
1167 {
1168 this.AddPatchMetadata(sourceLineNumbers, company, property, value);
1169 }
1170 }
1171
1172 /// <summary>
1173 /// Parses a patch sequence element.
1174 /// </summary>
1175 /// <param name="node">The element to parse.</param>
1176 private void ParsePatchSequenceElement(XElement node)
1177 {
1178 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1179 string family = null;
1180 string target = null;
1181 string sequence = null;
1182 var attributes = 0;
1183
1184 foreach (var attrib in node.Attributes())
1185 {
1186 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1187 {
1188 switch (attrib.Name.LocalName)
1189 {
1190 case "PatchFamily":
1191 family = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
1192 break;
1193 case "ProductCode":
1194 if (null != target)
1195 {
1196 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Target", "TargetImage"));
1197 }
1198 target = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
1199 break;
1200 case "Target":
1201 if (null != target)
1202 {
1203 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "TargetImage", "ProductCode"));
1204 }
1205 this.Core.Write(WarningMessages.DeprecatedPatchSequenceTargetAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
1206 target = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1207 break;
1208 case "TargetImage":
1209 if (null != target)
1210 {
1211 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Target", "ProductCode"));
1212 }
1213 target = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1214 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.TargetImages, target);
1215 break;
1216 case "Sequence":
1217 sequence = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
1218 break;
1219 case "Supersede":
1220 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
1221 {
1222 attributes |= 0x1;
1223 }
1224 break;
1225 default:
1226 this.Core.UnexpectedAttribute(node, attrib);
1227 break;
1228 }
1229 }
1230 else
1231 {
1232 this.Core.ParseExtensionAttribute(node, attrib);
1233 }
1234 }
1235
1236 if (null == family)
1237 {
1238 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "PatchFamily"));
1239 }
1240
1241 this.Core.ParseForExtensionElements(node);
1242
1243 if (!this.Core.EncounteredError)
1244 {
1245 this.Core.AddSymbol(new PatchSequenceSymbol(sourceLineNumbers)
1246 {
1247 PatchFamily = family,
1248 Target = target,
1249 Sequence = sequence,
1250 Supersede = attributes,
1251 });
1252 }
1253 }
1254
1255 private void AddPatchMetadata(SourceLineNumber sourceLineNumbers, string company, string property, string value)
1256 {
1257 this.Core.AddSymbol(new PatchMetadataSymbol(sourceLineNumbers, new Identifier(AccessModifier.Section, company, property))
1258 {
1259 Company = company,
1260 Property = property,
1261 Value = value,
1262 });
1263 }
1264 }
1265 }