main
cs 688 lines 29.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.Globalization;
7 using System.Xml.Linq;
8 using WixToolset.Data;
9 using WixToolset.Data.Symbols;
10 using WixToolset.Extensibility;
11
12 /// <summary>
13 /// Compiler of the WiX toolset.
14 /// </summary>
15 internal partial class Compiler : ICompiler
16 {
17 /// <summary>
18 /// Parses a module element.
19 /// </summary>
20 /// <param name="node">Element to parse.</param>
21 private void ParseModuleElement(XElement node)
22 {
23 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
24 var codepage = 0;
25 string moduleId = null;
26 string version = null;
27 var setCodepage = false;
28 var setComments = false;
29 var setPackageName = false;
30 var setKeywords = false;
31 var ignoredForMergeModules = false;
32
33 this.GetDefaultPlatformAndInstallerVersion(out var platform, out var msiVersion);
34
35 this.activeName = null;
36 this.activeLanguage = null;
37
38 foreach (var attrib in node.Attributes())
39 {
40 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
41 {
42 switch (attrib.Name.LocalName)
43 {
44 case "Id":
45 this.activeName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
46 if ("PUT-MODULE-NAME-HERE" == this.activeName)
47 {
48 this.Core.Write(WarningMessages.PlaceholderValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, this.activeName));
49 }
50 else
51 {
52 this.activeName = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
53 }
54 break;
55 case "Codepage":
56 codepage = this.Core.GetAttributeCodePageValue(sourceLineNumbers, attrib);
57 break;
58 case "Guid":
59 moduleId = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
60 break;
61 case "InstallerVersion":
62 msiVersion = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
63 break;
64 case "Language":
65 this.activeLanguage = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
66 break;
67 case "Version":
68 version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
69 break;
70 default:
71 this.Core.UnexpectedAttribute(node, attrib);
72 break;
73 }
74 }
75 else
76 {
77 this.Core.ParseExtensionAttribute(node, attrib);
78 }
79 }
80
81 if (null == this.activeName)
82 {
83 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
84 }
85
86 if (null == moduleId)
87 {
88 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Guid"));
89 }
90
91 if (null == this.activeLanguage)
92 {
93 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Language"));
94 }
95
96 if (null == version)
97 {
98 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
99 }
100
101 try
102 {
103 this.compilingModule = true; // notice that we are actually building a Merge Module here
104 this.Core.CreateActiveSection(this.activeName, SectionType.Module, this.Context.CompilationId);
105
106 foreach (var child in node.Elements())
107 {
108 if (CompilerCore.WixNamespace == child.Name.Namespace)
109 {
110 switch (child.Name.LocalName)
111 {
112 case "AdminExecuteSequence":
113 this.ParseSequenceElement(child, SequenceTable.AdminExecuteSequence);
114 break;
115 case "AdminUISequence":
116 this.ParseSequenceElement(child, SequenceTable.AdminUISequence);
117 break;
118 case "AdvertiseExecuteSequence":
119 this.ParseSequenceElement(child, SequenceTable.AdvertiseExecuteSequence);
120 break;
121 case "InstallExecuteSequence":
122 this.ParseSequenceElement(child, SequenceTable.InstallExecuteSequence);
123 break;
124 case "InstallUISequence":
125 this.ParseSequenceElement(child, SequenceTable.InstallUISequence);
126 break;
127 case "AppId":
128 this.ParseAppIdElement(child, null, YesNoType.Yes, null, null, null);
129 break;
130 case "Binary":
131 this.ParseBinaryElement(child);
132 break;
133 case "Component":
134 this.ParseComponentElement(child, ComplexReferenceParentType.Module, this.activeName, this.activeLanguage, CompilerConstants.IntegerNotSet, null, null);
135 break;
136 case "ComponentGroupRef":
137 this.ParseComponentGroupRefElement(child, ComplexReferenceParentType.Module, this.activeName, this.activeLanguage);
138 break;
139 case "ComponentRef":
140 this.ParseComponentRefElement(child, ComplexReferenceParentType.Module, this.activeName, this.activeLanguage);
141 break;
142 case "Configuration":
143 this.ParseConfigurationElement(child);
144 break;
145 case "CustomAction":
146 this.ParseCustomActionElement(child);
147 break;
148 case "CustomActionRef":
149 this.ParseSimpleRefElement(child, SymbolDefinitions.CustomAction);
150 break;
151 case "CustomTable":
152 this.ParseCustomTableElement(child);
153 break;
154 case "CustomTableRef":
155 this.ParseCustomTableRefElement(child);
156 break;
157 case "Dependency":
158 this.ParseDependencyElement(child);
159 break;
160 case "Directory":
161 this.ParseDirectoryElement(child, null, CompilerConstants.IntegerNotSet, String.Empty);
162 break;
163 case "DirectoryRef":
164 this.ParseDirectoryRefElement(child);
165 break;
166 case "EmbeddedChainer":
167 this.ParseEmbeddedChainerElement(child);
168 break;
169 case "EmbeddedChainerRef":
170 this.ParseSimpleRefElement(child, SymbolDefinitions.MsiEmbeddedChainer);
171 break;
172 case "EnsureTable":
173 this.ParseEnsureTableElement(child);
174 break;
175 case "Exclusion":
176 this.ParseExclusionElement(child);
177 break;
178 case "File":
179 this.ParseNakedFileElement(child, ComplexReferenceParentType.Module, this.activeName, null, null);
180 break;
181 case "Files":
182 this.ParseFilesElement(child, ComplexReferenceParentType.Module, this.activeName, null, null);
183 break;
184 case "Icon":
185 this.ParseIconElement(child);
186 break;
187 case "IgnoreTable":
188 this.ParseIgnoreTableElement(child);
189 break;
190 case "Property":
191 this.ParsePropertyElement(child);
192 break;
193 case "PropertyRef":
194 this.ParseSimpleRefElement(child, SymbolDefinitions.Property);
195 break;
196 case "Requires":
197 this.ParseRequiresElement(child, null);
198 break;
199 case "SetDirectory":
200 this.ParseSetDirectoryElement(child);
201 break;
202 case "SetProperty":
203 this.ParseSetPropertyElement(child);
204 break;
205 case "SFPCatalog":
206 string parentName = null;
207 this.ParseSFPCatalogElement(child, ref parentName);
208 break;
209 case "StandardDirectory":
210 this.ParseStandardDirectoryElement(child);
211 break;
212 case "Substitution":
213 this.ParseSubstitutionElement(child);
214 break;
215 case "SummaryInformation":
216 this.ParseSummaryInformationElement(child, ref setCodepage, ref setComments, ref setPackageName, ref setKeywords, ref ignoredForMergeModules);
217 break;
218 case "UI":
219 this.ParseUIElement(child);
220 break;
221 case "UIRef":
222 this.ParseSimpleRefElement(child, SymbolDefinitions.WixUI);
223 break;
224 case "WixVariable":
225 this.ParseWixVariableElement(child);
226 break;
227 default:
228 this.Core.UnexpectedElement(node, child);
229 break;
230 }
231 }
232 else
233 {
234 this.Core.ParseExtensionElement(node, child);
235 }
236 }
237
238
239 if (!this.Core.EncounteredError)
240 {
241 if (!setPackageName)
242 {
243 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
244 {
245 PropertyId = SummaryInformationType.Subject,
246 Value = this.activeName
247 });
248 }
249
250 if (!setKeywords)
251 {
252 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
253 {
254 PropertyId = SummaryInformationType.Keywords,
255 Value = "MergeModule, MSI, database"
256 });
257 }
258
259 var symbol = this.Core.AddSymbol(new WixModuleSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, this.activeName, this.activeLanguage))
260 {
261 ModuleId = this.activeName,
262 Language = this.activeLanguage,
263 Version = version
264 });
265
266 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
267 {
268 PropertyId = SummaryInformationType.PackageCode,
269 Value = moduleId
270 });
271
272 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
273 {
274 PropertyId = SummaryInformationType.Title,
275 Value = "Merge Module"
276 });
277
278 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
279 {
280 PropertyId = SummaryInformationType.WordCount,
281 Value = "0"
282 });
283
284 if (!setComments)
285 {
286 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
287 {
288 PropertyId = SummaryInformationType.Comments,
289 Value = String.Format(CultureInfo.InvariantCulture, "This merge module contains the logic and data required to install {0}.", this.activeName)
290 });
291 }
292
293 this.ValidateAndAddCommonSummaryInformationSymbols(sourceLineNumbers, msiVersion, platform, this.activeLanguage);
294
295 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixFragment, WixStandardLibraryIdentifiers.WixStandardModuleReferences);
296 }
297 }
298 finally
299 {
300 this.compilingModule = false; // notice that we are no longer building a Merge Module here
301 }
302 }
303
304 /// <summary>
305 /// Parses a dependency element.
306 /// </summary>
307 /// <param name="node">Element to parse.</param>
308 private void ParseDependencyElement(XElement node)
309 {
310 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
311 string requiredId = null;
312 var requiredLanguage = CompilerConstants.IntegerNotSet;
313 string requiredVersion = null;
314
315 foreach (var attrib in node.Attributes())
316 {
317 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
318 {
319 switch (attrib.Name.LocalName)
320 {
321 case "RequiredId":
322 requiredId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
323 break;
324 case "RequiredLanguage":
325 requiredLanguage = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
326 break;
327 case "RequiredVersion":
328 requiredVersion = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
329 break;
330 default:
331 this.Core.UnexpectedAttribute(node, attrib);
332 break;
333 }
334 }
335 else
336 {
337 this.Core.ParseExtensionAttribute(node, attrib);
338 }
339 }
340
341 if (null == requiredId)
342 {
343 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "RequiredId"));
344 requiredId = String.Empty;
345 }
346
347 if (CompilerConstants.IntegerNotSet == requiredLanguage)
348 {
349 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "RequiredLanguage"));
350 requiredLanguage = CompilerConstants.IllegalInteger;
351 }
352
353 this.Core.ParseForExtensionElements(node);
354
355 if (!this.Core.EncounteredError)
356 {
357 var symbol = this.Core.AddSymbol(new ModuleDependencySymbol(sourceLineNumbers)
358 {
359 ModuleID = this.activeName,
360 RequiredID = requiredId,
361 RequiredLanguage = requiredLanguage,
362 RequiredVersion = requiredVersion
363 });
364
365 symbol.Set((int)ModuleDependencySymbolFields.ModuleLanguage, this.activeLanguage);
366 }
367 }
368
369 /// <summary>
370 /// Parses an exclusion element.
371 /// </summary>
372 /// <param name="node">Element to parse.</param>
373 private void ParseExclusionElement(XElement node)
374 {
375 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
376 string excludedId = null;
377 var excludeExceptLanguage = CompilerConstants.IntegerNotSet;
378 var excludeLanguage = CompilerConstants.IntegerNotSet;
379 var excludedLanguageField = "0";
380 string excludedMaxVersion = null;
381 string excludedMinVersion = null;
382
383 foreach (var attrib in node.Attributes())
384 {
385 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
386 {
387 switch (attrib.Name.LocalName)
388 {
389 case "ExcludedId":
390 excludedId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
391 break;
392 case "ExcludeExceptLanguage":
393 excludeExceptLanguage = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
394 break;
395 case "ExcludeLanguage":
396 excludeLanguage = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
397 break;
398 case "ExcludedMaxVersion":
399 excludedMaxVersion = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
400 break;
401 case "ExcludedMinVersion":
402 excludedMinVersion = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
403 break;
404 default:
405 this.Core.UnexpectedAttribute(node, attrib);
406 break;
407 }
408 }
409 else
410 {
411 this.Core.ParseExtensionAttribute(node, attrib);
412 }
413 }
414
415 if (null == excludedId)
416 {
417 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ExcludedId"));
418 excludedId = String.Empty;
419 }
420
421 if (CompilerConstants.IntegerNotSet != excludeExceptLanguage && CompilerConstants.IntegerNotSet != excludeLanguage)
422 {
423 this.Core.Write(ErrorMessages.IllegalModuleExclusionLanguageAttributes(sourceLineNumbers));
424 }
425 else if (CompilerConstants.IntegerNotSet != excludeExceptLanguage)
426 {
427 excludedLanguageField = Convert.ToString(-excludeExceptLanguage, CultureInfo.InvariantCulture);
428 }
429 else if (CompilerConstants.IntegerNotSet != excludeLanguage)
430 {
431 excludedLanguageField = Convert.ToString(excludeLanguage, CultureInfo.InvariantCulture);
432 }
433
434 this.Core.ParseForExtensionElements(node);
435
436 if (!this.Core.EncounteredError)
437 {
438 var symbol = this.Core.AddSymbol(new ModuleExclusionSymbol(sourceLineNumbers)
439 {
440 ModuleID = this.activeName,
441 ExcludedID = excludedId,
442 ExcludedMinVersion = excludedMinVersion,
443 ExcludedMaxVersion = excludedMaxVersion
444 });
445
446 symbol.Set((int)ModuleExclusionSymbolFields.ModuleLanguage, this.activeLanguage);
447 symbol.Set((int)ModuleExclusionSymbolFields.ExcludedLanguage, excludedLanguageField);
448 }
449 }
450
451 /// <summary>
452 /// Parses a configuration element for a configurable merge module.
453 /// </summary>
454 /// <param name="node">Element to parse.</param>
455 private void ParseConfigurationElement(XElement node)
456 {
457 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
458 string contextData = null;
459 string defaultValue = null;
460 string description = null;
461 string displayName = null;
462 var format = CompilerConstants.IntegerNotSet;
463 string helpKeyword = null;
464 string helpLocation = null;
465 bool keyNoOrphan = false;
466 bool nonNullable = false;
467 Identifier name = null;
468 string type = null;
469
470 foreach (var attrib in node.Attributes())
471 {
472 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
473 {
474 switch (attrib.Name.LocalName)
475 {
476 case "Name":
477 name = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
478 break;
479 case "ContextData":
480 contextData = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
481 break;
482 case "Description":
483 description = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
484 break;
485 case "DefaultValue":
486 defaultValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
487 break;
488 case "DisplayName":
489 displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
490 break;
491 case "Format":
492 var formatStr = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
493 switch (formatStr)
494 {
495 case "Text":
496 case "text":
497 format = 0;
498 break;
499 case "Key":
500 case "key":
501 format = 1;
502 break;
503 case "Integer":
504 case "integer":
505 format = 2;
506 break;
507 case "Bitfield":
508 case "bitfield":
509 format = 3;
510 break;
511 case "":
512 break;
513 default:
514 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Format", formatStr, "Text", "Key", "Integer", "Bitfield"));
515 break;
516 }
517 break;
518 case "HelpKeyword":
519 helpKeyword = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
520 break;
521 case "HelpLocation":
522 helpLocation = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
523 break;
524 case "KeyNoOrphan":
525 keyNoOrphan = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
526 break;
527 case "NonNullable":
528 nonNullable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
529 break;
530 case "Type":
531 type = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
532 break;
533 default:
534 this.Core.UnexpectedAttribute(node, attrib);
535 break;
536 }
537 }
538 else
539 {
540 this.Core.ParseExtensionAttribute(node, attrib);
541 }
542 }
543
544 if (null == name)
545 {
546 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
547 }
548
549 if (CompilerConstants.IntegerNotSet == format)
550 {
551 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Format"));
552 }
553
554 this.Core.ParseForExtensionElements(node);
555
556 if (!this.Core.EncounteredError)
557 {
558 this.Core.AddSymbol(new ModuleConfigurationSymbol(sourceLineNumbers, name)
559 {
560 Format = format,
561 Type = type,
562 ContextData = contextData,
563 DefaultValue = defaultValue,
564 KeyNoOrphan = keyNoOrphan,
565 NonNullable = nonNullable,
566 DisplayName = displayName,
567 Description = description,
568 HelpLocation = helpLocation,
569 HelpKeyword = helpKeyword
570 });
571 }
572 }
573
574 /// <summary>
575 /// Parses a substitution element for a configurable merge module.
576 /// </summary>
577 /// <param name="node">Element to parse.</param>
578 private void ParseSubstitutionElement(XElement node)
579 {
580 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
581 string column = null;
582 string rowKeys = null;
583 string table = null;
584 string value = null;
585
586 foreach (var attrib in node.Attributes())
587 {
588 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
589 {
590 switch (attrib.Name.LocalName)
591 {
592 case "Column":
593 column = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
594 break;
595 case "Row":
596 rowKeys = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
597 break;
598 case "Table":
599 table = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
600 break;
601 case "Value":
602 value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
603 break;
604 default:
605 this.Core.UnexpectedAttribute(node, attrib);
606 break;
607 }
608 }
609 else
610 {
611 this.Core.ParseExtensionAttribute(node, attrib);
612 }
613 }
614
615 if (null == column)
616 {
617 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Column"));
618 column = String.Empty;
619 }
620
621 if (null == table)
622 {
623 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Table"));
624 table = String.Empty;
625 }
626
627 if (null == rowKeys)
628 {
629 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Row"));
630 }
631
632 this.Core.ParseForExtensionElements(node);
633
634 if (!this.Core.EncounteredError)
635 {
636 this.Core.AddSymbol(new ModuleSubstitutionSymbol(sourceLineNumbers)
637 {
638 Table = table,
639 Row = rowKeys,
640 Column = column,
641 Value = value
642 });
643 }
644 }
645
646 /// <summary>
647 /// Parses an IgnoreTable element.
648 /// </summary>
649 /// <param name="node">Element to parse.</param>
650 private void ParseIgnoreTableElement(XElement node)
651 {
652 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
653 string id = null;
654
655 foreach (var attrib in node.Attributes())
656 {
657 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
658 {
659 switch (attrib.Name.LocalName)
660 {
661 case "Id":
662 id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
663 break;
664 default:
665 this.Core.UnexpectedAttribute(node, attrib);
666 break;
667 }
668 }
669 else
670 {
671 this.Core.ParseExtensionAttribute(node, attrib);
672 }
673 }
674
675 if (null == id)
676 {
677 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
678 }
679
680 this.Core.ParseForExtensionElements(node);
681
682 if (!this.Core.EncounteredError)
683 {
684 this.Core.AddSymbol(new ModuleIgnoreTableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Section, id)));
685 }
686 }
687 }
688 }