diff options
Diffstat (limited to 'src/WixToolset.Core/Compiler_Bundle.cs')
-rw-r--r-- | src/WixToolset.Core/Compiler_Bundle.cs | 2727 |
1 files changed, 2727 insertions, 0 deletions
diff --git a/src/WixToolset.Core/Compiler_Bundle.cs b/src/WixToolset.Core/Compiler_Bundle.cs new file mode 100644 index 00000000..21028b6f --- /dev/null +++ b/src/WixToolset.Core/Compiler_Bundle.cs | |||
@@ -0,0 +1,2727 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Core | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Diagnostics; | ||
8 | using System.Globalization; | ||
9 | using System.IO; | ||
10 | using System.Xml.Linq; | ||
11 | using WixToolset.Data; | ||
12 | using WixToolset.Data.Tuples; | ||
13 | using WixToolset.Extensibility; | ||
14 | |||
15 | /// <summary> | ||
16 | /// Compiler of the WiX toolset. | ||
17 | /// </summary> | ||
18 | internal partial class Compiler : ICompiler | ||
19 | { | ||
20 | public const string BurnUXContainerId = "WixUXContainer"; | ||
21 | public const string BurnDefaultAttachedContainerId = "WixAttachedContainer"; | ||
22 | |||
23 | // The following constants must stay in sync with src\burn\engine\core.h | ||
24 | private const string BURN_BUNDLE_NAME = "WixBundleName"; | ||
25 | private const string BURN_BUNDLE_ORIGINAL_SOURCE = "WixBundleOriginalSource"; | ||
26 | private const string BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = "WixBundleOriginalSourceFolder"; | ||
27 | private const string BURN_BUNDLE_LAST_USED_SOURCE = "WixBundleLastUsedSource"; | ||
28 | |||
29 | /// <summary> | ||
30 | /// Parses an ApprovedExeForElevation element. | ||
31 | /// </summary> | ||
32 | /// <param name="node">Element to parse</param> | ||
33 | private void ParseApprovedExeForElevation(XElement node) | ||
34 | { | ||
35 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
36 | Identifier id = null; | ||
37 | string key = null; | ||
38 | string valueName = null; | ||
39 | var win64 = YesNoType.NotSet; | ||
40 | |||
41 | foreach (var attrib in node.Attributes()) | ||
42 | { | ||
43 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
44 | { | ||
45 | switch (attrib.Name.LocalName) | ||
46 | { | ||
47 | case "Id": | ||
48 | id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); | ||
49 | break; | ||
50 | case "Key": | ||
51 | key = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
52 | break; | ||
53 | case "Value": | ||
54 | valueName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
55 | break; | ||
56 | case "Win64": | ||
57 | win64 = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); | ||
58 | break; | ||
59 | default: | ||
60 | this.Core.UnexpectedAttribute(node, attrib); | ||
61 | break; | ||
62 | } | ||
63 | } | ||
64 | else | ||
65 | { | ||
66 | this.Core.ParseExtensionAttribute(node, attrib); | ||
67 | } | ||
68 | } | ||
69 | |||
70 | if (null == id) | ||
71 | { | ||
72 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); | ||
73 | } | ||
74 | |||
75 | if (null == key) | ||
76 | { | ||
77 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); | ||
78 | } | ||
79 | |||
80 | var attributes = BundleApprovedExeForElevationAttributes.None; | ||
81 | |||
82 | if (win64 == YesNoType.Yes) | ||
83 | { | ||
84 | attributes |= BundleApprovedExeForElevationAttributes.Win64; | ||
85 | } | ||
86 | |||
87 | this.Core.ParseForExtensionElements(node); | ||
88 | |||
89 | if (!this.Core.EncounteredError) | ||
90 | { | ||
91 | var wixApprovedExeForElevationRow = (WixApprovedExeForElevationTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixApprovedExeForElevation, id); | ||
92 | wixApprovedExeForElevationRow.Key = key; | ||
93 | wixApprovedExeForElevationRow.Value = valueName; | ||
94 | wixApprovedExeForElevationRow.Attributes = (int)attributes; | ||
95 | } | ||
96 | } | ||
97 | |||
98 | /// <summary> | ||
99 | /// Parses a Bundle element. | ||
100 | /// </summary> | ||
101 | /// <param name="node">Element to parse</param> | ||
102 | private void ParseBundleElement(XElement node) | ||
103 | { | ||
104 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
105 | string copyright = null; | ||
106 | string aboutUrl = null; | ||
107 | var compressed = YesNoDefaultType.Default; | ||
108 | var disableModify = -1; | ||
109 | var disableRemove = YesNoType.NotSet; | ||
110 | string helpTelephone = null; | ||
111 | string helpUrl = null; | ||
112 | string manufacturer = null; | ||
113 | string name = null; | ||
114 | string tag = null; | ||
115 | string updateUrl = null; | ||
116 | string upgradeCode = null; | ||
117 | string version = null; | ||
118 | string condition = null; | ||
119 | string parentName = null; | ||
120 | |||
121 | string fileSystemSafeBundleName = null; | ||
122 | string logVariablePrefixAndExtension = null; | ||
123 | string iconSourceFile = null; | ||
124 | string splashScreenSourceFile = null; | ||
125 | |||
126 | // Process only standard attributes until the active section is initialized. | ||
127 | foreach (var attrib in node.Attributes()) | ||
128 | { | ||
129 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
130 | { | ||
131 | switch (attrib.Name.LocalName) | ||
132 | { | ||
133 | case "AboutUrl": | ||
134 | aboutUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
135 | break; | ||
136 | case "Compressed": | ||
137 | compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); | ||
138 | break; | ||
139 | case "Condition": | ||
140 | condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
141 | break; | ||
142 | case "Copyright": | ||
143 | copyright = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
144 | break; | ||
145 | case "DisableModify": | ||
146 | var value = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
147 | switch (value) | ||
148 | { | ||
149 | case "button": | ||
150 | disableModify = 2; | ||
151 | break; | ||
152 | case "yes": | ||
153 | disableModify = 1; | ||
154 | break; | ||
155 | case "no": | ||
156 | disableModify = 0; | ||
157 | break; | ||
158 | default: | ||
159 | this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, value, "button", "yes", "no")); | ||
160 | break; | ||
161 | } | ||
162 | break; | ||
163 | case "DisableRemove": | ||
164 | disableRemove = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); | ||
165 | break; | ||
166 | case "DisableRepair": | ||
167 | this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName)); | ||
168 | break; | ||
169 | case "HelpTelephone": | ||
170 | helpTelephone = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
171 | break; | ||
172 | case "HelpUrl": | ||
173 | helpUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
174 | break; | ||
175 | case "Manufacturer": | ||
176 | manufacturer = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
177 | break; | ||
178 | case "IconSourceFile": | ||
179 | iconSourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
180 | break; | ||
181 | case "Name": | ||
182 | name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
183 | break; | ||
184 | case "ParentName": | ||
185 | parentName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
186 | break; | ||
187 | case "SplashScreenSourceFile": | ||
188 | splashScreenSourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
189 | break; | ||
190 | case "Tag": | ||
191 | tag = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
192 | break; | ||
193 | case "UpdateUrl": | ||
194 | updateUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
195 | break; | ||
196 | case "UpgradeCode": | ||
197 | upgradeCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); | ||
198 | break; | ||
199 | case "Version": | ||
200 | version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); | ||
201 | break; | ||
202 | default: | ||
203 | this.Core.UnexpectedAttribute(node, attrib); | ||
204 | break; | ||
205 | } | ||
206 | } | ||
207 | } | ||
208 | |||
209 | if (String.IsNullOrEmpty(version)) | ||
210 | { | ||
211 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version")); | ||
212 | } | ||
213 | else if (!CompilerCore.IsValidModuleOrBundleVersion(version)) | ||
214 | { | ||
215 | this.Core.Write(WarningMessages.InvalidModuleOrBundleVersion(sourceLineNumbers, "Bundle", version)); | ||
216 | } | ||
217 | |||
218 | if (String.IsNullOrEmpty(upgradeCode)) | ||
219 | { | ||
220 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "UpgradeCode")); | ||
221 | } | ||
222 | |||
223 | if (String.IsNullOrEmpty(copyright)) | ||
224 | { | ||
225 | if (String.IsNullOrEmpty(manufacturer)) | ||
226 | { | ||
227 | copyright = "Copyright (c). All rights reserved."; | ||
228 | } | ||
229 | else | ||
230 | { | ||
231 | copyright = String.Format("Copyright (c) {0}. All rights reserved.", manufacturer); | ||
232 | } | ||
233 | } | ||
234 | |||
235 | if (String.IsNullOrEmpty(name)) | ||
236 | { | ||
237 | logVariablePrefixAndExtension = String.Concat("WixBundleLog:Setup.log"); | ||
238 | } | ||
239 | else | ||
240 | { | ||
241 | // Ensure only allowable path characters are in "name" (and change spaces to underscores). | ||
242 | fileSystemSafeBundleName = CompilerCore.MakeValidLongFileName(name.Replace(' ', '_'), "_"); | ||
243 | logVariablePrefixAndExtension = String.Concat("WixBundleLog:", fileSystemSafeBundleName, ".log"); | ||
244 | } | ||
245 | |||
246 | this.activeName = String.IsNullOrEmpty(name) ? Common.GenerateGuid() : name; | ||
247 | this.Core.CreateActiveSection(this.activeName, SectionType.Bundle, 0, this.Context.CompilationId); | ||
248 | |||
249 | // Now that the active section is initialized, process only extension attributes. | ||
250 | foreach (var attrib in node.Attributes()) | ||
251 | { | ||
252 | if (!String.IsNullOrEmpty(attrib.Name.NamespaceName) && CompilerCore.WixNamespace != attrib.Name.Namespace) | ||
253 | { | ||
254 | this.Core.ParseExtensionAttribute(node, attrib); | ||
255 | } | ||
256 | } | ||
257 | |||
258 | var baSeen = false; | ||
259 | var chainSeen = false; | ||
260 | var logSeen = false; | ||
261 | |||
262 | foreach (var child in node.Elements()) | ||
263 | { | ||
264 | if (CompilerCore.WixNamespace == child.Name.Namespace) | ||
265 | { | ||
266 | switch (child.Name.LocalName) | ||
267 | { | ||
268 | case "ApprovedExeForElevation": | ||
269 | this.ParseApprovedExeForElevation(child); | ||
270 | break; | ||
271 | case "BootstrapperApplication": | ||
272 | if (baSeen) | ||
273 | { | ||
274 | var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); | ||
275 | this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "BootstrapperApplication")); | ||
276 | } | ||
277 | this.ParseBootstrapperApplicationElement(child); | ||
278 | baSeen = true; | ||
279 | break; | ||
280 | case "BootstrapperApplicationRef": | ||
281 | this.ParseBootstrapperApplicationRefElement(child); | ||
282 | break; | ||
283 | case "OptionalUpdateRegistration": | ||
284 | this.ParseOptionalUpdateRegistrationElement(child, manufacturer, parentName, name); | ||
285 | break; | ||
286 | case "Catalog": | ||
287 | this.ParseCatalogElement(child); | ||
288 | break; | ||
289 | case "Chain": | ||
290 | if (chainSeen) | ||
291 | { | ||
292 | var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); | ||
293 | this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "Chain")); | ||
294 | } | ||
295 | this.ParseChainElement(child); | ||
296 | chainSeen = true; | ||
297 | break; | ||
298 | case "Container": | ||
299 | this.ParseContainerElement(child); | ||
300 | break; | ||
301 | case "ContainerRef": | ||
302 | this.ParseSimpleRefElement(child, "WixBundleContainer"); | ||
303 | break; | ||
304 | case "Log": | ||
305 | if (logSeen) | ||
306 | { | ||
307 | var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); | ||
308 | this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "Log")); | ||
309 | } | ||
310 | logVariablePrefixAndExtension = this.ParseLogElement(child, fileSystemSafeBundleName); | ||
311 | logSeen = true; | ||
312 | break; | ||
313 | case "PayloadGroup": | ||
314 | this.ParsePayloadGroupElement(child, ComplexReferenceParentType.Layout, "BundleLayoutOnlyPayloads"); | ||
315 | break; | ||
316 | case "PayloadGroupRef": | ||
317 | this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Layout, "BundleLayoutOnlyPayloads", ComplexReferenceChildType.Unknown, null); | ||
318 | break; | ||
319 | case "RelatedBundle": | ||
320 | this.ParseRelatedBundleElement(child); | ||
321 | break; | ||
322 | case "Update": | ||
323 | this.ParseUpdateElement(child); | ||
324 | break; | ||
325 | case "Variable": | ||
326 | this.ParseVariableElement(child); | ||
327 | break; | ||
328 | case "WixVariable": | ||
329 | this.ParseWixVariableElement(child); | ||
330 | break; | ||
331 | default: | ||
332 | this.Core.UnexpectedElement(node, child); | ||
333 | break; | ||
334 | } | ||
335 | } | ||
336 | else | ||
337 | { | ||
338 | this.Core.ParseExtensionElement(node, child); | ||
339 | } | ||
340 | } | ||
341 | |||
342 | if (!chainSeen) | ||
343 | { | ||
344 | this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "Chain")); | ||
345 | } | ||
346 | |||
347 | if (!this.Core.EncounteredError) | ||
348 | { | ||
349 | if (null != upgradeCode) | ||
350 | { | ||
351 | var tuple = new WixRelatedBundleTuple(sourceLineNumbers) | ||
352 | { | ||
353 | BundleId = upgradeCode, | ||
354 | Action = RelatedBundleActionType.Upgrade, | ||
355 | }; | ||
356 | |||
357 | this.Core.AddTuple(tuple); | ||
358 | } | ||
359 | |||
360 | var containerRow = (WixBundleContainerTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleContainer); | ||
361 | containerRow.WixBundleContainer = Compiler.BurnDefaultAttachedContainerId; | ||
362 | containerRow.Name = "bundle-attached.cab"; | ||
363 | containerRow.Type = ContainerType.Attached; | ||
364 | |||
365 | var row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundle); | ||
366 | row.Set(0, version); | ||
367 | row.Set(1, copyright); | ||
368 | row.Set(2, name); | ||
369 | row.Set(3, aboutUrl); | ||
370 | if (-1 != disableModify) | ||
371 | { | ||
372 | row.Set(4, disableModify); | ||
373 | } | ||
374 | if (YesNoType.NotSet != disableRemove) | ||
375 | { | ||
376 | row.Set(5, (YesNoType.Yes == disableRemove) ? 1 : 0); | ||
377 | } | ||
378 | // row.Set(6] - (deprecated) "disable repair" | ||
379 | row.Set(7, helpTelephone); | ||
380 | row.Set(8, helpUrl); | ||
381 | row.Set(9, manufacturer); | ||
382 | row.Set(10, updateUrl); | ||
383 | if (YesNoDefaultType.Default != compressed) | ||
384 | { | ||
385 | row.Set(11, (YesNoDefaultType.Yes == compressed) ? 1 : 0); | ||
386 | } | ||
387 | |||
388 | row.Set(12, logVariablePrefixAndExtension); | ||
389 | row.Set(13, iconSourceFile); | ||
390 | row.Set(14, splashScreenSourceFile); | ||
391 | row.Set(15, condition); | ||
392 | row.Set(16, tag); | ||
393 | row.Set(17, this.CurrentPlatform.ToString()); | ||
394 | row.Set(18, parentName); | ||
395 | row.Set(19, upgradeCode); | ||
396 | |||
397 | // Ensure that the bundle stores the well-known persisted values. | ||
398 | var bundleNameWellKnownVariable = (WixBundleVariableTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleVariable); | ||
399 | bundleNameWellKnownVariable.WixBundleVariable = Compiler.BURN_BUNDLE_NAME; | ||
400 | bundleNameWellKnownVariable.Hidden = false; | ||
401 | bundleNameWellKnownVariable.Persisted = true; | ||
402 | |||
403 | var bundleOriginalSourceWellKnownVariable = (WixBundleVariableTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleVariable); | ||
404 | bundleOriginalSourceWellKnownVariable.WixBundleVariable = Compiler.BURN_BUNDLE_ORIGINAL_SOURCE; | ||
405 | bundleOriginalSourceWellKnownVariable.Hidden = false; | ||
406 | bundleOriginalSourceWellKnownVariable.Persisted = true; | ||
407 | |||
408 | var bundleOriginalSourceFolderWellKnownVariable = (WixBundleVariableTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleVariable); | ||
409 | bundleOriginalSourceFolderWellKnownVariable.WixBundleVariable = Compiler.BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER; | ||
410 | bundleOriginalSourceFolderWellKnownVariable.Hidden = false; | ||
411 | bundleOriginalSourceFolderWellKnownVariable.Persisted = true; | ||
412 | |||
413 | var bundleLastUsedSourceWellKnownVariable = (WixBundleVariableTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleVariable); | ||
414 | bundleLastUsedSourceWellKnownVariable.WixBundleVariable = Compiler.BURN_BUNDLE_LAST_USED_SOURCE; | ||
415 | bundleLastUsedSourceWellKnownVariable.Hidden = false; | ||
416 | bundleLastUsedSourceWellKnownVariable.Persisted = true; | ||
417 | } | ||
418 | } | ||
419 | |||
420 | /// <summary> | ||
421 | /// Parse a Container element. | ||
422 | /// </summary> | ||
423 | /// <param name="node">Element to parse</param> | ||
424 | private string ParseLogElement(XElement node, string fileSystemSafeBundleName) | ||
425 | { | ||
426 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
427 | var disableLog = YesNoType.NotSet; | ||
428 | var variable = "WixBundleLog"; | ||
429 | var logPrefix = fileSystemSafeBundleName ?? "Setup"; | ||
430 | var logExtension = ".log"; | ||
431 | |||
432 | foreach (var attrib in node.Attributes()) | ||
433 | { | ||
434 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
435 | { | ||
436 | switch (attrib.Name.LocalName) | ||
437 | { | ||
438 | case "Disable": | ||
439 | disableLog = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); | ||
440 | break; | ||
441 | case "PathVariable": | ||
442 | variable = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); | ||
443 | break; | ||
444 | case "Prefix": | ||
445 | logPrefix = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
446 | break; | ||
447 | case "Extension": | ||
448 | logExtension = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
449 | break; | ||
450 | default: | ||
451 | this.Core.UnexpectedAttribute(node, attrib); | ||
452 | break; | ||
453 | } | ||
454 | } | ||
455 | else | ||
456 | { | ||
457 | this.Core.ParseExtensionAttribute(node, attrib); | ||
458 | } | ||
459 | } | ||
460 | |||
461 | if (!logExtension.StartsWith(".", StringComparison.Ordinal)) | ||
462 | { | ||
463 | logExtension = String.Concat(".", logExtension); | ||
464 | } | ||
465 | |||
466 | this.Core.ParseForExtensionElements(node); | ||
467 | |||
468 | return YesNoType.Yes == disableLog ? null : String.Concat(variable, ":", logPrefix, logExtension); | ||
469 | } | ||
470 | |||
471 | /// <summary> | ||
472 | /// Parse a Catalog element. | ||
473 | /// </summary> | ||
474 | /// <param name="node">Element to parse</param> | ||
475 | private void ParseCatalogElement(XElement node) | ||
476 | { | ||
477 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
478 | Identifier id = null; | ||
479 | string sourceFile = 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 | id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); | ||
489 | break; | ||
490 | case "SourceFile": | ||
491 | sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
492 | break; | ||
493 | default: | ||
494 | this.Core.UnexpectedAttribute(node, attrib); | ||
495 | break; | ||
496 | } | ||
497 | } | ||
498 | } | ||
499 | |||
500 | if (null == id) | ||
501 | { | ||
502 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); | ||
503 | } | ||
504 | |||
505 | if (null == sourceFile) | ||
506 | { | ||
507 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile")); | ||
508 | } | ||
509 | |||
510 | this.Core.ParseForExtensionElements(node); | ||
511 | |||
512 | // Create catalog row | ||
513 | if (!this.Core.EncounteredError) | ||
514 | { | ||
515 | this.CreatePayloadRow(sourceLineNumbers, id, Path.GetFileName(sourceFile), sourceFile, null, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, ComplexReferenceChildType.Unknown, null, YesNoDefaultType.Yes, YesNoType.Yes, null, null, null); | ||
516 | |||
517 | var wixCatalogRow = (WixBundleCatalogTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleCatalog, id); | ||
518 | wixCatalogRow.Payload_ = id.Id; | ||
519 | } | ||
520 | } | ||
521 | |||
522 | /// <summary> | ||
523 | /// Parse a Container element. | ||
524 | /// </summary> | ||
525 | /// <param name="node">Element to parse</param> | ||
526 | private void ParseContainerElement(XElement node) | ||
527 | { | ||
528 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
529 | Identifier id = null; | ||
530 | string downloadUrl = null; | ||
531 | string name = null; | ||
532 | var type = ContainerType.Detached; | ||
533 | |||
534 | foreach (var attrib in node.Attributes()) | ||
535 | { | ||
536 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
537 | { | ||
538 | switch (attrib.Name.LocalName) | ||
539 | { | ||
540 | case "Id": | ||
541 | id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); | ||
542 | break; | ||
543 | case "DownloadUrl": | ||
544 | downloadUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
545 | break; | ||
546 | case "Name": | ||
547 | name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
548 | break; | ||
549 | case "Type": | ||
550 | var typeString = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
551 | if (!Enum.TryParse<ContainerType>(typeString, out type)) | ||
552 | { | ||
553 | this.Core.Write(ErrorMessages.IllegalAttributeValueWithLegalList(sourceLineNumbers, node.Name.LocalName, "Type", typeString, "attached, detached")); | ||
554 | } | ||
555 | break; | ||
556 | default: | ||
557 | this.Core.UnexpectedAttribute(node, attrib); | ||
558 | break; | ||
559 | } | ||
560 | } | ||
561 | else | ||
562 | { | ||
563 | this.Core.ParseExtensionAttribute(node, attrib); | ||
564 | } | ||
565 | } | ||
566 | |||
567 | if (null == id) | ||
568 | { | ||
569 | if (!String.IsNullOrEmpty(name)) | ||
570 | { | ||
571 | id = this.Core.CreateIdentifierFromFilename(name); | ||
572 | } | ||
573 | |||
574 | if (null == id) | ||
575 | { | ||
576 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); | ||
577 | id = Identifier.Invalid; | ||
578 | } | ||
579 | else if (!Common.IsIdentifier(id.Id)) | ||
580 | { | ||
581 | this.Core.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, node.Name.LocalName, "Id", id.Id)); | ||
582 | } | ||
583 | } | ||
584 | else if (null == name) | ||
585 | { | ||
586 | name = id.Id; | ||
587 | } | ||
588 | |||
589 | if (!String.IsNullOrEmpty(downloadUrl) && ContainerType.Detached != type) | ||
590 | { | ||
591 | this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DownloadUrl", "Type", "attached")); | ||
592 | } | ||
593 | |||
594 | foreach (var child in node.Elements()) | ||
595 | { | ||
596 | if (CompilerCore.WixNamespace == child.Name.Namespace) | ||
597 | { | ||
598 | switch (child.Name.LocalName) | ||
599 | { | ||
600 | case "PackageGroupRef": | ||
601 | this.ParsePackageGroupRefElement(child, ComplexReferenceParentType.Container, id.Id); | ||
602 | break; | ||
603 | default: | ||
604 | this.Core.UnexpectedElement(node, child); | ||
605 | break; | ||
606 | } | ||
607 | } | ||
608 | else | ||
609 | { | ||
610 | this.Core.ParseExtensionElement(node, child); | ||
611 | } | ||
612 | } | ||
613 | |||
614 | |||
615 | if (!this.Core.EncounteredError) | ||
616 | { | ||
617 | var row = (WixBundleContainerTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleContainer, id); | ||
618 | row.Name = name; | ||
619 | row.Type = type; | ||
620 | row.DownloadUrl = downloadUrl; | ||
621 | } | ||
622 | } | ||
623 | |||
624 | /// <summary> | ||
625 | /// Parse the BoostrapperApplication element. | ||
626 | /// </summary> | ||
627 | /// <param name="node">Element to parse</param> | ||
628 | private void ParseBootstrapperApplicationElement(XElement node) | ||
629 | { | ||
630 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
631 | string id = null; | ||
632 | string previousId = null; | ||
633 | var previousType = ComplexReferenceChildType.Unknown; | ||
634 | |||
635 | // The BootstrapperApplication element acts like a Payload element so delegate to the "Payload" attribute parsing code to parse and create a Payload entry. | ||
636 | id = this.ParsePayloadElementContent(node, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId, false); | ||
637 | if (null != id) | ||
638 | { | ||
639 | previousId = id; | ||
640 | previousType = ComplexReferenceChildType.Payload; | ||
641 | } | ||
642 | |||
643 | foreach (var child in node.Elements()) | ||
644 | { | ||
645 | if (CompilerCore.WixNamespace == child.Name.Namespace) | ||
646 | { | ||
647 | switch (child.Name.LocalName) | ||
648 | { | ||
649 | case "Payload": | ||
650 | previousId = this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId); | ||
651 | previousType = ComplexReferenceChildType.Payload; | ||
652 | break; | ||
653 | case "PayloadGroupRef": | ||
654 | previousId = this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId); | ||
655 | previousType = ComplexReferenceChildType.PayloadGroup; | ||
656 | break; | ||
657 | default: | ||
658 | this.Core.UnexpectedElement(node, child); | ||
659 | break; | ||
660 | } | ||
661 | } | ||
662 | else | ||
663 | { | ||
664 | this.Core.ParseExtensionElement(node, child); | ||
665 | } | ||
666 | } | ||
667 | |||
668 | if (null == previousId) | ||
669 | { | ||
670 | // We need *either* <Payload> or <PayloadGroupRef> or even just @SourceFile on the BA... | ||
671 | // but we just say there's a missing <Payload>. | ||
672 | // TODO: Is there a better message for this? | ||
673 | this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "Payload")); | ||
674 | } | ||
675 | |||
676 | // Add the application as an attached container and if an Id was provided add that too. | ||
677 | if (!this.Core.EncounteredError) | ||
678 | { | ||
679 | var containerRow = (WixBundleContainerTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleContainer); | ||
680 | containerRow.WixBundleContainer = Compiler.BurnUXContainerId; | ||
681 | containerRow.Name = "bundle-ux.cab"; | ||
682 | containerRow.Type = ContainerType.Attached; | ||
683 | |||
684 | if (!String.IsNullOrEmpty(id)) | ||
685 | { | ||
686 | var row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBootstrapperApplication); | ||
687 | row.Set(0, id); | ||
688 | } | ||
689 | } | ||
690 | } | ||
691 | |||
692 | /// <summary> | ||
693 | /// Parse the BoostrapperApplicationRef element. | ||
694 | /// </summary> | ||
695 | /// <param name="node">Element to parse</param> | ||
696 | private void ParseBootstrapperApplicationRefElement(XElement node) | ||
697 | { | ||
698 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
699 | string id = null; | ||
700 | string previousId = null; | ||
701 | var previousType = ComplexReferenceChildType.Unknown; | ||
702 | |||
703 | foreach (var attrib in node.Attributes()) | ||
704 | { | ||
705 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
706 | { | ||
707 | switch (attrib.Name.LocalName) | ||
708 | { | ||
709 | case "Id": | ||
710 | id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); | ||
711 | break; | ||
712 | default: | ||
713 | this.Core.UnexpectedAttribute(node, attrib); | ||
714 | break; | ||
715 | } | ||
716 | } | ||
717 | else | ||
718 | { | ||
719 | this.Core.ParseExtensionAttribute(node, attrib); | ||
720 | } | ||
721 | } | ||
722 | |||
723 | foreach (var child in node.Elements()) | ||
724 | { | ||
725 | if (CompilerCore.WixNamespace == child.Name.Namespace) | ||
726 | { | ||
727 | switch (child.Name.LocalName) | ||
728 | { | ||
729 | case "Payload": | ||
730 | previousId = this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId); | ||
731 | previousType = ComplexReferenceChildType.Payload; | ||
732 | break; | ||
733 | case "PayloadGroupRef": | ||
734 | previousId = this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId); | ||
735 | previousType = ComplexReferenceChildType.PayloadGroup; | ||
736 | break; | ||
737 | default: | ||
738 | this.Core.UnexpectedElement(node, child); | ||
739 | break; | ||
740 | } | ||
741 | } | ||
742 | else | ||
743 | { | ||
744 | this.Core.ParseExtensionElement(node, child); | ||
745 | } | ||
746 | } | ||
747 | |||
748 | |||
749 | if (String.IsNullOrEmpty(id)) | ||
750 | { | ||
751 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); | ||
752 | } | ||
753 | else | ||
754 | { | ||
755 | this.Core.CreateSimpleReference(sourceLineNumbers, "WixBootstrapperApplication", id); | ||
756 | } | ||
757 | } | ||
758 | |||
759 | /// <summary> | ||
760 | /// Parse the OptionalUpdateRegistration element. | ||
761 | /// </summary> | ||
762 | /// <param name="node">The element to parse.</param> | ||
763 | /// <param name="defaultManufacturer">The manufacturer.</param> | ||
764 | /// <param name="defaultProductFamily">The product family.</param> | ||
765 | /// <param name="defaultName">The bundle name.</param> | ||
766 | private void ParseOptionalUpdateRegistrationElement(XElement node, string defaultManufacturer, string defaultProductFamily, string defaultName) | ||
767 | { | ||
768 | const string defaultClassification = "Update"; | ||
769 | |||
770 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
771 | string manufacturer = null; | ||
772 | string department = null; | ||
773 | string productFamily = null; | ||
774 | string name = null; | ||
775 | var classification = defaultClassification; | ||
776 | |||
777 | foreach (var attrib in node.Attributes()) | ||
778 | { | ||
779 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
780 | { | ||
781 | switch (attrib.Name.LocalName) | ||
782 | { | ||
783 | case "Manufacturer": | ||
784 | manufacturer = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
785 | break; | ||
786 | case "Department": | ||
787 | department = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
788 | break; | ||
789 | case "ProductFamily": | ||
790 | productFamily = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
791 | break; | ||
792 | case "Name": | ||
793 | name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
794 | break; | ||
795 | case "Classification": | ||
796 | classification = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
797 | break; | ||
798 | default: | ||
799 | this.Core.UnexpectedAttribute(node, attrib); | ||
800 | break; | ||
801 | } | ||
802 | } | ||
803 | else | ||
804 | { | ||
805 | this.Core.ParseExtensionAttribute(node, attrib); | ||
806 | } | ||
807 | } | ||
808 | |||
809 | if (String.IsNullOrEmpty(manufacturer)) | ||
810 | { | ||
811 | if (!String.IsNullOrEmpty(defaultManufacturer)) | ||
812 | { | ||
813 | manufacturer = defaultManufacturer; | ||
814 | } | ||
815 | else | ||
816 | { | ||
817 | this.Core.Write(ErrorMessages.ExpectedAttributeInElementOrParent(sourceLineNumbers, node.Name.LocalName, "Manufacturer", node.Parent.Name.LocalName)); | ||
818 | } | ||
819 | } | ||
820 | |||
821 | if (String.IsNullOrEmpty(productFamily)) | ||
822 | { | ||
823 | if (!String.IsNullOrEmpty(defaultProductFamily)) | ||
824 | { | ||
825 | productFamily = defaultProductFamily; | ||
826 | } | ||
827 | } | ||
828 | |||
829 | if (String.IsNullOrEmpty(name)) | ||
830 | { | ||
831 | if (!String.IsNullOrEmpty(defaultName)) | ||
832 | { | ||
833 | name = defaultName; | ||
834 | } | ||
835 | else | ||
836 | { | ||
837 | this.Core.Write(ErrorMessages.ExpectedAttributeInElementOrParent(sourceLineNumbers, node.Name.LocalName, "Name", node.Parent.Name.LocalName)); | ||
838 | } | ||
839 | } | ||
840 | |||
841 | if (String.IsNullOrEmpty(classification)) | ||
842 | { | ||
843 | this.Core.Write(ErrorMessages.IllegalEmptyAttributeValue(sourceLineNumbers, node.Name.LocalName, "Classification", defaultClassification)); | ||
844 | } | ||
845 | |||
846 | this.Core.ParseForExtensionElements(node); | ||
847 | |||
848 | if (!this.Core.EncounteredError) | ||
849 | { | ||
850 | var row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixUpdateRegistration); | ||
851 | row.Set(0, manufacturer); | ||
852 | row.Set(1, department); | ||
853 | row.Set(2, productFamily); | ||
854 | row.Set(3, name); | ||
855 | row.Set(4, classification); | ||
856 | } | ||
857 | } | ||
858 | |||
859 | /// <summary> | ||
860 | /// Parse Payload element. | ||
861 | /// </summary> | ||
862 | /// <param name="node">Element to parse</param> | ||
863 | /// <param name="parentType">ComplexReferenceParentType of parent element. (BA or PayloadGroup)</param> | ||
864 | /// <param name="parentId">Identifier of parent element.</param> | ||
865 | private string ParsePayloadElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId) | ||
866 | { | ||
867 | Debug.Assert(ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType); | ||
868 | Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PayloadGroup == previousType || ComplexReferenceChildType.Payload == previousType); | ||
869 | |||
870 | var id = this.ParsePayloadElementContent(node, parentType, parentId, previousType, previousId, true); | ||
871 | var context = new Dictionary<string, string> | ||
872 | { | ||
873 | ["Id"] = id | ||
874 | }; | ||
875 | |||
876 | foreach (var child in node.Elements()) | ||
877 | { | ||
878 | if (CompilerCore.WixNamespace == child.Name.Namespace) | ||
879 | { | ||
880 | switch (child.Name.LocalName) | ||
881 | { | ||
882 | default: | ||
883 | this.Core.UnexpectedElement(node, child); | ||
884 | break; | ||
885 | } | ||
886 | } | ||
887 | else | ||
888 | { | ||
889 | this.Core.ParseExtensionElement(node, child, context); | ||
890 | } | ||
891 | } | ||
892 | |||
893 | return id; | ||
894 | } | ||
895 | |||
896 | /// <summary> | ||
897 | /// Parse the attributes of the Payload element. | ||
898 | /// </summary> | ||
899 | /// <param name="node">Element to parse</param> | ||
900 | /// <param name="parentType">ComplexReferenceParentType of parent element.</param> | ||
901 | /// <param name="parentId">Identifier of parent element.</param> | ||
902 | private string ParsePayloadElementContent(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId, bool required) | ||
903 | { | ||
904 | Debug.Assert(ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType); | ||
905 | |||
906 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
907 | var compressed = YesNoDefaultType.Default; | ||
908 | var enableSignatureVerification = YesNoType.No; | ||
909 | Identifier id = null; | ||
910 | string name = null; | ||
911 | string sourceFile = null; | ||
912 | string downloadUrl = null; | ||
913 | RemotePayload remotePayload = null; | ||
914 | |||
915 | // This list lets us evaluate extension attributes *after* all core attributes | ||
916 | // have been parsed and dealt with, regardless of authoring order. | ||
917 | var extensionAttributes = new List<XAttribute>(); | ||
918 | |||
919 | foreach (var attrib in node.Attributes()) | ||
920 | { | ||
921 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
922 | { | ||
923 | switch (attrib.Name.LocalName) | ||
924 | { | ||
925 | case "Id": | ||
926 | id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); | ||
927 | break; | ||
928 | case "Compressed": | ||
929 | compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); | ||
930 | break; | ||
931 | case "Name": | ||
932 | name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false, true); | ||
933 | break; | ||
934 | case "SourceFile": | ||
935 | sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
936 | break; | ||
937 | case "DownloadUrl": | ||
938 | downloadUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
939 | break; | ||
940 | case "EnableSignatureVerification": | ||
941 | enableSignatureVerification = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); | ||
942 | break; | ||
943 | default: | ||
944 | this.Core.UnexpectedAttribute(node, attrib); | ||
945 | break; | ||
946 | } | ||
947 | } | ||
948 | else | ||
949 | { | ||
950 | extensionAttributes.Add(attrib); | ||
951 | } | ||
952 | } | ||
953 | |||
954 | if (!required && null == sourceFile) | ||
955 | { | ||
956 | // Nothing left to do! | ||
957 | return null; | ||
958 | } | ||
959 | |||
960 | if (null == id) | ||
961 | { | ||
962 | id = this.Core.CreateIdentifier("pay", (null != sourceFile) ? sourceFile.ToUpperInvariant() : String.Empty); | ||
963 | } | ||
964 | |||
965 | // Now that the PayloadId is known, we can parse the extension attributes. | ||
966 | var context = new Dictionary<string, string> | ||
967 | { | ||
968 | ["Id"] = id.Id | ||
969 | }; | ||
970 | |||
971 | foreach (var extensionAttribute in extensionAttributes) | ||
972 | { | ||
973 | this.Core.ParseExtensionAttribute(node, extensionAttribute, context); | ||
974 | } | ||
975 | |||
976 | // We only handle the elements we care about. Let caller handle other children. | ||
977 | foreach (var child in node.Elements(CompilerCore.WixNamespace + "RemotePayload")) | ||
978 | { | ||
979 | var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); | ||
980 | |||
981 | if (CompilerCore.WixNamespace == node.Name.Namespace && node.Name.LocalName != "ExePackage") | ||
982 | { | ||
983 | this.Core.Write(ErrorMessages.RemotePayloadUnsupported(childSourceLineNumbers)); | ||
984 | continue; | ||
985 | } | ||
986 | |||
987 | if (null != remotePayload) | ||
988 | { | ||
989 | this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName)); | ||
990 | } | ||
991 | |||
992 | remotePayload = this.ParseRemotePayloadElement(child); | ||
993 | } | ||
994 | |||
995 | if (null != sourceFile && null != remotePayload) | ||
996 | { | ||
997 | this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, "RemotePayload", "SourceFile")); | ||
998 | } | ||
999 | else if (null == sourceFile && null == remotePayload) | ||
1000 | { | ||
1001 | this.Core.Write(ErrorMessages.ExpectedAttributeOrElement(sourceLineNumbers, node.Name.LocalName, "SourceFile", "RemotePayload")); | ||
1002 | } | ||
1003 | else if (null == sourceFile) | ||
1004 | { | ||
1005 | sourceFile = String.Empty; | ||
1006 | } | ||
1007 | |||
1008 | if (null == downloadUrl && null != remotePayload) | ||
1009 | { | ||
1010 | this.Core.Write(ErrorMessages.ExpectedAttributeWithElement(sourceLineNumbers, node.Name.LocalName, "DownloadUrl", "RemotePayload")); | ||
1011 | } | ||
1012 | |||
1013 | if (Compiler.BurnUXContainerId == parentId) | ||
1014 | { | ||
1015 | if (compressed == YesNoDefaultType.No) | ||
1016 | { | ||
1017 | this.Core.Write(WarningMessages.UxPayloadsOnlySupportEmbedding(sourceLineNumbers, sourceFile)); | ||
1018 | } | ||
1019 | |||
1020 | compressed = YesNoDefaultType.Yes; | ||
1021 | } | ||
1022 | |||
1023 | this.CreatePayloadRow(sourceLineNumbers, id, name, sourceFile, downloadUrl, parentType, parentId, previousType, previousId, compressed, enableSignatureVerification, null, null, remotePayload); | ||
1024 | |||
1025 | return id.Id; | ||
1026 | } | ||
1027 | |||
1028 | private RemotePayload ParseRemotePayloadElement(XElement node) | ||
1029 | { | ||
1030 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
1031 | var remotePayload = new RemotePayload(); | ||
1032 | |||
1033 | foreach (var attrib in node.Attributes()) | ||
1034 | { | ||
1035 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
1036 | { | ||
1037 | switch (attrib.Name.LocalName) | ||
1038 | { | ||
1039 | case "CertificatePublicKey": | ||
1040 | remotePayload.CertificatePublicKey = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1041 | break; | ||
1042 | case "CertificateThumbprint": | ||
1043 | remotePayload.CertificateThumbprint = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1044 | break; | ||
1045 | case "Description": | ||
1046 | remotePayload.Description = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1047 | break; | ||
1048 | case "Hash": | ||
1049 | remotePayload.Hash = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1050 | break; | ||
1051 | case "ProductName": | ||
1052 | remotePayload.ProductName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1053 | break; | ||
1054 | case "Size": | ||
1055 | remotePayload.Size = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); | ||
1056 | break; | ||
1057 | case "Version": | ||
1058 | remotePayload.Version = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1059 | break; | ||
1060 | default: | ||
1061 | this.Core.UnexpectedAttribute(node, attrib); | ||
1062 | break; | ||
1063 | } | ||
1064 | } | ||
1065 | else | ||
1066 | { | ||
1067 | this.Core.ParseExtensionAttribute(node, attrib); | ||
1068 | } | ||
1069 | } | ||
1070 | |||
1071 | if (String.IsNullOrEmpty(remotePayload.ProductName)) | ||
1072 | { | ||
1073 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ProductName")); | ||
1074 | } | ||
1075 | |||
1076 | if (String.IsNullOrEmpty(remotePayload.Description)) | ||
1077 | { | ||
1078 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Description")); | ||
1079 | } | ||
1080 | |||
1081 | if (String.IsNullOrEmpty(remotePayload.Hash)) | ||
1082 | { | ||
1083 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Hash")); | ||
1084 | } | ||
1085 | |||
1086 | if (0 == remotePayload.Size) | ||
1087 | { | ||
1088 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Size")); | ||
1089 | } | ||
1090 | |||
1091 | if (String.IsNullOrEmpty(remotePayload.Version)) | ||
1092 | { | ||
1093 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version")); | ||
1094 | } | ||
1095 | |||
1096 | return remotePayload; | ||
1097 | } | ||
1098 | |||
1099 | /// <summary> | ||
1100 | /// Creates the row for a Payload. | ||
1101 | /// </summary> | ||
1102 | /// <param name="node">Element to parse</param> | ||
1103 | /// <param name="parentType">ComplexReferenceParentType of parent element</param> | ||
1104 | /// <param name="parentId">Identifier of parent element.</param> | ||
1105 | private WixBundlePayloadTuple CreatePayloadRow(SourceLineNumber sourceLineNumbers, Identifier id, string name, string sourceFile, string downloadUrl, ComplexReferenceParentType parentType, | ||
1106 | string parentId, ComplexReferenceChildType previousType, string previousId, YesNoDefaultType compressed, YesNoType enableSignatureVerification, string displayName, string description, | ||
1107 | RemotePayload remotePayload) | ||
1108 | { | ||
1109 | WixBundlePayloadTuple row = null; | ||
1110 | |||
1111 | if (!this.Core.EncounteredError) | ||
1112 | { | ||
1113 | row = (WixBundlePayloadTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundlePayload, id); | ||
1114 | row.Name = String.IsNullOrEmpty(name) ? Path.GetFileName(sourceFile) : name; | ||
1115 | row.SourceFile = sourceFile; | ||
1116 | row.DownloadUrl = downloadUrl; | ||
1117 | row.Compressed = compressed; | ||
1118 | row.UnresolvedSourceFile = sourceFile; // duplicate of sourceFile but in a string column so it won't get resolved to a full path during binding. | ||
1119 | row.DisplayName = displayName; | ||
1120 | row.Description = description; | ||
1121 | row.EnableSignatureValidation = (YesNoType.Yes == enableSignatureVerification); | ||
1122 | |||
1123 | if (null != remotePayload) | ||
1124 | { | ||
1125 | row.Description = remotePayload.Description; | ||
1126 | row.DisplayName = remotePayload.ProductName; | ||
1127 | row.Hash = remotePayload.Hash; | ||
1128 | row.PublicKey = remotePayload.CertificatePublicKey; | ||
1129 | row.Thumbprint = remotePayload.CertificateThumbprint; | ||
1130 | row.FileSize = remotePayload.Size; | ||
1131 | row.Version = remotePayload.Version; | ||
1132 | } | ||
1133 | |||
1134 | this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Payload, id.Id, previousType, previousId); | ||
1135 | } | ||
1136 | |||
1137 | return row; | ||
1138 | } | ||
1139 | |||
1140 | /// <summary> | ||
1141 | /// Parse PayloadGroup element. | ||
1142 | /// </summary> | ||
1143 | /// <param name="node">Element to parse</param> | ||
1144 | /// <param name="parentType">Optional ComplexReferenceParentType of parent element. (typically another PayloadGroup)</param> | ||
1145 | /// <param name="parentId">Identifier of parent element.</param> | ||
1146 | private void ParsePayloadGroupElement(XElement node, ComplexReferenceParentType parentType, string parentId) | ||
1147 | { | ||
1148 | Debug.Assert(ComplexReferenceParentType.Unknown == parentType || ComplexReferenceParentType.Layout == parentType || ComplexReferenceParentType.PayloadGroup == parentType); | ||
1149 | |||
1150 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
1151 | Identifier id = null; | ||
1152 | |||
1153 | foreach (var attrib in node.Attributes()) | ||
1154 | { | ||
1155 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
1156 | { | ||
1157 | switch (attrib.Name.LocalName) | ||
1158 | { | ||
1159 | case "Id": | ||
1160 | id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); | ||
1161 | break; | ||
1162 | default: | ||
1163 | this.Core.UnexpectedAttribute(node, attrib); | ||
1164 | break; | ||
1165 | } | ||
1166 | } | ||
1167 | else | ||
1168 | { | ||
1169 | this.Core.ParseExtensionAttribute(node, attrib); | ||
1170 | } | ||
1171 | } | ||
1172 | |||
1173 | if (null == id) | ||
1174 | { | ||
1175 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); | ||
1176 | id = Identifier.Invalid; | ||
1177 | } | ||
1178 | |||
1179 | var previousType = ComplexReferenceChildType.Unknown; | ||
1180 | string previousId = null; | ||
1181 | foreach (var child in node.Elements()) | ||
1182 | { | ||
1183 | if (CompilerCore.WixNamespace == child.Name.Namespace) | ||
1184 | { | ||
1185 | switch (child.Name.LocalName) | ||
1186 | { | ||
1187 | case "Payload": | ||
1188 | previousId = this.ParsePayloadElement(child, ComplexReferenceParentType.PayloadGroup, id.Id, previousType, previousId); | ||
1189 | previousType = ComplexReferenceChildType.Payload; | ||
1190 | break; | ||
1191 | case "PayloadGroupRef": | ||
1192 | previousId = this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.PayloadGroup, id.Id, previousType, previousId); | ||
1193 | previousType = ComplexReferenceChildType.PayloadGroup; | ||
1194 | break; | ||
1195 | default: | ||
1196 | this.Core.UnexpectedElement(node, child); | ||
1197 | break; | ||
1198 | } | ||
1199 | } | ||
1200 | else | ||
1201 | { | ||
1202 | this.Core.ParseExtensionElement(node, child); | ||
1203 | } | ||
1204 | } | ||
1205 | |||
1206 | |||
1207 | if (!this.Core.EncounteredError) | ||
1208 | { | ||
1209 | this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundlePayloadGroup, id); | ||
1210 | |||
1211 | this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.PayloadGroup, id.Id, ComplexReferenceChildType.Unknown, null); | ||
1212 | } | ||
1213 | } | ||
1214 | |||
1215 | /// <summary> | ||
1216 | /// Parses a payload group reference element. | ||
1217 | /// </summary> | ||
1218 | /// <param name="node">Element to parse.</param> | ||
1219 | /// <param name="parentType">ComplexReferenceParentType of parent element (BA or PayloadGroup).</param> | ||
1220 | /// <param name="parentId">Identifier of parent element.</param> | ||
1221 | private string ParsePayloadGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId) | ||
1222 | { | ||
1223 | Debug.Assert(ComplexReferenceParentType.Layout == parentType || ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType); | ||
1224 | Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PayloadGroup == previousType || ComplexReferenceChildType.Payload == previousType); | ||
1225 | |||
1226 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
1227 | string id = null; | ||
1228 | |||
1229 | foreach (var attrib in node.Attributes()) | ||
1230 | { | ||
1231 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
1232 | { | ||
1233 | switch (attrib.Name.LocalName) | ||
1234 | { | ||
1235 | case "Id": | ||
1236 | id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); | ||
1237 | this.Core.CreateSimpleReference(sourceLineNumbers, "WixBundlePayloadGroup", id); | ||
1238 | break; | ||
1239 | default: | ||
1240 | this.Core.UnexpectedAttribute(node, attrib); | ||
1241 | break; | ||
1242 | } | ||
1243 | } | ||
1244 | else | ||
1245 | { | ||
1246 | this.Core.ParseExtensionAttribute(node, attrib); | ||
1247 | } | ||
1248 | } | ||
1249 | |||
1250 | if (null == id) | ||
1251 | { | ||
1252 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); | ||
1253 | } | ||
1254 | |||
1255 | this.Core.ParseForExtensionElements(node); | ||
1256 | |||
1257 | this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.PayloadGroup, id, previousType, previousId); | ||
1258 | |||
1259 | return id; | ||
1260 | } | ||
1261 | |||
1262 | /// <summary> | ||
1263 | /// Creates group and ordering information. | ||
1264 | /// </summary> | ||
1265 | /// <param name="sourceLineNumbers">Source line numbers.</param> | ||
1266 | /// <param name="parentType">Type of parent group, if known.</param> | ||
1267 | /// <param name="parentId">Identifier of parent group, if known.</param> | ||
1268 | /// <param name="type">Type of this item.</param> | ||
1269 | /// <param name="id">Identifier for this item.</param> | ||
1270 | /// <param name="previousType">Type of previous item, if known.</param> | ||
1271 | /// <param name="previousId">Identifier of previous item, if known</param> | ||
1272 | private void CreateGroupAndOrderingRows(SourceLineNumber sourceLineNumbers, | ||
1273 | ComplexReferenceParentType parentType, string parentId, | ||
1274 | ComplexReferenceChildType type, string id, | ||
1275 | ComplexReferenceChildType previousType, string previousId) | ||
1276 | { | ||
1277 | if (ComplexReferenceParentType.Unknown != parentType && null != parentId) | ||
1278 | { | ||
1279 | this.Core.CreateWixGroupRow(sourceLineNumbers, parentType, parentId, type, id); | ||
1280 | } | ||
1281 | |||
1282 | if (ComplexReferenceChildType.Unknown != previousType && null != previousId) | ||
1283 | { | ||
1284 | this.CreateWixOrderingRow(sourceLineNumbers, type, id, previousType, previousId); | ||
1285 | } | ||
1286 | } | ||
1287 | |||
1288 | /// <summary> | ||
1289 | /// Parse ExitCode element. | ||
1290 | /// </summary> | ||
1291 | /// <param name="node">Element to parse</param> | ||
1292 | /// <param name="packageId">Id of parent element</param> | ||
1293 | private void ParseExitCodeElement(XElement node, string packageId) | ||
1294 | { | ||
1295 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
1296 | var value = CompilerConstants.IntegerNotSet; | ||
1297 | var behavior = ExitCodeBehaviorType.NotSet; | ||
1298 | |||
1299 | foreach (var attrib in node.Attributes()) | ||
1300 | { | ||
1301 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
1302 | { | ||
1303 | switch (attrib.Name.LocalName) | ||
1304 | { | ||
1305 | case "Value": | ||
1306 | value = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, Int32.MinValue + 2, Int32.MaxValue); | ||
1307 | break; | ||
1308 | case "Behavior": | ||
1309 | var behaviorString = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1310 | if (!Enum.TryParse<ExitCodeBehaviorType>(behaviorString, true, out behavior)) | ||
1311 | { | ||
1312 | this.Core.Write(ErrorMessages.IllegalAttributeValueWithLegalList(sourceLineNumbers, node.Name.LocalName, "Behavior", behaviorString, "success, error, scheduleReboot, forceReboot")); | ||
1313 | } | ||
1314 | break; | ||
1315 | default: | ||
1316 | this.Core.UnexpectedAttribute(node, attrib); | ||
1317 | break; | ||
1318 | } | ||
1319 | } | ||
1320 | else | ||
1321 | { | ||
1322 | this.Core.ParseExtensionAttribute(node, attrib); | ||
1323 | } | ||
1324 | } | ||
1325 | |||
1326 | if (ExitCodeBehaviorType.NotSet == behavior) | ||
1327 | { | ||
1328 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Behavior")); | ||
1329 | } | ||
1330 | |||
1331 | this.Core.ParseForExtensionElements(node); | ||
1332 | |||
1333 | if (!this.Core.EncounteredError) | ||
1334 | { | ||
1335 | var row = (WixBundlePackageExitCodeTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundlePackageExitCode); | ||
1336 | row.ChainPackageId = packageId; | ||
1337 | row.Code = value; | ||
1338 | row.Behavior = behavior; | ||
1339 | } | ||
1340 | } | ||
1341 | |||
1342 | /// <summary> | ||
1343 | /// Parse Chain element. | ||
1344 | /// </summary> | ||
1345 | /// <param name="node">Element to parse</param> | ||
1346 | private void ParseChainElement(XElement node) | ||
1347 | { | ||
1348 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
1349 | var attributes = WixChainAttributes.None; | ||
1350 | |||
1351 | foreach (var attrib in node.Attributes()) | ||
1352 | { | ||
1353 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
1354 | { | ||
1355 | switch (attrib.Name.LocalName) | ||
1356 | { | ||
1357 | case "DisableRollback": | ||
1358 | if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) | ||
1359 | { | ||
1360 | attributes |= WixChainAttributes.DisableRollback; | ||
1361 | } | ||
1362 | break; | ||
1363 | case "DisableSystemRestore": | ||
1364 | if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) | ||
1365 | { | ||
1366 | attributes |= WixChainAttributes.DisableSystemRestore; | ||
1367 | } | ||
1368 | break; | ||
1369 | case "ParallelCache": | ||
1370 | if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) | ||
1371 | { | ||
1372 | attributes |= WixChainAttributes.ParallelCache; | ||
1373 | } | ||
1374 | break; | ||
1375 | default: | ||
1376 | this.Core.UnexpectedAttribute(node, attrib); | ||
1377 | break; | ||
1378 | } | ||
1379 | } | ||
1380 | else | ||
1381 | { | ||
1382 | this.Core.ParseExtensionAttribute(node, attrib); | ||
1383 | } | ||
1384 | } | ||
1385 | |||
1386 | // Ensure there is always a rollback boundary at the beginning of the chain. | ||
1387 | this.CreateRollbackBoundary(sourceLineNumbers, new Identifier("WixDefaultBoundary", AccessModifier.Public), YesNoType.Yes, YesNoType.No, ComplexReferenceParentType.PackageGroup, "WixChain", ComplexReferenceChildType.Unknown, null); | ||
1388 | |||
1389 | var previousId = "WixDefaultBoundary"; | ||
1390 | var previousType = ComplexReferenceChildType.Package; | ||
1391 | |||
1392 | foreach (var child in node.Elements()) | ||
1393 | { | ||
1394 | if (CompilerCore.WixNamespace == child.Name.Namespace) | ||
1395 | { | ||
1396 | switch (child.Name.LocalName) | ||
1397 | { | ||
1398 | case "MsiPackage": | ||
1399 | previousId = this.ParseMsiPackageElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId); | ||
1400 | previousType = ComplexReferenceChildType.Package; | ||
1401 | break; | ||
1402 | case "MspPackage": | ||
1403 | previousId = this.ParseMspPackageElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId); | ||
1404 | previousType = ComplexReferenceChildType.Package; | ||
1405 | break; | ||
1406 | case "MsuPackage": | ||
1407 | previousId = this.ParseMsuPackageElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId); | ||
1408 | previousType = ComplexReferenceChildType.Package; | ||
1409 | break; | ||
1410 | case "ExePackage": | ||
1411 | previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId); | ||
1412 | previousType = ComplexReferenceChildType.Package; | ||
1413 | break; | ||
1414 | case "RollbackBoundary": | ||
1415 | previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId); | ||
1416 | previousType = ComplexReferenceChildType.Package; | ||
1417 | break; | ||
1418 | case "PackageGroupRef": | ||
1419 | previousId = this.ParsePackageGroupRefElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId); | ||
1420 | previousType = ComplexReferenceChildType.PackageGroup; | ||
1421 | break; | ||
1422 | default: | ||
1423 | this.Core.UnexpectedElement(node, child); | ||
1424 | break; | ||
1425 | } | ||
1426 | } | ||
1427 | else | ||
1428 | { | ||
1429 | this.Core.ParseExtensionElement(node, child); | ||
1430 | } | ||
1431 | } | ||
1432 | |||
1433 | |||
1434 | if (null == previousId) | ||
1435 | { | ||
1436 | this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "MsiPackage", "ExePackage", "PackageGroupRef")); | ||
1437 | } | ||
1438 | |||
1439 | if (!this.Core.EncounteredError) | ||
1440 | { | ||
1441 | var row = (WixChainTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixChain); | ||
1442 | row.Attributes = attributes; | ||
1443 | } | ||
1444 | } | ||
1445 | |||
1446 | /// <summary> | ||
1447 | /// Parse MsiPackage element | ||
1448 | /// </summary> | ||
1449 | /// <param name="node">Element to parse</param> | ||
1450 | /// <param name="parentType">Type of parent group, if known.</param> | ||
1451 | /// <param name="parentId">Identifier of parent group, if known.</param> | ||
1452 | /// <param name="previousType">Type of previous item, if known.</param> | ||
1453 | /// <param name="previousId">Identifier of previous item, if known</param> | ||
1454 | /// <returns>Identifier for package element.</returns> | ||
1455 | private string ParseMsiPackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId) | ||
1456 | { | ||
1457 | return this.ParseChainPackage(node, WixBundlePackageType.Msi, parentType, parentId, previousType, previousId); | ||
1458 | } | ||
1459 | |||
1460 | /// <summary> | ||
1461 | /// Parse MspPackage element | ||
1462 | /// </summary> | ||
1463 | /// <param name="node">Element to parse</param> | ||
1464 | /// <param name="parentType">Type of parent group, if known.</param> | ||
1465 | /// <param name="parentId">Identifier of parent group, if known.</param> | ||
1466 | /// <param name="previousType">Type of previous item, if known.</param> | ||
1467 | /// <param name="previousId">Identifier of previous item, if known</param> | ||
1468 | /// <returns>Identifier for package element.</returns> | ||
1469 | private string ParseMspPackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId) | ||
1470 | { | ||
1471 | return this.ParseChainPackage(node, WixBundlePackageType.Msp, parentType, parentId, previousType, previousId); | ||
1472 | } | ||
1473 | |||
1474 | /// <summary> | ||
1475 | /// Parse MsuPackage element | ||
1476 | /// </summary> | ||
1477 | /// <param name="node">Element to parse</param> | ||
1478 | /// <param name="parentType">Type of parent group, if known.</param> | ||
1479 | /// <param name="parentId">Identifier of parent group, if known.</param> | ||
1480 | /// <param name="previousType">Type of previous item, if known.</param> | ||
1481 | /// <param name="previousId">Identifier of previous item, if known</param> | ||
1482 | /// <returns>Identifier for package element.</returns> | ||
1483 | private string ParseMsuPackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId) | ||
1484 | { | ||
1485 | return this.ParseChainPackage(node, WixBundlePackageType.Msu, parentType, parentId, previousType, previousId); | ||
1486 | } | ||
1487 | |||
1488 | /// <summary> | ||
1489 | /// Parse ExePackage element | ||
1490 | /// </summary> | ||
1491 | /// <param name="node">Element to parse</param> | ||
1492 | /// <param name="parentType">Type of parent group, if known.</param> | ||
1493 | /// <param name="parentId">Identifier of parent group, if known.</param> | ||
1494 | /// <param name="previousType">Type of previous item, if known.</param> | ||
1495 | /// <param name="previousId">Identifier of previous item, if known</param> | ||
1496 | /// <returns>Identifier for package element.</returns> | ||
1497 | private string ParseExePackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId) | ||
1498 | { | ||
1499 | return this.ParseChainPackage(node, WixBundlePackageType.Exe, parentType, parentId, previousType, previousId); | ||
1500 | } | ||
1501 | |||
1502 | /// <summary> | ||
1503 | /// Parse RollbackBoundary element | ||
1504 | /// </summary> | ||
1505 | /// <param name="node">Element to parse</param> | ||
1506 | /// <param name="parentType">Type of parent group, if known.</param> | ||
1507 | /// <param name="parentId">Identifier of parent group, if known.</param> | ||
1508 | /// <param name="previousType">Type of previous item, if known.</param> | ||
1509 | /// <param name="previousId">Identifier of previous item, if known</param> | ||
1510 | /// <returns>Identifier for package element.</returns> | ||
1511 | private string ParseRollbackBoundaryElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId) | ||
1512 | { | ||
1513 | Debug.Assert(ComplexReferenceParentType.PackageGroup == parentType); | ||
1514 | Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType); | ||
1515 | |||
1516 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
1517 | Identifier id = null; | ||
1518 | var vital = YesNoType.Yes; | ||
1519 | var transaction = YesNoType.No; | ||
1520 | |||
1521 | // This list lets us evaluate extension attributes *after* all core attributes | ||
1522 | // have been parsed and dealt with, regardless of authoring order. | ||
1523 | var extensionAttributes = new List<XAttribute>(); | ||
1524 | |||
1525 | foreach (var attrib in node.Attributes()) | ||
1526 | { | ||
1527 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
1528 | { | ||
1529 | var allowed = true; | ||
1530 | switch (attrib.Name.LocalName) | ||
1531 | { | ||
1532 | case "Id": | ||
1533 | id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); | ||
1534 | break; | ||
1535 | case "Vital": | ||
1536 | vital = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); | ||
1537 | break; | ||
1538 | case "Transaction": | ||
1539 | transaction = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); | ||
1540 | break; | ||
1541 | default: | ||
1542 | allowed = false; | ||
1543 | break; | ||
1544 | } | ||
1545 | |||
1546 | if (!allowed) | ||
1547 | { | ||
1548 | this.Core.UnexpectedAttribute(node, attrib); | ||
1549 | } | ||
1550 | } | ||
1551 | else | ||
1552 | { | ||
1553 | // Save the extension attributes for later... | ||
1554 | extensionAttributes.Add(attrib); | ||
1555 | } | ||
1556 | } | ||
1557 | |||
1558 | if (null == id) | ||
1559 | { | ||
1560 | if (!String.IsNullOrEmpty(previousId)) | ||
1561 | { | ||
1562 | id = this.Core.CreateIdentifier("rba", previousId); | ||
1563 | } | ||
1564 | |||
1565 | if (null == id) | ||
1566 | { | ||
1567 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); | ||
1568 | id = Identifier.Invalid; | ||
1569 | } | ||
1570 | else if (!Common.IsIdentifier(id.Id)) | ||
1571 | { | ||
1572 | this.Core.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, node.Name.LocalName, "Id", id.Id)); | ||
1573 | } | ||
1574 | } | ||
1575 | |||
1576 | // Now that the rollback identifier is known, we can parse the extension attributes... | ||
1577 | var contextValues = new Dictionary<string, string> | ||
1578 | { | ||
1579 | ["RollbackBoundaryId"] = id.Id | ||
1580 | }; | ||
1581 | foreach (var attribute in extensionAttributes) | ||
1582 | { | ||
1583 | this.Core.ParseExtensionAttribute(node, attribute, contextValues); | ||
1584 | } | ||
1585 | |||
1586 | this.Core.ParseForExtensionElements(node); | ||
1587 | |||
1588 | if (!this.Core.EncounteredError) | ||
1589 | { | ||
1590 | this.CreateRollbackBoundary(sourceLineNumbers, id, vital, transaction, parentType, parentId, previousType, previousId); | ||
1591 | } | ||
1592 | |||
1593 | return id.Id; | ||
1594 | } | ||
1595 | |||
1596 | /// <summary> | ||
1597 | /// Parses one of the ChainPackage elements | ||
1598 | /// </summary> | ||
1599 | /// <param name="node">Element to parse</param> | ||
1600 | /// <param name="packageType">Type of package to parse</param> | ||
1601 | /// <param name="parentType">Type of parent group, if known.</param> | ||
1602 | /// <param name="parentId">Identifier of parent group, if known.</param> | ||
1603 | /// <param name="previousType">Type of previous item, if known.</param> | ||
1604 | /// <param name="previousId">Identifier of previous item, if known</param> | ||
1605 | /// <returns>Identifier for package element.</returns> | ||
1606 | /// <remarks>This method contains the shared logic for parsing all of the ChainPackage | ||
1607 | /// types, as there is more in common between them than different.</remarks> | ||
1608 | private string ParseChainPackage(XElement node, WixBundlePackageType packageType, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId) | ||
1609 | { | ||
1610 | Debug.Assert(ComplexReferenceParentType.PackageGroup == parentType); | ||
1611 | Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType); | ||
1612 | |||
1613 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
1614 | Identifier id = null; | ||
1615 | string name = null; | ||
1616 | string sourceFile = null; | ||
1617 | string downloadUrl = null; | ||
1618 | string after = null; | ||
1619 | string installCondition = null; | ||
1620 | var cache = YesNoAlwaysType.Yes; // the default is to cache everything in tradeoff for stability over disk space. | ||
1621 | string cacheId = null; | ||
1622 | string description = null; | ||
1623 | string displayName = null; | ||
1624 | var logPathVariable = (packageType == WixBundlePackageType.Msu) ? String.Empty : null; | ||
1625 | var rollbackPathVariable = (packageType == WixBundlePackageType.Msu) ? String.Empty : null; | ||
1626 | var permanent = YesNoType.NotSet; | ||
1627 | var visible = YesNoType.NotSet; | ||
1628 | var vital = YesNoType.Yes; | ||
1629 | string installCommand = null; | ||
1630 | string repairCommand = null; | ||
1631 | var repairable = YesNoType.NotSet; | ||
1632 | string uninstallCommand = null; | ||
1633 | var perMachine = YesNoDefaultType.NotSet; | ||
1634 | string detectCondition = null; | ||
1635 | string protocol = null; | ||
1636 | var installSize = CompilerConstants.IntegerNotSet; | ||
1637 | string msuKB = null; | ||
1638 | var suppressLooseFilePayloadGeneration = YesNoType.NotSet; | ||
1639 | var enableSignatureVerification = YesNoType.No; | ||
1640 | var compressed = YesNoDefaultType.Default; | ||
1641 | var displayInternalUI = YesNoType.NotSet; | ||
1642 | var enableFeatureSelection = YesNoType.NotSet; | ||
1643 | var forcePerMachine = YesNoType.NotSet; | ||
1644 | RemotePayload remotePayload = null; | ||
1645 | var slipstream = YesNoType.NotSet; | ||
1646 | |||
1647 | var expectedNetFx4Args = new string[] { "/q", "/norestart", "/chainingpackage" }; | ||
1648 | |||
1649 | // This list lets us evaluate extension attributes *after* all core attributes | ||
1650 | // have been parsed and dealt with, regardless of authoring order. | ||
1651 | var extensionAttributes = new List<XAttribute>(); | ||
1652 | |||
1653 | foreach (var attrib in node.Attributes()) | ||
1654 | { | ||
1655 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
1656 | { | ||
1657 | var allowed = true; | ||
1658 | switch (attrib.Name.LocalName) | ||
1659 | { | ||
1660 | case "Id": | ||
1661 | id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); | ||
1662 | break; | ||
1663 | case "Name": | ||
1664 | name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false, true); | ||
1665 | if (!this.Core.IsValidLongFilename(name, false, true)) | ||
1666 | { | ||
1667 | this.Core.Write(ErrorMessages.IllegalLongFilename(sourceLineNumbers, node.Name.LocalName, "Name", name)); | ||
1668 | } | ||
1669 | break; | ||
1670 | case "SourceFile": | ||
1671 | sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1672 | break; | ||
1673 | case "DownloadUrl": | ||
1674 | downloadUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1675 | break; | ||
1676 | case "After": | ||
1677 | after = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1678 | break; | ||
1679 | case "InstallCondition": | ||
1680 | installCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1681 | break; | ||
1682 | case "Cache": | ||
1683 | cache = this.Core.GetAttributeYesNoAlwaysValue(sourceLineNumbers, attrib); | ||
1684 | break; | ||
1685 | case "CacheId": | ||
1686 | cacheId = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1687 | break; | ||
1688 | case "Description": | ||
1689 | description = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1690 | break; | ||
1691 | case "DisplayName": | ||
1692 | displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1693 | break; | ||
1694 | case "DisplayInternalUI": | ||
1695 | displayInternalUI = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); | ||
1696 | allowed = (packageType == WixBundlePackageType.Msi || packageType == WixBundlePackageType.Msp); | ||
1697 | break; | ||
1698 | case "EnableFeatureSelection": | ||
1699 | enableFeatureSelection = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); | ||
1700 | allowed = (packageType == WixBundlePackageType.Msi); | ||
1701 | break; | ||
1702 | case "ForcePerMachine": | ||
1703 | forcePerMachine = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); | ||
1704 | allowed = (packageType == WixBundlePackageType.Msi); | ||
1705 | break; | ||
1706 | case "LogPathVariable": | ||
1707 | logPathVariable = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); | ||
1708 | break; | ||
1709 | case "RollbackLogPathVariable": | ||
1710 | rollbackPathVariable = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); | ||
1711 | break; | ||
1712 | case "Permanent": | ||
1713 | permanent = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); | ||
1714 | break; | ||
1715 | case "Visible": | ||
1716 | visible = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); | ||
1717 | allowed = (packageType == WixBundlePackageType.Msi); | ||
1718 | break; | ||
1719 | case "Vital": | ||
1720 | vital = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); | ||
1721 | break; | ||
1722 | case "InstallCommand": | ||
1723 | installCommand = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1724 | allowed = (packageType == WixBundlePackageType.Exe); | ||
1725 | break; | ||
1726 | case "RepairCommand": | ||
1727 | repairCommand = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); | ||
1728 | repairable = YesNoType.Yes; | ||
1729 | allowed = (packageType == WixBundlePackageType.Exe); | ||
1730 | break; | ||
1731 | case "UninstallCommand": | ||
1732 | uninstallCommand = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1733 | allowed = (packageType == WixBundlePackageType.Exe); | ||
1734 | break; | ||
1735 | case "PerMachine": | ||
1736 | perMachine = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); | ||
1737 | allowed = (packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msp); | ||
1738 | break; | ||
1739 | case "DetectCondition": | ||
1740 | detectCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1741 | allowed = (packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msu); | ||
1742 | break; | ||
1743 | case "Protocol": | ||
1744 | protocol = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1745 | allowed = (packageType == WixBundlePackageType.Exe); | ||
1746 | break; | ||
1747 | case "InstallSize": | ||
1748 | installSize = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); | ||
1749 | break; | ||
1750 | case "KB": | ||
1751 | msuKB = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
1752 | allowed = (packageType == WixBundlePackageType.Msu); | ||
1753 | break; | ||
1754 | case "Compressed": | ||
1755 | compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); | ||
1756 | break; | ||
1757 | case "SuppressLooseFilePayloadGeneration": | ||
1758 | this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName)); | ||
1759 | suppressLooseFilePayloadGeneration = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); | ||
1760 | allowed = (packageType == WixBundlePackageType.Msi); | ||
1761 | break; | ||
1762 | case "EnableSignatureVerification": | ||
1763 | enableSignatureVerification = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); | ||
1764 | break; | ||
1765 | case "Slipstream": | ||
1766 | slipstream = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); | ||
1767 | allowed = (packageType == WixBundlePackageType.Msp); | ||
1768 | break; | ||
1769 | default: | ||
1770 | allowed = false; | ||
1771 | break; | ||
1772 | } | ||
1773 | |||
1774 | if (!allowed) | ||
1775 | { | ||
1776 | this.Core.UnexpectedAttribute(node, attrib); | ||
1777 | } | ||
1778 | } | ||
1779 | else | ||
1780 | { | ||
1781 | // Save the extension attributes for later... | ||
1782 | extensionAttributes.Add(attrib); | ||
1783 | } | ||
1784 | } | ||
1785 | |||
1786 | // We need to handle RemotePayload up front because it effects value of sourceFile which is used in Id generation. Id is needed by other child elements. | ||
1787 | foreach (var child in node.Elements(CompilerCore.WixNamespace + "RemotePayload")) | ||
1788 | { | ||
1789 | var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); | ||
1790 | |||
1791 | if (CompilerCore.WixNamespace == node.Name.Namespace && node.Name.LocalName != "ExePackage" && node.Name.LocalName != "MsuPackage") | ||
1792 | { | ||
1793 | this.Core.Write(ErrorMessages.RemotePayloadUnsupported(childSourceLineNumbers)); | ||
1794 | continue; | ||
1795 | } | ||
1796 | |||
1797 | if (null != remotePayload) | ||
1798 | { | ||
1799 | this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName)); | ||
1800 | } | ||
1801 | |||
1802 | remotePayload = this.ParseRemotePayloadElement(child); | ||
1803 | } | ||
1804 | |||
1805 | if (String.IsNullOrEmpty(sourceFile)) | ||
1806 | { | ||
1807 | if (String.IsNullOrEmpty(name)) | ||
1808 | { | ||
1809 | this.Core.Write(ErrorMessages.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Name", "SourceFile")); | ||
1810 | } | ||
1811 | else if (null == remotePayload) | ||
1812 | { | ||
1813 | sourceFile = Path.Combine("SourceDir", name); | ||
1814 | } | ||
1815 | } | ||
1816 | else if (null != remotePayload) | ||
1817 | { | ||
1818 | this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, "RemotePayload", "SourceFile")); | ||
1819 | } | ||
1820 | else if (sourceFile.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) | ||
1821 | { | ||
1822 | if (String.IsNullOrEmpty(name)) | ||
1823 | { | ||
1824 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name", "SourceFile", sourceFile)); | ||
1825 | } | ||
1826 | else | ||
1827 | { | ||
1828 | sourceFile = Path.Combine(sourceFile, Path.GetFileName(name)); | ||
1829 | } | ||
1830 | } | ||
1831 | |||
1832 | if (null == downloadUrl && null != remotePayload) | ||
1833 | { | ||
1834 | this.Core.Write(ErrorMessages.ExpectedAttributeWithElement(sourceLineNumbers, node.Name.LocalName, "DownloadUrl", "RemotePayload")); | ||
1835 | } | ||
1836 | |||
1837 | if (YesNoDefaultType.No != compressed && null != remotePayload) | ||
1838 | { | ||
1839 | compressed = YesNoDefaultType.No; | ||
1840 | this.Core.Write(WarningMessages.RemotePayloadsMustNotAlsoBeCompressed(sourceLineNumbers, node.Name.LocalName)); | ||
1841 | } | ||
1842 | |||
1843 | if (null == id) | ||
1844 | { | ||
1845 | if (!String.IsNullOrEmpty(name)) | ||
1846 | { | ||
1847 | id = this.Core.CreateIdentifierFromFilename(Path.GetFileName(name)); | ||
1848 | } | ||
1849 | else if (!String.IsNullOrEmpty(sourceFile)) | ||
1850 | { | ||
1851 | id = this.Core.CreateIdentifierFromFilename(Path.GetFileName(sourceFile)); | ||
1852 | } | ||
1853 | |||
1854 | if (null == id) | ||
1855 | { | ||
1856 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); | ||
1857 | id = Identifier.Invalid; | ||
1858 | } | ||
1859 | else if (!Common.IsIdentifier(id.Id)) | ||
1860 | { | ||
1861 | this.Core.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, node.Name.LocalName, "Id", id.Id)); | ||
1862 | } | ||
1863 | } | ||
1864 | |||
1865 | if (null == logPathVariable) | ||
1866 | { | ||
1867 | logPathVariable = String.Concat("WixBundleLog_", id.Id); | ||
1868 | } | ||
1869 | |||
1870 | if (null == rollbackPathVariable) | ||
1871 | { | ||
1872 | rollbackPathVariable = String.Concat("WixBundleRollbackLog_", id.Id); | ||
1873 | } | ||
1874 | |||
1875 | if (!String.IsNullOrEmpty(protocol) && !protocol.Equals("burn", StringComparison.Ordinal) && !protocol.Equals("netfx4", StringComparison.Ordinal) && !protocol.Equals("none", StringComparison.Ordinal)) | ||
1876 | { | ||
1877 | this.Core.Write(ErrorMessages.IllegalAttributeValueWithLegalList(sourceLineNumbers, node.Name.LocalName, "Protocol", protocol, "none, burn, netfx4")); | ||
1878 | } | ||
1879 | |||
1880 | if (!String.IsNullOrEmpty(protocol) && protocol.Equals("netfx4", StringComparison.Ordinal)) | ||
1881 | { | ||
1882 | foreach (var expectedArgument in expectedNetFx4Args) | ||
1883 | { | ||
1884 | if (null == installCommand || -1 == installCommand.IndexOf(expectedArgument, StringComparison.OrdinalIgnoreCase)) | ||
1885 | { | ||
1886 | this.Core.Write(WarningMessages.AttributeShouldContain(sourceLineNumbers, node.Name.LocalName, "InstallCommand", installCommand, expectedArgument, "Protocol", "netfx4")); | ||
1887 | } | ||
1888 | |||
1889 | if (null == repairCommand || -1 == repairCommand.IndexOf(expectedArgument, StringComparison.OrdinalIgnoreCase)) | ||
1890 | { | ||
1891 | this.Core.Write(WarningMessages.AttributeShouldContain(sourceLineNumbers, node.Name.LocalName, "RepairCommand", repairCommand, expectedArgument, "Protocol", "netfx4")); | ||
1892 | } | ||
1893 | |||
1894 | if (null == uninstallCommand || -1 == uninstallCommand.IndexOf(expectedArgument, StringComparison.OrdinalIgnoreCase)) | ||
1895 | { | ||
1896 | this.Core.Write(WarningMessages.AttributeShouldContain(sourceLineNumbers, node.Name.LocalName, "UninstallCommand", uninstallCommand, expectedArgument, "Protocol", "netfx4")); | ||
1897 | } | ||
1898 | } | ||
1899 | } | ||
1900 | |||
1901 | // Only set default scope for EXEs and MSPs if not already set. | ||
1902 | if ((WixBundlePackageType.Exe == packageType || WixBundlePackageType.Msp == packageType) && YesNoDefaultType.NotSet == perMachine) | ||
1903 | { | ||
1904 | perMachine = YesNoDefaultType.Default; | ||
1905 | } | ||
1906 | |||
1907 | // Now that the package ID is known, we can parse the extension attributes... | ||
1908 | var contextValues = new Dictionary<string, string>() { { "PackageId", id.Id } }; | ||
1909 | foreach (var attribute in extensionAttributes) | ||
1910 | { | ||
1911 | this.Core.ParseExtensionAttribute(node, attribute, contextValues); | ||
1912 | } | ||
1913 | |||
1914 | foreach (var child in node.Elements()) | ||
1915 | { | ||
1916 | if (CompilerCore.WixNamespace == child.Name.Namespace) | ||
1917 | { | ||
1918 | var allowed = true; | ||
1919 | switch (child.Name.LocalName) | ||
1920 | { | ||
1921 | case "SlipstreamMsp": | ||
1922 | allowed = (packageType == WixBundlePackageType.Msi); | ||
1923 | if (allowed) | ||
1924 | { | ||
1925 | this.ParseSlipstreamMspElement(child, id.Id); | ||
1926 | } | ||
1927 | break; | ||
1928 | case "MsiProperty": | ||
1929 | allowed = (packageType == WixBundlePackageType.Msi || packageType == WixBundlePackageType.Msp); | ||
1930 | if (allowed) | ||
1931 | { | ||
1932 | this.ParseMsiPropertyElement(child, id.Id); | ||
1933 | } | ||
1934 | break; | ||
1935 | case "Payload": | ||
1936 | this.ParsePayloadElement(child, ComplexReferenceParentType.Package, id.Id, ComplexReferenceChildType.Unknown, null); | ||
1937 | break; | ||
1938 | case "PayloadGroupRef": | ||
1939 | this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Package, id.Id, ComplexReferenceChildType.Unknown, null); | ||
1940 | break; | ||
1941 | case "ExitCode": | ||
1942 | allowed = (packageType == WixBundlePackageType.Exe); | ||
1943 | if (allowed) | ||
1944 | { | ||
1945 | this.ParseExitCodeElement(child, id.Id); | ||
1946 | } | ||
1947 | break; | ||
1948 | case "CommandLine": | ||
1949 | allowed = (packageType == WixBundlePackageType.Exe); | ||
1950 | if (allowed) | ||
1951 | { | ||
1952 | this.ParseCommandLineElement(child, id.Id); | ||
1953 | } | ||
1954 | break; | ||
1955 | case "RemotePayload": | ||
1956 | // Handled previously | ||
1957 | break; | ||
1958 | default: | ||
1959 | allowed = false; | ||
1960 | break; | ||
1961 | } | ||
1962 | |||
1963 | if (!allowed) | ||
1964 | { | ||
1965 | this.Core.UnexpectedElement(node, child); | ||
1966 | } | ||
1967 | } | ||
1968 | else | ||
1969 | { | ||
1970 | var context = new Dictionary<string, string>() { { "Id", id.Id } }; | ||
1971 | this.Core.ParseExtensionElement(node, child, context); | ||
1972 | } | ||
1973 | } | ||
1974 | |||
1975 | if (!this.Core.EncounteredError) | ||
1976 | { | ||
1977 | // We create the package contents as a payload with this package as the parent | ||
1978 | this.CreatePayloadRow(sourceLineNumbers, id, name, sourceFile, downloadUrl, ComplexReferenceParentType.Package, id.Id, | ||
1979 | ComplexReferenceChildType.Unknown, null, compressed, enableSignatureVerification, displayName, description, remotePayload); | ||
1980 | |||
1981 | var chainItemRow = (WixChainItemTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixChainItem, id); | ||
1982 | |||
1983 | WixBundlePackageAttributes attributes = 0; | ||
1984 | attributes |= (YesNoType.Yes == permanent) ? WixBundlePackageAttributes.Permanent : 0; | ||
1985 | attributes |= (YesNoType.Yes == visible) ? WixBundlePackageAttributes.Visible : 0; | ||
1986 | |||
1987 | var chainPackageRow = (WixBundlePackageTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundlePackage, id); | ||
1988 | chainPackageRow.Type = packageType; | ||
1989 | chainPackageRow.Payload_ = id.Id; | ||
1990 | chainPackageRow.Attributes = attributes; | ||
1991 | |||
1992 | chainPackageRow.InstallCondition = installCondition; | ||
1993 | |||
1994 | if (YesNoAlwaysType.NotSet != cache) | ||
1995 | { | ||
1996 | chainPackageRow.Cache = cache; | ||
1997 | } | ||
1998 | |||
1999 | chainPackageRow.CacheId = cacheId; | ||
2000 | |||
2001 | if (YesNoType.NotSet != vital) | ||
2002 | { | ||
2003 | chainPackageRow.Vital = (vital == YesNoType.Yes); | ||
2004 | } | ||
2005 | |||
2006 | if (YesNoDefaultType.NotSet != perMachine) | ||
2007 | { | ||
2008 | chainPackageRow.PerMachine = perMachine; | ||
2009 | } | ||
2010 | |||
2011 | chainPackageRow.LogPathVariable = logPathVariable; | ||
2012 | chainPackageRow.RollbackLogPathVariable = rollbackPathVariable; | ||
2013 | |||
2014 | if (CompilerConstants.IntegerNotSet != installSize) | ||
2015 | { | ||
2016 | chainPackageRow.InstallSize = installSize; | ||
2017 | } | ||
2018 | |||
2019 | switch (packageType) | ||
2020 | { | ||
2021 | case WixBundlePackageType.Exe: | ||
2022 | WixBundleExePackageAttributes exeAttributes = 0; | ||
2023 | exeAttributes |= (YesNoType.Yes == repairable) ? WixBundleExePackageAttributes.Repairable : 0; | ||
2024 | |||
2025 | var exeRow = (WixBundleExePackageTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleExePackage, id); | ||
2026 | exeRow.Attributes = exeAttributes; | ||
2027 | exeRow.DetectCondition = detectCondition; | ||
2028 | exeRow.InstallCommand = installCommand; | ||
2029 | exeRow.RepairCommand = repairCommand; | ||
2030 | exeRow.UninstallCommand = uninstallCommand; | ||
2031 | exeRow.ExeProtocol = protocol; | ||
2032 | break; | ||
2033 | |||
2034 | case WixBundlePackageType.Msi: | ||
2035 | WixBundleMsiPackageAttributes msiAttributes = 0; | ||
2036 | msiAttributes |= (YesNoType.Yes == displayInternalUI) ? WixBundleMsiPackageAttributes.DisplayInternalUI : 0; | ||
2037 | msiAttributes |= (YesNoType.Yes == enableFeatureSelection) ? WixBundleMsiPackageAttributes.EnableFeatureSelection : 0; | ||
2038 | msiAttributes |= (YesNoType.Yes == forcePerMachine) ? WixBundleMsiPackageAttributes.ForcePerMachine : 0; | ||
2039 | msiAttributes |= (YesNoType.Yes == suppressLooseFilePayloadGeneration) ? WixBundleMsiPackageAttributes.SuppressLooseFilePayloadGeneration : 0; | ||
2040 | |||
2041 | var msiRow = (WixBundleMsiPackageTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleMsiPackage, id); | ||
2042 | msiRow.Attributes = msiAttributes; | ||
2043 | break; | ||
2044 | |||
2045 | case WixBundlePackageType.Msp: | ||
2046 | WixBundleMspPackageAttributes mspAttributes = 0; | ||
2047 | mspAttributes |= (YesNoType.Yes == displayInternalUI) ? WixBundleMspPackageAttributes.DisplayInternalUI : 0; | ||
2048 | mspAttributes |= (YesNoType.Yes == slipstream) ? WixBundleMspPackageAttributes.Slipstream : 0; | ||
2049 | |||
2050 | var mspRow = (WixBundleMspPackageTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleMspPackage, id); | ||
2051 | mspRow.Attributes = mspAttributes; | ||
2052 | break; | ||
2053 | |||
2054 | case WixBundlePackageType.Msu: | ||
2055 | var msuRow = (WixBundleMsuPackageTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleMsuPackage, id); | ||
2056 | msuRow.DetectCondition = detectCondition; | ||
2057 | msuRow.MsuKB = msuKB; | ||
2058 | break; | ||
2059 | } | ||
2060 | |||
2061 | this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Package, id.Id, previousType, previousId, after); | ||
2062 | } | ||
2063 | |||
2064 | return id.Id; | ||
2065 | } | ||
2066 | |||
2067 | /// <summary> | ||
2068 | /// Parse CommandLine element. | ||
2069 | /// </summary> | ||
2070 | /// <param name="node">Element to parse</param> | ||
2071 | private void ParseCommandLineElement(XElement node, string packageId) | ||
2072 | { | ||
2073 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
2074 | string installArgument = null; | ||
2075 | string uninstallArgument = null; | ||
2076 | string repairArgument = null; | ||
2077 | string condition = null; | ||
2078 | |||
2079 | foreach (var attrib in node.Attributes()) | ||
2080 | { | ||
2081 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
2082 | { | ||
2083 | switch (attrib.Name.LocalName) | ||
2084 | { | ||
2085 | case "InstallArgument": | ||
2086 | installArgument = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
2087 | break; | ||
2088 | case "UninstallArgument": | ||
2089 | uninstallArgument = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
2090 | break; | ||
2091 | case "RepairArgument": | ||
2092 | repairArgument = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
2093 | break; | ||
2094 | case "Condition": | ||
2095 | condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
2096 | break; | ||
2097 | default: | ||
2098 | this.Core.UnexpectedAttribute(node, attrib); | ||
2099 | break; | ||
2100 | } | ||
2101 | } | ||
2102 | else | ||
2103 | { | ||
2104 | this.Core.ParseExtensionAttribute(node, attrib); | ||
2105 | } | ||
2106 | } | ||
2107 | |||
2108 | if (String.IsNullOrEmpty(condition)) | ||
2109 | { | ||
2110 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Condition")); | ||
2111 | } | ||
2112 | |||
2113 | this.Core.ParseForExtensionElements(node); | ||
2114 | |||
2115 | if (!this.Core.EncounteredError) | ||
2116 | { | ||
2117 | var row = (WixBundlePackageCommandLineTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundlePackageCommandLine); | ||
2118 | row.WixBundlePackage_ = packageId; | ||
2119 | row.InstallArgument = installArgument; | ||
2120 | row.UninstallArgument = uninstallArgument; | ||
2121 | row.RepairArgument = repairArgument; | ||
2122 | row.Condition = condition; | ||
2123 | } | ||
2124 | } | ||
2125 | |||
2126 | /// <summary> | ||
2127 | /// Parse PackageGroup element. | ||
2128 | /// </summary> | ||
2129 | /// <param name="node">Element to parse</param> | ||
2130 | private void ParsePackageGroupElement(XElement node) | ||
2131 | { | ||
2132 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
2133 | Identifier id = null; | ||
2134 | |||
2135 | foreach (var attrib in node.Attributes()) | ||
2136 | { | ||
2137 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
2138 | { | ||
2139 | switch (attrib.Name.LocalName) | ||
2140 | { | ||
2141 | case "Id": | ||
2142 | id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); | ||
2143 | break; | ||
2144 | default: | ||
2145 | this.Core.UnexpectedAttribute(node, attrib); | ||
2146 | break; | ||
2147 | } | ||
2148 | } | ||
2149 | else | ||
2150 | { | ||
2151 | this.Core.ParseExtensionAttribute(node, attrib); | ||
2152 | } | ||
2153 | } | ||
2154 | |||
2155 | if (null == id) | ||
2156 | { | ||
2157 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); | ||
2158 | id = Identifier.Invalid; | ||
2159 | } | ||
2160 | |||
2161 | var previousType = ComplexReferenceChildType.Unknown; | ||
2162 | string previousId = null; | ||
2163 | foreach (var child in node.Elements()) | ||
2164 | { | ||
2165 | if (CompilerCore.WixNamespace == child.Name.Namespace) | ||
2166 | { | ||
2167 | switch (child.Name.LocalName) | ||
2168 | { | ||
2169 | case "MsiPackage": | ||
2170 | previousId = this.ParseMsiPackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId); | ||
2171 | previousType = ComplexReferenceChildType.Package; | ||
2172 | break; | ||
2173 | case "MspPackage": | ||
2174 | previousId = this.ParseMspPackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId); | ||
2175 | previousType = ComplexReferenceChildType.Package; | ||
2176 | break; | ||
2177 | case "MsuPackage": | ||
2178 | previousId = this.ParseMsuPackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId); | ||
2179 | previousType = ComplexReferenceChildType.Package; | ||
2180 | break; | ||
2181 | case "ExePackage": | ||
2182 | previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId); | ||
2183 | previousType = ComplexReferenceChildType.Package; | ||
2184 | break; | ||
2185 | case "RollbackBoundary": | ||
2186 | previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId); | ||
2187 | previousType = ComplexReferenceChildType.Package; | ||
2188 | break; | ||
2189 | case "PackageGroupRef": | ||
2190 | previousId = this.ParsePackageGroupRefElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId); | ||
2191 | previousType = ComplexReferenceChildType.PackageGroup; | ||
2192 | break; | ||
2193 | default: | ||
2194 | this.Core.UnexpectedElement(node, child); | ||
2195 | break; | ||
2196 | } | ||
2197 | } | ||
2198 | else | ||
2199 | { | ||
2200 | this.Core.ParseExtensionElement(node, child); | ||
2201 | } | ||
2202 | } | ||
2203 | |||
2204 | |||
2205 | if (!this.Core.EncounteredError) | ||
2206 | { | ||
2207 | this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundlePackageGroup, id); | ||
2208 | } | ||
2209 | } | ||
2210 | |||
2211 | /// <summary> | ||
2212 | /// Parses a package group reference element. | ||
2213 | /// </summary> | ||
2214 | /// <param name="node">Element to parse.</param> | ||
2215 | /// <param name="parentType">ComplexReferenceParentType of parent element (Unknown or PackageGroup).</param> | ||
2216 | /// <param name="parentId">Identifier of parent element.</param> | ||
2217 | /// <returns>Identifier for package group element.</rereturns> | ||
2218 | private string ParsePackageGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId) | ||
2219 | { | ||
2220 | return this.ParsePackageGroupRefElement(node, parentType, parentId, ComplexReferenceChildType.Unknown, null); | ||
2221 | } | ||
2222 | |||
2223 | /// <summary> | ||
2224 | /// Parses a package group reference element. | ||
2225 | /// </summary> | ||
2226 | /// <param name="node">Element to parse.</param> | ||
2227 | /// <param name="parentType">ComplexReferenceParentType of parent element (Unknown or PackageGroup).</param> | ||
2228 | /// <param name="parentId">Identifier of parent element.</param> | ||
2229 | /// <param name="parentType">ComplexReferenceParentType of previous element (Unknown, Package, or PackageGroup).</param> | ||
2230 | /// <param name="parentId">Identifier of parent element.</param> | ||
2231 | /// <returns>Identifier for package group element.</rereturns> | ||
2232 | private string ParsePackageGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId) | ||
2233 | { | ||
2234 | Debug.Assert(ComplexReferenceParentType.Unknown == parentType || ComplexReferenceParentType.PackageGroup == parentType || ComplexReferenceParentType.Container == parentType); | ||
2235 | Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType); | ||
2236 | |||
2237 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
2238 | string id = null; | ||
2239 | string after = null; | ||
2240 | |||
2241 | foreach (var attrib in node.Attributes()) | ||
2242 | { | ||
2243 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
2244 | { | ||
2245 | switch (attrib.Name.LocalName) | ||
2246 | { | ||
2247 | case "Id": | ||
2248 | id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); | ||
2249 | this.Core.CreateSimpleReference(sourceLineNumbers, "WixBundlePackageGroup", id); | ||
2250 | break; | ||
2251 | case "After": | ||
2252 | after = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); | ||
2253 | break; | ||
2254 | default: | ||
2255 | this.Core.UnexpectedAttribute(node, attrib); | ||
2256 | break; | ||
2257 | } | ||
2258 | } | ||
2259 | else | ||
2260 | { | ||
2261 | this.Core.ParseExtensionAttribute(node, attrib); | ||
2262 | |||
2263 | } | ||
2264 | } | ||
2265 | |||
2266 | if (null == id) | ||
2267 | { | ||
2268 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); | ||
2269 | } | ||
2270 | |||
2271 | if (null != after && ComplexReferenceParentType.Container == parentType) | ||
2272 | { | ||
2273 | this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "After", parentId)); | ||
2274 | } | ||
2275 | |||
2276 | this.Core.ParseForExtensionElements(node); | ||
2277 | |||
2278 | if (ComplexReferenceParentType.Container == parentType) | ||
2279 | { | ||
2280 | this.Core.CreateWixGroupRow(sourceLineNumbers, ComplexReferenceParentType.Container, parentId, ComplexReferenceChildType.PackageGroup, id); | ||
2281 | } | ||
2282 | else | ||
2283 | { | ||
2284 | this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.PackageGroup, id, previousType, previousId, after); | ||
2285 | } | ||
2286 | |||
2287 | return id; | ||
2288 | } | ||
2289 | |||
2290 | /// <summary> | ||
2291 | /// Creates rollback boundary. | ||
2292 | /// </summary> | ||
2293 | /// <param name="sourceLineNumbers">Source line numbers.</param> | ||
2294 | /// <param name="id">Identifier for the rollback boundary.</param> | ||
2295 | /// <param name="vital">Indicates whether the rollback boundary is vital or not.</param> | ||
2296 | /// <param name="parentType">Type of parent group.</param> | ||
2297 | /// <param name="parentId">Identifier of parent group.</param> | ||
2298 | /// <param name="previousType">Type of previous item, if any.</param> | ||
2299 | /// <param name="previousId">Identifier of previous item, if any.</param> | ||
2300 | private void CreateRollbackBoundary(SourceLineNumber sourceLineNumbers, Identifier id, YesNoType vital, YesNoType transaction, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId) | ||
2301 | { | ||
2302 | var row = (WixChainItemTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixChainItem, id); | ||
2303 | |||
2304 | var rollbackBoundary = (WixBundleRollbackBoundaryTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleRollbackBoundary, id); | ||
2305 | |||
2306 | if (YesNoType.NotSet != vital) | ||
2307 | { | ||
2308 | rollbackBoundary.Vital = (vital == YesNoType.Yes); | ||
2309 | } | ||
2310 | if (YesNoType.NotSet != transaction) | ||
2311 | { | ||
2312 | rollbackBoundary.Transaction = (transaction == YesNoType.Yes); | ||
2313 | } | ||
2314 | |||
2315 | this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Package, id.Id, previousType, previousId, null); | ||
2316 | } | ||
2317 | |||
2318 | /// <summary> | ||
2319 | /// Creates group and ordering information for packages | ||
2320 | /// </summary> | ||
2321 | /// <param name="sourceLineNumbers">Source line numbers.</param> | ||
2322 | /// <param name="parentType">Type of parent group, if known.</param> | ||
2323 | /// <param name="parentId">Identifier of parent group, if known.</param> | ||
2324 | /// <param name="type">Type of this item.</param> | ||
2325 | /// <param name="id">Identifier for this item.</param> | ||
2326 | /// <param name="previousType">Type of previous item, if known.</param> | ||
2327 | /// <param name="previousId">Identifier of previous item, if known</param> | ||
2328 | /// <param name="afterId">Identifier of explicit 'After' attribute, if given.</param> | ||
2329 | private void CreateChainPackageMetaRows(SourceLineNumber sourceLineNumbers, | ||
2330 | ComplexReferenceParentType parentType, string parentId, | ||
2331 | ComplexReferenceChildType type, string id, | ||
2332 | ComplexReferenceChildType previousType, string previousId, string afterId) | ||
2333 | { | ||
2334 | // If there's an explicit 'After' attribute, it overrides the inferred previous item. | ||
2335 | if (null != afterId) | ||
2336 | { | ||
2337 | previousType = ComplexReferenceChildType.Package; | ||
2338 | previousId = afterId; | ||
2339 | } | ||
2340 | |||
2341 | this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId, type, id, previousType, previousId); | ||
2342 | } | ||
2343 | |||
2344 | // TODO: Should we define our own enum for this, just to ensure there's no "cross-contamination"? | ||
2345 | // TODO: Also, we could potentially include an 'Attributes' field to track things like | ||
2346 | // 'before' vs. 'after', and explicit vs. inferred dependencies. | ||
2347 | private void CreateWixOrderingRow(SourceLineNumber sourceLineNumbers, | ||
2348 | ComplexReferenceChildType itemType, string itemId, | ||
2349 | ComplexReferenceChildType dependsOnType, string dependsOnId) | ||
2350 | { | ||
2351 | if (!this.Core.EncounteredError) | ||
2352 | { | ||
2353 | var row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixOrdering); | ||
2354 | row.Set(0, itemType.ToString()); | ||
2355 | row.Set(1, itemId); | ||
2356 | row.Set(2, dependsOnType.ToString()); | ||
2357 | row.Set(3, dependsOnId); | ||
2358 | } | ||
2359 | } | ||
2360 | |||
2361 | /// <summary> | ||
2362 | /// Parse MsiProperty element | ||
2363 | /// </summary> | ||
2364 | /// <param name="node">Element to parse</param> | ||
2365 | /// <param name="packageId">Id of parent element</param> | ||
2366 | private void ParseMsiPropertyElement(XElement node, string packageId) | ||
2367 | { | ||
2368 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
2369 | string name = null; | ||
2370 | string value = null; | ||
2371 | string condition = null; | ||
2372 | |||
2373 | foreach (var attrib in node.Attributes()) | ||
2374 | { | ||
2375 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
2376 | { | ||
2377 | switch (attrib.Name.LocalName) | ||
2378 | { | ||
2379 | case "Name": | ||
2380 | name = this.Core.GetAttributeMsiPropertyNameValue(sourceLineNumbers, attrib); | ||
2381 | break; | ||
2382 | case "Value": | ||
2383 | value = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
2384 | break; | ||
2385 | case "Condition": | ||
2386 | condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
2387 | break; | ||
2388 | default: | ||
2389 | this.Core.UnexpectedAttribute(node, attrib); | ||
2390 | break; | ||
2391 | } | ||
2392 | } | ||
2393 | else | ||
2394 | { | ||
2395 | this.Core.ParseExtensionAttribute(node, attrib); | ||
2396 | } | ||
2397 | } | ||
2398 | |||
2399 | if (null == name) | ||
2400 | { | ||
2401 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); | ||
2402 | } | ||
2403 | |||
2404 | if (null == value) | ||
2405 | { | ||
2406 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value")); | ||
2407 | } | ||
2408 | |||
2409 | this.Core.ParseForExtensionElements(node); | ||
2410 | |||
2411 | if (!this.Core.EncounteredError) | ||
2412 | { | ||
2413 | var row = (WixBundleMsiPropertyTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleMsiProperty); | ||
2414 | row.WixBundlePackage_ = packageId; | ||
2415 | row.Name = name; | ||
2416 | row.Value = value; | ||
2417 | |||
2418 | if (!String.IsNullOrEmpty(condition)) | ||
2419 | { | ||
2420 | row.Condition = condition; | ||
2421 | } | ||
2422 | } | ||
2423 | } | ||
2424 | |||
2425 | /// <summary> | ||
2426 | /// Parse SlipstreamMsp element | ||
2427 | /// </summary> | ||
2428 | /// <param name="node">Element to parse</param> | ||
2429 | /// <param name="packageId">Id of parent element</param> | ||
2430 | private void ParseSlipstreamMspElement(XElement node, string packageId) | ||
2431 | { | ||
2432 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
2433 | string id = null; | ||
2434 | |||
2435 | foreach (var attrib in node.Attributes()) | ||
2436 | { | ||
2437 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
2438 | { | ||
2439 | switch (attrib.Name.LocalName) | ||
2440 | { | ||
2441 | case "Id": | ||
2442 | id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); | ||
2443 | this.Core.CreateSimpleReference(sourceLineNumbers, "WixBundlePackage", id); | ||
2444 | break; | ||
2445 | default: | ||
2446 | this.Core.UnexpectedAttribute(node, attrib); | ||
2447 | break; | ||
2448 | } | ||
2449 | } | ||
2450 | else | ||
2451 | { | ||
2452 | this.Core.ParseExtensionAttribute(node, attrib); | ||
2453 | } | ||
2454 | } | ||
2455 | |||
2456 | if (null == id) | ||
2457 | { | ||
2458 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); | ||
2459 | } | ||
2460 | |||
2461 | this.Core.ParseForExtensionElements(node); | ||
2462 | |||
2463 | if (!this.Core.EncounteredError) | ||
2464 | { | ||
2465 | var row = (WixBundleSlipstreamMspTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleSlipstreamMsp); | ||
2466 | row.WixBundlePackage_ = packageId; | ||
2467 | row.WixBundlePackage_Msp = id; | ||
2468 | } | ||
2469 | } | ||
2470 | |||
2471 | /// <summary> | ||
2472 | /// Parse RelatedBundle element | ||
2473 | /// </summary> | ||
2474 | /// <param name="node">Element to parse</param> | ||
2475 | private void ParseRelatedBundleElement(XElement node) | ||
2476 | { | ||
2477 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
2478 | string id = null; | ||
2479 | var actionType = RelatedBundleActionType.Detect; | ||
2480 | |||
2481 | foreach (var attrib in node.Attributes()) | ||
2482 | { | ||
2483 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
2484 | { | ||
2485 | switch (attrib.Name.LocalName) | ||
2486 | { | ||
2487 | case "Id": | ||
2488 | id = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); | ||
2489 | break; | ||
2490 | case "Action": | ||
2491 | var action = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
2492 | switch (action) | ||
2493 | { | ||
2494 | case "Detect": | ||
2495 | case "detect": | ||
2496 | actionType = RelatedBundleActionType.Detect; | ||
2497 | break; | ||
2498 | case "Upgrade": | ||
2499 | case "upgrade": | ||
2500 | actionType = RelatedBundleActionType.Upgrade; | ||
2501 | break; | ||
2502 | case "Addon": | ||
2503 | case "addon": | ||
2504 | actionType = RelatedBundleActionType.Addon; | ||
2505 | break; | ||
2506 | case "Patch": | ||
2507 | case "patch": | ||
2508 | actionType = RelatedBundleActionType.Patch; | ||
2509 | break; | ||
2510 | case "": | ||
2511 | break; | ||
2512 | default: | ||
2513 | this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Action", action, "Detect", "Upgrade", "Addon", "Patch")); | ||
2514 | break; | ||
2515 | } | ||
2516 | break; | ||
2517 | default: | ||
2518 | this.Core.UnexpectedAttribute(node, attrib); | ||
2519 | break; | ||
2520 | } | ||
2521 | } | ||
2522 | else | ||
2523 | { | ||
2524 | this.Core.ParseExtensionAttribute(node, attrib); | ||
2525 | } | ||
2526 | } | ||
2527 | |||
2528 | if (null == id) | ||
2529 | { | ||
2530 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); | ||
2531 | } | ||
2532 | |||
2533 | this.Core.ParseForExtensionElements(node); | ||
2534 | |||
2535 | if (!this.Core.EncounteredError) | ||
2536 | { | ||
2537 | var tuple = new WixRelatedBundleTuple(sourceLineNumbers) | ||
2538 | { | ||
2539 | BundleId = id, | ||
2540 | Action = actionType, | ||
2541 | }; | ||
2542 | |||
2543 | this.Core.AddTuple(tuple); | ||
2544 | //var row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixRelatedBundle); | ||
2545 | //row.Set(0, id); | ||
2546 | //row.Set(1, (int)actionType); | ||
2547 | } | ||
2548 | } | ||
2549 | |||
2550 | /// <summary> | ||
2551 | /// Parse Update element | ||
2552 | /// </summary> | ||
2553 | /// <param name="node">Element to parse</param> | ||
2554 | private void ParseUpdateElement(XElement node) | ||
2555 | { | ||
2556 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
2557 | string location = null; | ||
2558 | |||
2559 | foreach (var attrib in node.Attributes()) | ||
2560 | { | ||
2561 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
2562 | { | ||
2563 | switch (attrib.Name.LocalName) | ||
2564 | { | ||
2565 | case "Location": | ||
2566 | location = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
2567 | break; | ||
2568 | default: | ||
2569 | this.Core.UnexpectedAttribute(node, attrib); | ||
2570 | break; | ||
2571 | } | ||
2572 | } | ||
2573 | else | ||
2574 | { | ||
2575 | this.Core.ParseExtensionAttribute(node, attrib); | ||
2576 | } | ||
2577 | } | ||
2578 | |||
2579 | if (null == location) | ||
2580 | { | ||
2581 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Location")); | ||
2582 | } | ||
2583 | |||
2584 | this.Core.ParseForExtensionElements(node); | ||
2585 | |||
2586 | if (!this.Core.EncounteredError) | ||
2587 | { | ||
2588 | var row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleUpdate); | ||
2589 | row.Set(0, location); | ||
2590 | } | ||
2591 | } | ||
2592 | |||
2593 | /// <summary> | ||
2594 | /// Parse Variable element | ||
2595 | /// </summary> | ||
2596 | /// <param name="node">Element to parse</param> | ||
2597 | private void ParseVariableElement(XElement node) | ||
2598 | { | ||
2599 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | ||
2600 | var hidden = false; | ||
2601 | string name = null; | ||
2602 | var persisted = false; | ||
2603 | string value = null; | ||
2604 | string type = null; | ||
2605 | |||
2606 | foreach (var attrib in node.Attributes()) | ||
2607 | { | ||
2608 | if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) | ||
2609 | { | ||
2610 | switch (attrib.Name.LocalName) | ||
2611 | { | ||
2612 | case "Hidden": | ||
2613 | if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) | ||
2614 | { | ||
2615 | hidden = true; | ||
2616 | } | ||
2617 | break; | ||
2618 | case "Name": | ||
2619 | name = this.Core.GetAttributeBundleVariableValue(sourceLineNumbers, attrib); | ||
2620 | break; | ||
2621 | case "Persisted": | ||
2622 | if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) | ||
2623 | { | ||
2624 | persisted = true; | ||
2625 | } | ||
2626 | break; | ||
2627 | case "Value": | ||
2628 | value = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); | ||
2629 | break; | ||
2630 | case "Type": | ||
2631 | type = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
2632 | break; | ||
2633 | default: | ||
2634 | this.Core.UnexpectedAttribute(node, attrib); | ||
2635 | break; | ||
2636 | } | ||
2637 | } | ||
2638 | else | ||
2639 | { | ||
2640 | this.Core.ParseExtensionAttribute(node, attrib); | ||
2641 | } | ||
2642 | } | ||
2643 | |||
2644 | if (null == name) | ||
2645 | { | ||
2646 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); | ||
2647 | } | ||
2648 | else if (name.StartsWith("Wix", StringComparison.OrdinalIgnoreCase)) | ||
2649 | { | ||
2650 | this.Core.Write(ErrorMessages.ReservedNamespaceViolation(sourceLineNumbers, node.Name.LocalName, "Name", "Wix")); | ||
2651 | } | ||
2652 | |||
2653 | if (null == type && null != value) | ||
2654 | { | ||
2655 | // Infer the type from the current value... | ||
2656 | if (value.StartsWith("v", StringComparison.OrdinalIgnoreCase)) | ||
2657 | { | ||
2658 | // Version constructor does not support simple "v#" syntax so check to see if the value is | ||
2659 | // non-negative real quick. | ||
2660 | if (Int32.TryParse(value.Substring(1), NumberStyles.None, CultureInfo.InvariantCulture.NumberFormat, out var number)) | ||
2661 | { | ||
2662 | type = "version"; | ||
2663 | } | ||
2664 | else | ||
2665 | { | ||
2666 | // Sadly, Version doesn't have a TryParse() method until .NET 4, so we have to try/catch to see if it parses. | ||
2667 | try | ||
2668 | { | ||
2669 | var version = new Version(value.Substring(1)); | ||
2670 | type = "version"; | ||
2671 | } | ||
2672 | catch (Exception) | ||
2673 | { | ||
2674 | } | ||
2675 | } | ||
2676 | } | ||
2677 | |||
2678 | // Not a version, check for numeric. | ||
2679 | if (null == type) | ||
2680 | { | ||
2681 | if (Int64.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture.NumberFormat, out var number)) | ||
2682 | { | ||
2683 | type = "numeric"; | ||
2684 | } | ||
2685 | else | ||
2686 | { | ||
2687 | type = "string"; | ||
2688 | } | ||
2689 | } | ||
2690 | } | ||
2691 | |||
2692 | if (null == value && null != type) | ||
2693 | { | ||
2694 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, "Variable", "Value", "Type")); | ||
2695 | } | ||
2696 | |||
2697 | this.Core.ParseForExtensionElements(node); | ||
2698 | |||
2699 | if (!this.Core.EncounteredError) | ||
2700 | { | ||
2701 | var row = (WixBundleVariableTuple)this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixBundleVariable); | ||
2702 | row.WixBundleVariable = name; | ||
2703 | row.Value = value; | ||
2704 | row.Type = type; | ||
2705 | row.Hidden = hidden; | ||
2706 | row.Persisted = persisted; | ||
2707 | } | ||
2708 | } | ||
2709 | |||
2710 | private class RemotePayload | ||
2711 | { | ||
2712 | public string CertificatePublicKey { get; set; } | ||
2713 | |||
2714 | public string CertificateThumbprint { get; set; } | ||
2715 | |||
2716 | public string Description { get; set; } | ||
2717 | |||
2718 | public string Hash { get; set; } | ||
2719 | |||
2720 | public string ProductName { get; set; } | ||
2721 | |||
2722 | public int Size { get; set; } | ||
2723 | |||
2724 | public string Version { get; set; } | ||
2725 | } | ||
2726 | } | ||
2727 | } | ||