aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/Compiler_PatchCreation.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/WixToolset.Core/Compiler_PatchCreation.cs1313
1 files changed, 1313 insertions, 0 deletions
diff --git a/src/WixToolset.Core/Compiler_PatchCreation.cs b/src/WixToolset.Core/Compiler_PatchCreation.cs
new file mode 100644
index 00000000..42cdf374
--- /dev/null
+++ b/src/WixToolset.Core/Compiler_PatchCreation.cs
@@ -0,0 +1,1313 @@
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.Globalization;
8 using System.Xml.Linq;
9 using WixToolset.Data;
10 using WixToolset.Data.Tuples;
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, codepage, 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 tuple = new ImageFamiliesTuple(sourceLineNumbers)
262 {
263 Family = name,
264 MediaSrcPropName = mediaSrcProp,
265 DiskPrompt = diskPrompt,
266 VolumeLabel = volumeLabel
267 };
268
269 if (CompilerConstants.IntegerNotSet != diskId)
270 {
271 tuple.MediaDiskId = diskId;
272 }
273
274 if (CompilerConstants.IntegerNotSet != sequenceStart)
275 {
276 tuple.FileSequenceStart = sequenceStart;
277 }
278
279 this.Core.AddTuple(tuple);
280 }
281 }
282
283 /// <summary>
284 /// Parses an upgrade image element.
285 /// </summary>
286 /// <param name="node">The element to parse.</param>
287 /// <param name="family">The family for this element.</param>
288 private void ParseUpgradeImageElement(XElement node, string family)
289 {
290 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
291 string sourceFile = null;
292 string sourcePatch = null;
293 var symbols = new List<string>();
294 string upgrade = null;
295
296 foreach (var attrib in node.Attributes())
297 {
298 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
299 {
300 switch (attrib.Name.LocalName)
301 {
302 case "Id":
303 upgrade = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
304 if (13 < upgrade.Length)
305 {
306 this.Core.Write(ErrorMessages.IdentifierTooLongError(sourceLineNumbers, node.Name.LocalName, "Id", upgrade, 13));
307 }
308 break;
309 case "SourceFile":
310 case "src":
311 if (null != sourceFile)
312 {
313 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "src", "SourceFile"));
314 }
315
316 if ("src" == attrib.Name.LocalName)
317 {
318 this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "SourceFile"));
319 }
320 sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
321 break;
322 case "SourcePatch":
323 case "srcPatch":
324 if (null != sourcePatch)
325 {
326 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "srcPatch", "SourcePatch"));
327 }
328
329 if ("srcPatch" == attrib.Name.LocalName)
330 {
331 this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "SourcePatch"));
332 }
333 sourcePatch = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
334 break;
335 default:
336 this.Core.UnexpectedAttribute(node, attrib);
337 break;
338 }
339 }
340 else
341 {
342 this.Core.ParseExtensionAttribute(node, attrib);
343 }
344 }
345
346 if (null == upgrade)
347 {
348 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
349 }
350
351 if (null == sourceFile)
352 {
353 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
354 }
355
356 foreach (var child in node.Elements())
357 {
358 if (CompilerCore.WixNamespace == child.Name.Namespace)
359 {
360 switch (child.Name.LocalName)
361 {
362 case "SymbolPath":
363 symbols.Add(this.ParseSymbolPathElement(child));
364 break;
365 case "TargetImage":
366 this.ParseTargetImageElement(child, upgrade, family);
367 break;
368 case "UpgradeFile":
369 this.ParseUpgradeFileElement(child, upgrade);
370 break;
371 default:
372 this.Core.UnexpectedElement(node, child);
373 break;
374 }
375 }
376 else
377 {
378 this.Core.ParseExtensionElement(node, child);
379 }
380 }
381
382 if (!this.Core.EncounteredError)
383 {
384 this.Core.AddTuple(new UpgradedImagesTuple(sourceLineNumbers)
385 {
386 Upgraded = upgrade,
387 MsiPath = sourceFile,
388 PatchMsiPath = sourcePatch,
389 SymbolPaths = String.Join(";", symbols),
390 Family = family
391 });
392 }
393 }
394
395 /// <summary>
396 /// Parses an upgrade file element.
397 /// </summary>
398 /// <param name="node">The element to parse.</param>
399 /// <param name="upgrade">The upgrade key for this element.</param>
400 private void ParseUpgradeFileElement(XElement node, string upgrade)
401 {
402 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
403 var allowIgnoreOnError = false;
404 string file = null;
405 var ignore = false;
406 var symbols = new List<string>();
407 var wholeFile = false;
408
409 foreach (var attrib in node.Attributes())
410 {
411 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
412 {
413 switch (attrib.Name.LocalName)
414 {
415 case "AllowIgnoreOnError":
416 allowIgnoreOnError = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
417 break;
418 case "File":
419 file = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
420 break;
421 case "Ignore":
422 ignore = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
423 break;
424 case "WholeFile":
425 wholeFile = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
426 break;
427 default:
428 this.Core.UnexpectedAttribute(node, attrib);
429 break;
430 }
431 }
432 else
433 {
434 this.Core.ParseExtensionAttribute(node, attrib);
435 }
436 }
437
438 if (null == file)
439 {
440 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "File"));
441 }
442
443 foreach (var child in node.Elements())
444 {
445 if (CompilerCore.WixNamespace == child.Name.Namespace)
446 {
447 switch (child.Name.LocalName)
448 {
449 case "SymbolPath":
450 symbols.Add(this.ParseSymbolPathElement(child));
451 break;
452 default:
453 this.Core.UnexpectedElement(node, child);
454 break;
455 }
456 }
457 else
458 {
459 this.Core.ParseExtensionElement(node, child);
460 }
461 }
462
463 if (!this.Core.EncounteredError)
464 {
465 if (ignore)
466 {
467 this.Core.AddTuple(new UpgradedFilesToIgnoreTuple(sourceLineNumbers)
468 {
469 Upgraded = upgrade,
470 FTK = file
471 });
472 }
473 else
474 {
475 this.Core.AddTuple(new UpgradedFiles_OptionalDataTuple(sourceLineNumbers)
476 {
477 Upgraded = upgrade,
478 FTK = file,
479 SymbolPaths = String.Join(";", symbols),
480 AllowIgnoreOnPatchError = allowIgnoreOnError,
481 IncludeWholeFile = wholeFile
482 });
483 }
484 }
485 }
486
487 /// <summary>
488 /// Parses a target image element.
489 /// </summary>
490 /// <param name="node">The element to parse.</param>
491 /// <param name="upgrade">The upgrade key for this element.</param>
492 /// <param name="family">The family key for this element.</param>
493 private void ParseTargetImageElement(XElement node, string upgrade, string family)
494 {
495 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
496 var ignore = false;
497 var order = CompilerConstants.IntegerNotSet;
498 string sourceFile = null;
499 string symbols = null;
500 string target = null;
501 string validation = null;
502
503 foreach (var attrib in node.Attributes())
504 {
505 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
506 {
507 switch (attrib.Name.LocalName)
508 {
509 case "Id":
510 target = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
511 if (target.Length > 13)
512 {
513 this.Core.Write(ErrorMessages.IdentifierTooLongError(sourceLineNumbers, node.Name.LocalName, "Id", target, 13));
514 }
515 break;
516 case "IgnoreMissingFiles":
517 ignore = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
518 break;
519 case "Order":
520 order = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, Int32.MinValue + 2, Int32.MaxValue);
521 break;
522 case "SourceFile":
523 case "src":
524 if (null != sourceFile)
525 {
526 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "src", "SourceFile"));
527 }
528
529 if ("src" == attrib.Name.LocalName)
530 {
531 this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "SourceFile"));
532 }
533 sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
534 break;
535 case "Validation":
536 validation = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
537 break;
538 default:
539 this.Core.UnexpectedAttribute(node, attrib);
540 break;
541 }
542 }
543 else
544 {
545 this.Core.ParseExtensionAttribute(node, attrib);
546 }
547 }
548
549 if (null == target)
550 {
551 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
552 }
553
554 if (null == sourceFile)
555 {
556 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
557 }
558
559 if (CompilerConstants.IntegerNotSet == order)
560 {
561 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Order"));
562 }
563
564 foreach (var child in node.Elements())
565 {
566 if (CompilerCore.WixNamespace == child.Name.Namespace)
567 {
568 switch (child.Name.LocalName)
569 {
570 case "SymbolPath":
571 if (null != symbols)
572 {
573 symbols = String.Concat(symbols, ";", this.ParseSymbolPathElement(child));
574 }
575 else
576 {
577 symbols = this.ParseSymbolPathElement(child);
578 }
579 break;
580 case "TargetFile":
581 this.ParseTargetFileElement(child, target, family);
582 break;
583 default:
584 this.Core.UnexpectedElement(node, child);
585 break;
586 }
587 }
588 else
589 {
590 this.Core.ParseExtensionElement(node, child);
591 }
592 }
593
594 if (!this.Core.EncounteredError)
595 {
596 this.Core.AddTuple(new TargetImagesTuple(sourceLineNumbers)
597 {
598 Target = target,
599 MsiPath = sourceFile,
600 SymbolPaths = symbols,
601 Upgraded = upgrade,
602 Order = order,
603 ProductValidateFlags = validation,
604 IgnoreMissingSrcFiles = ignore
605 });
606 }
607 }
608
609 /// <summary>
610 /// Parses an upgrade file element.
611 /// </summary>
612 /// <param name="node">The element to parse.</param>
613 /// <param name="target">The upgrade key for this element.</param>
614 /// <param name="family">The family key for this element.</param>
615 private void ParseTargetFileElement(XElement node, string target, string family)
616 {
617 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
618 string file = null;
619 string ignoreLengths = null;
620 string ignoreOffsets = null;
621 string protectLengths = null;
622 string protectOffsets = null;
623 string symbols = null;
624
625 foreach (var attrib in node.Attributes())
626 {
627 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
628 {
629 switch (attrib.Name.LocalName)
630 {
631 case "Id":
632 file = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
633 break;
634 default:
635 this.Core.UnexpectedAttribute(node, attrib);
636 break;
637 }
638 }
639 else
640 {
641 this.Core.ParseExtensionAttribute(node, attrib);
642 }
643 }
644
645 if (null == file)
646 {
647 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
648 }
649
650 foreach (var child in node.Elements())
651 {
652 if (CompilerCore.WixNamespace == child.Name.Namespace)
653 {
654 switch (child.Name.LocalName)
655 {
656 case "IgnoreRange":
657 this.ParseRangeElement(child, ref ignoreOffsets, ref ignoreLengths);
658 break;
659 case "ProtectRange":
660 this.ParseRangeElement(child, ref protectOffsets, ref protectLengths);
661 break;
662 case "SymbolPath":
663 symbols = this.ParseSymbolPathElement(child);
664 break;
665 default:
666 this.Core.UnexpectedElement(node, child);
667 break;
668 }
669 }
670 else
671 {
672 this.Core.ParseExtensionElement(node, child);
673 }
674 }
675
676 if (!this.Core.EncounteredError)
677 {
678 var tuple = new TargetFiles_OptionalDataTuple(sourceLineNumbers)
679 {
680 Target = target,
681 FTK = file,
682 SymbolPaths = symbols,
683 IgnoreOffsets = ignoreOffsets,
684 IgnoreLengths = ignoreLengths
685 };
686
687 this.Core.AddTuple(tuple);
688
689 if (null != protectOffsets)
690 {
691 tuple.RetainOffsets = protectOffsets;
692
693 this.Core.AddTuple(new FamilyFileRangesTuple(sourceLineNumbers)
694 {
695 Family = family,
696 FTK = file,
697 RetainOffsets = protectOffsets,
698 RetainLengths = protectLengths
699 });
700 }
701 }
702 }
703
704 /// <summary>
705 /// Parses an external file element.
706 /// </summary>
707 /// <param name="node">The element to parse.</param>
708 /// <param name="family">The family for this element.</param>
709 private void ParseExternalFileElement(XElement node, string family)
710 {
711 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
712 string file = null;
713 string ignoreLengths = null;
714 string ignoreOffsets = null;
715 var order = CompilerConstants.IntegerNotSet;
716 string protectLengths = null;
717 string protectOffsets = null;
718 string source = null;
719 string symbols = null;
720
721 foreach (var attrib in node.Attributes())
722 {
723 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
724 {
725 switch (attrib.Name.LocalName)
726 {
727 case "File":
728 file = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
729 break;
730 case "Order":
731 order = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, Int32.MinValue + 2, Int32.MaxValue);
732 break;
733 case "Source":
734 case "src":
735 if (null != source)
736 {
737 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "src", "Source"));
738 }
739
740 if ("src" == attrib.Name.LocalName)
741 {
742 this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Source"));
743 }
744 source = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
745 break;
746 default:
747 this.Core.UnexpectedAttribute(node, attrib);
748 break;
749 }
750 }
751 else
752 {
753 this.Core.ParseExtensionAttribute(node, attrib);
754 }
755 }
756
757 if (null == file)
758 {
759 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "File"));
760 }
761
762 if (null == source)
763 {
764 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Source"));
765 }
766
767 if (CompilerConstants.IntegerNotSet == order)
768 {
769 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Order"));
770 }
771
772 foreach (var child in node.Elements())
773 {
774 if (CompilerCore.WixNamespace == child.Name.Namespace)
775 {
776 switch (child.Name.LocalName)
777 {
778 case "IgnoreRange":
779 this.ParseRangeElement(child, ref ignoreOffsets, ref ignoreLengths);
780 break;
781 case "ProtectRange":
782 this.ParseRangeElement(child, ref protectOffsets, ref protectLengths);
783 break;
784 case "SymbolPath":
785 symbols = this.ParseSymbolPathElement(child);
786 break;
787 default:
788 this.Core.UnexpectedElement(node, child);
789 break;
790 }
791 }
792 else
793 {
794 this.Core.ParseExtensionElement(node, child);
795 }
796 }
797
798 if (!this.Core.EncounteredError)
799 {
800 var tuple = new ExternalFilesTuple(sourceLineNumbers)
801 {
802 Family = family,
803 FTK = file,
804 FilePath = source,
805 SymbolPaths = symbols,
806 IgnoreOffsets = ignoreOffsets,
807 IgnoreLengths = ignoreLengths
808 };
809
810 if (null != protectOffsets)
811 {
812 tuple.RetainOffsets = protectOffsets;
813 }
814
815 if (CompilerConstants.IntegerNotSet != order)
816 {
817 tuple.Order = order;
818 }
819
820 this.Core.AddTuple(tuple);
821
822 if (null != protectOffsets)
823 {
824 this.Core.AddTuple(new FamilyFileRangesTuple(sourceLineNumbers)
825 {
826 Family = family,
827 FTK = file,
828 RetainOffsets = protectOffsets,
829 RetainLengths = protectLengths
830 });
831 }
832 }
833 }
834
835 /// <summary>
836 /// Parses a protect file element.
837 /// </summary>
838 /// <param name="node">The element to parse.</param>
839 /// <param name="family">The family for this element.</param>
840 private void ParseProtectFileElement(XElement node, string family)
841 {
842 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
843 string file = null;
844 string protectLengths = null;
845 string protectOffsets = null;
846
847 foreach (var attrib in node.Attributes())
848 {
849 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
850 {
851 switch (attrib.Name.LocalName)
852 {
853 case "File":
854 file = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
855 break;
856 default:
857 this.Core.UnexpectedAttribute(node, attrib);
858 break;
859 }
860 }
861 else
862 {
863 this.Core.ParseExtensionAttribute(node, attrib);
864 }
865 }
866
867 if (null == file)
868 {
869 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "File"));
870 }
871
872 foreach (var child in node.Elements())
873 {
874 if (CompilerCore.WixNamespace == child.Name.Namespace)
875 {
876 switch (child.Name.LocalName)
877 {
878 case "ProtectRange":
879 this.ParseRangeElement(child, ref protectOffsets, ref protectLengths);
880 break;
881 default:
882 this.Core.UnexpectedElement(node, child);
883 break;
884 }
885 }
886 else
887 {
888 this.Core.ParseExtensionElement(node, child);
889 }
890 }
891
892 if (null == protectOffsets || null == protectLengths)
893 {
894 this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "ProtectRange"));
895 }
896
897 if (!this.Core.EncounteredError)
898 {
899 this.Core.AddTuple(new FamilyFileRangesTuple(sourceLineNumbers)
900 {
901 Family = family,
902 FTK = file,
903 RetainOffsets = protectOffsets,
904 RetainLengths = protectLengths
905 });
906 }
907 }
908
909 /// <summary>
910 /// Parses a range element (ProtectRange, IgnoreRange, etc).
911 /// </summary>
912 /// <param name="node">The element to parse.</param>
913 /// <param name="offsets">Reference to the offsets string.</param>
914 /// <param name="lengths">Reference to the lengths string.</param>
915 private void ParseRangeElement(XElement node, ref string offsets, ref string lengths)
916 {
917 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
918 string length = null;
919 string offset = null;
920
921 foreach (var attrib in node.Attributes())
922 {
923 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
924 {
925 switch (attrib.Name.LocalName)
926 {
927 case "Length":
928 length = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
929 break;
930 case "Offset":
931 offset = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
932 break;
933 default:
934 this.Core.UnexpectedAttribute(node, attrib);
935 break;
936 }
937 }
938 else
939 {
940 this.Core.ParseExtensionAttribute(node, attrib);
941 }
942 }
943
944 if (null == length)
945 {
946 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Length"));
947 }
948
949 if (null == offset)
950 {
951 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Offset"));
952 }
953
954 this.Core.ParseForExtensionElements(node);
955
956 if (null != lengths)
957 {
958 lengths = String.Concat(lengths, ",", length);
959 }
960 else
961 {
962 lengths = length;
963 }
964
965 if (null != offsets)
966 {
967 offsets = String.Concat(offsets, ",", offset);
968 }
969 else
970 {
971 offsets = offset;
972 }
973 }
974
975 /// <summary>
976 /// Parses a patch metadata element.
977 /// </summary>
978 /// <param name="node">Element to parse.</param>
979 private void ParsePatchMetadataElement(XElement node)
980 {
981 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
982 var allowRemoval = YesNoType.NotSet;
983 string classification = null;
984 string creationTimeUtc = null;
985 string description = null;
986 string displayName = null;
987 string manufacturerName = null;
988 string minorUpdateTargetRTM = null;
989 string moreInfoUrl = null;
990 var optimizeCA = CompilerConstants.IntegerNotSet;
991 var optimizedInstallMode = YesNoType.NotSet;
992 string targetProductName = null;
993
994 foreach (var attrib in node.Attributes())
995 {
996 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
997 {
998 switch (attrib.Name.LocalName)
999 {
1000 case "AllowRemoval":
1001 allowRemoval = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1002 break;
1003 case "Classification":
1004 classification = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1005 break;
1006 case "CreationTimeUTC":
1007 creationTimeUtc = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1008 break;
1009 case "Description":
1010 description = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1011 break;
1012 case "DisplayName":
1013 displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1014 break;
1015 case "ManufacturerName":
1016 manufacturerName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1017 break;
1018 case "MinorUpdateTargetRTM":
1019 minorUpdateTargetRTM = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1020 break;
1021 case "MoreInfoURL":
1022 moreInfoUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1023 break;
1024 case "OptimizedInstallMode":
1025 optimizedInstallMode = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1026 break;
1027 case "TargetProductName":
1028 targetProductName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1029 break;
1030 default:
1031 this.Core.UnexpectedAttribute(node, attrib);
1032 break;
1033 }
1034 }
1035 else
1036 {
1037 this.Core.ParseExtensionAttribute(node, attrib);
1038 }
1039 }
1040
1041 if (YesNoType.NotSet == allowRemoval)
1042 {
1043 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "AllowRemoval"));
1044 }
1045
1046 if (null == classification)
1047 {
1048 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Classification"));
1049 }
1050
1051 if (null == description)
1052 {
1053 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Description"));
1054 }
1055
1056 if (null == displayName)
1057 {
1058 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DisplayName"));
1059 }
1060
1061 if (null == manufacturerName)
1062 {
1063 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ManufacturerName"));
1064 }
1065
1066 if (null == moreInfoUrl)
1067 {
1068 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "MoreInfoURL"));
1069 }
1070
1071 if (null == targetProductName)
1072 {
1073 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "TargetProductName"));
1074 }
1075
1076 foreach (var child in node.Elements())
1077 {
1078 if (CompilerCore.WixNamespace == child.Name.Namespace)
1079 {
1080 switch (child.Name.LocalName)
1081 {
1082 case "CustomProperty":
1083 this.ParseCustomPropertyElement(child);
1084 break;
1085 case "OptimizeCustomActions":
1086 optimizeCA = this.ParseOptimizeCustomActionsElement(child);
1087 break;
1088 default:
1089 this.Core.UnexpectedElement(node, child);
1090 break;
1091 }
1092 }
1093 else
1094 {
1095 this.Core.ParseExtensionElement(node, child);
1096 }
1097 }
1098
1099 if (!this.Core.EncounteredError)
1100 {
1101 if (YesNoType.NotSet != allowRemoval)
1102 {
1103 this.AddPatchMetadata(sourceLineNumbers, null, "AllowRemoval", YesNoType.Yes == allowRemoval ? "1" : "0");
1104 }
1105
1106 if (null != classification)
1107 {
1108 this.AddPatchMetadata(sourceLineNumbers, null, "Classification", classification);
1109 }
1110
1111 if (null != creationTimeUtc)
1112 {
1113 this.AddPatchMetadata(sourceLineNumbers, null, "CreationTimeUTC", creationTimeUtc);
1114 }
1115
1116 if (null != description)
1117 {
1118 this.AddPatchMetadata(sourceLineNumbers, null, "Description", description);
1119 }
1120
1121 if (null != displayName)
1122 {
1123 this.AddPatchMetadata(sourceLineNumbers, null, "DisplayName", displayName);
1124 }
1125
1126 if (null != manufacturerName)
1127 {
1128 this.AddPatchMetadata(sourceLineNumbers, null, "ManufacturerName", manufacturerName);
1129 }
1130
1131 if (null != minorUpdateTargetRTM)
1132 {
1133 this.AddPatchMetadata(sourceLineNumbers, null, "MinorUpdateTargetRTM", minorUpdateTargetRTM);
1134 }
1135
1136 if (null != moreInfoUrl)
1137 {
1138 this.AddPatchMetadata(sourceLineNumbers, null, "MoreInfoURL", moreInfoUrl);
1139 }
1140
1141 if (CompilerConstants.IntegerNotSet != optimizeCA)
1142 {
1143 this.AddPatchMetadata(sourceLineNumbers, null, "OptimizeCA", optimizeCA.ToString(CultureInfo.InvariantCulture));
1144 }
1145
1146 if (YesNoType.NotSet != optimizedInstallMode)
1147 {
1148 this.AddPatchMetadata(sourceLineNumbers, null, "OptimizedInstallMode", YesNoType.Yes == optimizedInstallMode ? "1" : "0");
1149 }
1150
1151 if (null != targetProductName)
1152 {
1153 this.AddPatchMetadata(sourceLineNumbers, null, "TargetProductName", targetProductName);
1154 }
1155 }
1156 }
1157
1158 /// <summary>
1159 /// Parses a custom property element for the PatchMetadata table.
1160 /// </summary>
1161 /// <param name="node">Element to parse.</param>
1162 private void ParseCustomPropertyElement(XElement node)
1163 {
1164 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1165 string company = null;
1166 string property = null;
1167 string value = null;
1168
1169 foreach (var attrib in node.Attributes())
1170 {
1171 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1172 {
1173 switch (attrib.Name.LocalName)
1174 {
1175 case "Company":
1176 company = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1177 break;
1178 case "Property":
1179 property = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1180 break;
1181 case "Value":
1182 value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1183 break;
1184 default:
1185 this.Core.UnexpectedAttribute(node, attrib);
1186 break;
1187 }
1188 }
1189 else
1190 {
1191 this.Core.ParseExtensionAttribute(node, attrib);
1192 }
1193 }
1194
1195 if (null == company)
1196 {
1197 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Company"));
1198 }
1199
1200 if (null == property)
1201 {
1202 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property"));
1203 }
1204
1205 if (null == value)
1206 {
1207 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
1208 }
1209
1210 this.Core.ParseForExtensionElements(node);
1211
1212 if (!this.Core.EncounteredError)
1213 {
1214 this.AddPatchMetadata(sourceLineNumbers, company, property, value);
1215 }
1216 }
1217
1218 /// <summary>
1219 /// Parses a patch sequence element.
1220 /// </summary>
1221 /// <param name="node">The element to parse.</param>
1222 private void ParsePatchSequenceElement(XElement node)
1223 {
1224 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1225 string family = null;
1226 string target = null;
1227 string sequence = null;
1228 var attributes = 0;
1229
1230 foreach (var attrib in node.Attributes())
1231 {
1232 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1233 {
1234 switch (attrib.Name.LocalName)
1235 {
1236 case "PatchFamily":
1237 family = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
1238 break;
1239 case "ProductCode":
1240 if (null != target)
1241 {
1242 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Target", "TargetImage"));
1243 }
1244 target = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
1245 break;
1246 case "Target":
1247 if (null != target)
1248 {
1249 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "TargetImage", "ProductCode"));
1250 }
1251 this.Core.Write(WarningMessages.DeprecatedPatchSequenceTargetAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
1252 target = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1253 break;
1254 case "TargetImage":
1255 if (null != target)
1256 {
1257 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Target", "ProductCode"));
1258 }
1259 target = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1260 this.Core.CreateSimpleReference(sourceLineNumbers, "TargetImages", target);
1261 break;
1262 case "Sequence":
1263 sequence = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
1264 break;
1265 case "Supersede":
1266 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
1267 {
1268 attributes |= 0x1;
1269 }
1270 break;
1271 default:
1272 this.Core.UnexpectedAttribute(node, attrib);
1273 break;
1274 }
1275 }
1276 else
1277 {
1278 this.Core.ParseExtensionAttribute(node, attrib);
1279 }
1280 }
1281
1282 if (null == family)
1283 {
1284 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "PatchFamily"));
1285 }
1286
1287 this.Core.ParseForExtensionElements(node);
1288
1289 if (!this.Core.EncounteredError)
1290 {
1291 var tuple = new PatchSequenceTuple(sourceLineNumbers)
1292 {
1293 PatchFamily = family,
1294 Target = target,
1295 Sequence = sequence,
1296 Supersede = attributes
1297 };
1298
1299 this.Core.AddTuple(tuple);
1300 }
1301 }
1302
1303 private void AddPatchMetadata(SourceLineNumber sourceLineNumbers, string company, string property, string value)
1304 {
1305 this.Core.AddTuple(new PatchMetadataTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, company, property))
1306 {
1307 Company = company,
1308 Property = property,
1309 Value = value
1310 });
1311 }
1312 }
1313}