aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2021-02-28 21:04:16 -0600
committerSean Hall <r.sean.hall@gmail.com>2021-03-02 15:50:47 -0600
commita6013a643208a8d1fc2d1136ef8d3a6c3e909522 (patch)
tree4f3007b58541d3d944cea469f630d3656ba5ef89
parentb54ac261d0a03b75cf05ef370351445774b82155 (diff)
downloadwix-a6013a643208a8d1fc2d1136ef8d3a6c3e909522.tar.gz
wix-a6013a643208a8d1fc2d1136ef8d3a6c3e909522.tar.bz2
wix-a6013a643208a8d1fc2d1136ef8d3a6c3e909522.zip
Refactor payload compiling and harvesting.
-rw-r--r--src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs8
-rw-r--r--src/WixToolset.Core.Burn/Bundles/ProcessPayloadsCommand.cs49
-rw-r--r--src/WixToolset.Core.Burn/ExtensibilityServices/PayloadHarvester.cs68
-rw-r--r--src/WixToolset.Core.Burn/Interfaces/IPayloadHarvester.cs23
-rw-r--r--src/WixToolset.Core.Burn/WixToolsetCoreServiceProviderExtensions.cs2
-rw-r--r--src/WixToolset.Core/Compile/CompilerPayload.cs247
-rw-r--r--src/WixToolset.Core/CompilerCore.cs40
-rw-r--r--src/WixToolset.Core/Compiler_Bundle.cs452
8 files changed, 546 insertions, 343 deletions
diff --git a/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs b/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
index c9a111c6..724dd7ff 100644
--- a/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+++ b/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
@@ -11,6 +11,7 @@ namespace WixToolset.Core.Burn
11 using WixToolset.Core.Bind; 11 using WixToolset.Core.Bind;
12 using WixToolset.Core.Burn.Bind; 12 using WixToolset.Core.Burn.Bind;
13 using WixToolset.Core.Burn.Bundles; 13 using WixToolset.Core.Burn.Bundles;
14 using WixToolset.Core.Burn.Interfaces;
14 using WixToolset.Data; 15 using WixToolset.Data;
15 using WixToolset.Data.Burn; 16 using WixToolset.Data.Burn;
16 using WixToolset.Data.Symbols; 17 using WixToolset.Data.Symbols;
@@ -31,6 +32,7 @@ namespace WixToolset.Core.Burn
31 32
32 this.BackendHelper = context.ServiceProvider.GetService<IBackendHelper>(); 33 this.BackendHelper = context.ServiceProvider.GetService<IBackendHelper>();
33 this.InternalBurnBackendHelper = context.ServiceProvider.GetService<IInternalBurnBackendHelper>(); 34 this.InternalBurnBackendHelper = context.ServiceProvider.GetService<IInternalBurnBackendHelper>();
35 this.PayloadHarvester = context.ServiceProvider.GetService<IPayloadHarvester>();
34 36
35 this.DefaultCompressionLevel = context.DefaultCompressionLevel; 37 this.DefaultCompressionLevel = context.DefaultCompressionLevel;
36 this.DelayedFields = context.DelayedFields; 38 this.DelayedFields = context.DelayedFields;
@@ -52,6 +54,8 @@ namespace WixToolset.Core.Burn
52 54
53 private IInternalBurnBackendHelper InternalBurnBackendHelper { get; } 55 private IInternalBurnBackendHelper InternalBurnBackendHelper { get; }
54 56
57 private IPayloadHarvester PayloadHarvester { get; }
58
55 private CompressionLevel? DefaultCompressionLevel { get; } 59 private CompressionLevel? DefaultCompressionLevel { get; }
56 60
57 public IEnumerable<IDelayedField> DelayedFields { get; } 61 public IEnumerable<IDelayedField> DelayedFields { get; }
@@ -165,7 +169,7 @@ namespace WixToolset.Core.Burn
165 // Process the explicitly authored payloads. 169 // Process the explicitly authored payloads.
166 ISet<string> processedPayloads; 170 ISet<string> processedPayloads;
167 { 171 {
168 var command = new ProcessPayloadsCommand(this.ServiceProvider, this.BackendHelper, payloadSymbols.Values, bundleSymbol.DefaultPackagingType, layoutDirectory); 172 var command = new ProcessPayloadsCommand(this.ServiceProvider, this.BackendHelper, this.PayloadHarvester, payloadSymbols.Values, bundleSymbol.DefaultPackagingType, layoutDirectory);
169 command.Execute(); 173 command.Execute();
170 174
171 fileTransfers.AddRange(command.FileTransfers); 175 fileTransfers.AddRange(command.FileTransfers);
@@ -247,7 +251,7 @@ namespace WixToolset.Core.Burn
247 { 251 {
248 var toProcess = payloadSymbols.Values.Where(r => !processedPayloads.Contains(r.Id.Id)).ToList(); 252 var toProcess = payloadSymbols.Values.Where(r => !processedPayloads.Contains(r.Id.Id)).ToList();
249 253
250 var command = new ProcessPayloadsCommand(this.ServiceProvider, this.BackendHelper, toProcess, bundleSymbol.DefaultPackagingType, layoutDirectory); 254 var command = new ProcessPayloadsCommand(this.ServiceProvider, this.BackendHelper, this.PayloadHarvester, toProcess, bundleSymbol.DefaultPackagingType, layoutDirectory);
251 command.Execute(); 255 command.Execute();
252 256
253 fileTransfers.AddRange(command.FileTransfers); 257 fileTransfers.AddRange(command.FileTransfers);
diff --git a/src/WixToolset.Core.Burn/Bundles/ProcessPayloadsCommand.cs b/src/WixToolset.Core.Burn/Bundles/ProcessPayloadsCommand.cs
index db5b03fb..dea5b336 100644
--- a/src/WixToolset.Core.Burn/Bundles/ProcessPayloadsCommand.cs
+++ b/src/WixToolset.Core.Burn/Bundles/ProcessPayloadsCommand.cs
@@ -6,6 +6,7 @@ namespace WixToolset.Core.Burn.Bundles
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Diagnostics; 7 using System.Diagnostics;
8 using System.IO; 8 using System.IO;
9 using WixToolset.Core.Burn.Interfaces;
9 using WixToolset.Data; 10 using WixToolset.Data;
10 using WixToolset.Data.Burn; 11 using WixToolset.Data.Burn;
11 using WixToolset.Data.Symbols; 12 using WixToolset.Data.Symbols;
@@ -14,13 +15,12 @@ namespace WixToolset.Core.Burn.Bundles
14 15
15 internal class ProcessPayloadsCommand 16 internal class ProcessPayloadsCommand
16 { 17 {
17 private static readonly Version EmptyVersion = new Version(0, 0, 0, 0); 18 public ProcessPayloadsCommand(IWixToolsetServiceProvider serviceProvider, IBackendHelper backendHelper, IPayloadHarvester payloadHarvester, IEnumerable<WixBundlePayloadSymbol> payloads, PackagingType defaultPackaging, string layoutDirectory)
18
19 public ProcessPayloadsCommand(IWixToolsetServiceProvider serviceProvider, IBackendHelper backendHelper, IEnumerable<WixBundlePayloadSymbol> payloads, PackagingType defaultPackaging, string layoutDirectory)
20 { 19 {
21 this.Messaging = serviceProvider.GetService<IMessaging>(); 20 this.Messaging = serviceProvider.GetService<IMessaging>();
22 21
23 this.BackendHelper = backendHelper; 22 this.BackendHelper = backendHelper;
23 this.PayloadHarvester = payloadHarvester;
24 this.Payloads = payloads; 24 this.Payloads = payloads;
25 this.DefaultPackaging = defaultPackaging; 25 this.DefaultPackaging = defaultPackaging;
26 this.LayoutDirectory = layoutDirectory; 26 this.LayoutDirectory = layoutDirectory;
@@ -34,6 +34,8 @@ namespace WixToolset.Core.Burn.Bundles
34 34
35 private IBackendHelper BackendHelper { get; } 35 private IBackendHelper BackendHelper { get; }
36 36
37 private IPayloadHarvester PayloadHarvester { get; }
38
37 private IEnumerable<WixBundlePayloadSymbol> Payloads { get; } 39 private IEnumerable<WixBundlePayloadSymbol> Payloads { get; }
38 40
39 private PackagingType DefaultPackaging { get; } 41 private PackagingType DefaultPackaging { get; }
@@ -56,17 +58,13 @@ namespace WixToolset.Core.Burn.Bundles
56 58
57 this.UpdatePayloadPackagingType(payload); 59 this.UpdatePayloadPackagingType(payload);
58 60
59 if (String.IsNullOrEmpty(sourceFile?.Path)) 61 if (!this.PayloadHarvester.HarvestStandardInformation(payload))
60 { 62 {
61 // Remote payloads obviously cannot be embedded. 63 // Remote payloads obviously cannot be embedded.
62 Debug.Assert(PackagingType.Embedded != payload.Packaging); 64 Debug.Assert(PackagingType.Embedded != payload.Packaging);
63 } 65 }
64 else // not a remote payload so we have a lot more to update. 66 else // not a remote payload so we have a lot more to update.
65 { 67 {
66 this.UpdatePayloadFileInformation(payload, sourceFile);
67
68 this.UpdatePayloadVersionInformation(payload, sourceFile);
69
70 // External payloads need to be transfered. 68 // External payloads need to be transfered.
71 if (PackagingType.External == payload.Packaging) 69 if (PackagingType.External == payload.Packaging)
72 { 70 {
@@ -110,40 +108,5 @@ namespace WixToolset.Core.Burn.Bundles
110 payload.ContainerRef = BurnConstants.BurnDefaultAttachedContainerName; 108 payload.ContainerRef = BurnConstants.BurnDefaultAttachedContainerName;
111 } 109 }
112 } 110 }
113
114 private void UpdatePayloadFileInformation(WixBundlePayloadSymbol payload, IntermediateFieldPathValue sourceFile)
115 {
116 var fileInfo = new FileInfo(sourceFile.Path);
117
118 if (null != fileInfo)
119 {
120 payload.FileSize = fileInfo.Length;
121
122 payload.Hash = BundleHashAlgorithm.Hash(fileInfo);
123 }
124 else
125 {
126 payload.FileSize = 0;
127 }
128 }
129
130 private void UpdatePayloadVersionInformation(WixBundlePayloadSymbol payload, IntermediateFieldPathValue sourceFile)
131 {
132 var versionInfo = FileVersionInfo.GetVersionInfo(sourceFile.Path);
133
134 if (null != versionInfo)
135 {
136 // Use the fixed version info block for the file since the resource text may not be a dotted quad.
137 var version = new Version(versionInfo.ProductMajorPart, versionInfo.ProductMinorPart, versionInfo.ProductBuildPart, versionInfo.ProductPrivatePart);
138
139 if (ProcessPayloadsCommand.EmptyVersion != version)
140 {
141 payload.Version = version.ToString();
142 }
143
144 payload.Description = versionInfo.FileDescription;
145 payload.DisplayName = versionInfo.ProductName;
146 }
147 }
148 } 111 }
149} 112}
diff --git a/src/WixToolset.Core.Burn/ExtensibilityServices/PayloadHarvester.cs b/src/WixToolset.Core.Burn/ExtensibilityServices/PayloadHarvester.cs
new file mode 100644
index 00000000..9ef91028
--- /dev/null
+++ b/src/WixToolset.Core.Burn/ExtensibilityServices/PayloadHarvester.cs
@@ -0,0 +1,68 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Core.Burn.ExtensibilityServices
4{
5 using System;
6 using System.Diagnostics;
7 using System.IO;
8 using WixToolset.Core.Burn.Bundles;
9 using WixToolset.Core.Burn.Interfaces;
10 using WixToolset.Data.Symbols;
11
12 internal class PayloadHarvester : IPayloadHarvester
13 {
14 private static readonly Version EmptyVersion = new Version(0, 0, 0, 0);
15
16 /// <inheritdoc />
17 public bool HarvestStandardInformation(WixBundlePayloadSymbol payload)
18 {
19 var filePath = payload.SourceFile?.Path;
20
21 if (String.IsNullOrEmpty(filePath))
22 {
23 return false;
24 }
25
26 this.UpdatePayloadFileInformation(payload, filePath);
27
28 this.UpdatePayloadVersionInformation(payload, filePath);
29
30 return true;
31 }
32
33 private void UpdatePayloadFileInformation(WixBundlePayloadSymbol payload, string filePath)
34 {
35 var fileInfo = new FileInfo(filePath);
36
37 if (null != fileInfo)
38 {
39 payload.FileSize = fileInfo.Length;
40
41 payload.Hash = BundleHashAlgorithm.Hash(fileInfo);
42 }
43 else
44 {
45 payload.FileSize = 0;
46 }
47 }
48
49 private void UpdatePayloadVersionInformation(WixBundlePayloadSymbol payload, string filePath)
50 {
51 var versionInfo = FileVersionInfo.GetVersionInfo(filePath);
52
53 if (null != versionInfo)
54 {
55 // Use the fixed version info block for the file since the resource text may not be a dotted quad.
56 var version = new Version(versionInfo.ProductMajorPart, versionInfo.ProductMinorPart, versionInfo.ProductBuildPart, versionInfo.ProductPrivatePart);
57
58 if (PayloadHarvester.EmptyVersion != version)
59 {
60 payload.Version = version.ToString();
61 }
62
63 payload.Description = versionInfo.FileDescription;
64 payload.DisplayName = versionInfo.ProductName;
65 }
66 }
67 }
68}
diff --git a/src/WixToolset.Core.Burn/Interfaces/IPayloadHarvester.cs b/src/WixToolset.Core.Burn/Interfaces/IPayloadHarvester.cs
new file mode 100644
index 00000000..1bafa46e
--- /dev/null
+++ b/src/WixToolset.Core.Burn/Interfaces/IPayloadHarvester.cs
@@ -0,0 +1,23 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Core.Burn.Interfaces
4{
5 using System.Diagnostics;
6 using WixToolset.Data.Symbols;
7
8 /// <summary>
9 /// Service for harvesting payload information.
10 /// </summary>
11 public interface IPayloadHarvester
12 {
13 /// <summary>
14 /// Uses <see cref="WixBundlePayloadSymbol.SourceFile"/> to:
15 /// update <see cref="WixBundlePayloadSymbol.Hash"/> from file contents,
16 /// update <see cref="WixBundlePayloadSymbol.FileSize"/> from file size, and
17 /// update <see cref="WixBundlePayloadSymbol.Description"/>, <see cref="WixBundlePayloadSymbol.DisplayName"/>, and <see cref="WixBundlePayloadSymbol.Version"/> from <see cref="FileVersionInfo"/>.
18 /// </summary>
19 /// <param name="payload">The symbol to update.</param>
20 /// <returns>Whether the symbol had a source file specified.</returns>
21 bool HarvestStandardInformation(WixBundlePayloadSymbol payload);
22 }
23}
diff --git a/src/WixToolset.Core.Burn/WixToolsetCoreServiceProviderExtensions.cs b/src/WixToolset.Core.Burn/WixToolsetCoreServiceProviderExtensions.cs
index b0401b4a..58076d5e 100644
--- a/src/WixToolset.Core.Burn/WixToolsetCoreServiceProviderExtensions.cs
+++ b/src/WixToolset.Core.Burn/WixToolsetCoreServiceProviderExtensions.cs
@@ -5,6 +5,7 @@ namespace WixToolset.Core.Burn
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using WixToolset.Core.Burn.ExtensibilityServices; 7 using WixToolset.Core.Burn.ExtensibilityServices;
8 using WixToolset.Core.Burn.Interfaces;
8 using WixToolset.Extensibility.Services; 9 using WixToolset.Extensibility.Services;
9 10
10 /// <summary> 11 /// <summary>
@@ -31,6 +32,7 @@ namespace WixToolset.Core.Burn
31 { 32 {
32 // Singletons. 33 // Singletons.
33 coreProvider.AddService((provider, singletons) => AddSingleton<IInternalBurnBackendHelper>(singletons, new BurnBackendHelper(provider))); 34 coreProvider.AddService((provider, singletons) => AddSingleton<IInternalBurnBackendHelper>(singletons, new BurnBackendHelper(provider)));
35 coreProvider.AddService((provider, singletons) => AddSingleton<IPayloadHarvester>(singletons, new PayloadHarvester()));
34 coreProvider.AddService((provider, singletons) => AddSingleton<IBurnBackendHelper>(singletons, provider.GetService<IInternalBurnBackendHelper>())); 36 coreProvider.AddService((provider, singletons) => AddSingleton<IBurnBackendHelper>(singletons, provider.GetService<IInternalBurnBackendHelper>()));
35 } 37 }
36 38
diff --git a/src/WixToolset.Core/Compile/CompilerPayload.cs b/src/WixToolset.Core/Compile/CompilerPayload.cs
new file mode 100644
index 00000000..4eda56f8
--- /dev/null
+++ b/src/WixToolset.Core/Compile/CompilerPayload.cs
@@ -0,0 +1,247 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Core
4{
5 using System;
6 using System.IO;
7 using System.Xml.Linq;
8 using WixToolset.Data;
9 using WixToolset.Data.Burn;
10 using WixToolset.Data.Symbols;
11
12 internal class CompilerPayload
13 {
14 public YesNoDefaultType Compressed { get; set; } = YesNoDefaultType.Default;
15
16 public string Description { get; set; }
17
18 public string DisplayName { get; set; }
19
20 public string DownloadUrl { get; set; }
21
22 public string Hash { get; set; }
23
24 public Identifier Id { get; set; }
25
26 public bool IsRequired { get; set; } = true;
27
28 public string Name { get; set; }
29
30 public string ProductName { get; set; }
31
32 public long? Size { get; set; }
33
34 public string SourceFile { get; set; }
35
36 public string Version { get; set; }
37
38 public CompilerPayload(CompilerCore core, SourceLineNumber sourceLineNumbers, XElement element)
39 {
40 this.Core = core;
41 this.Element = element;
42 this.SourceLineNumbers = sourceLineNumbers;
43 }
44
45 private CompilerCore Core { get; }
46
47 private XElement Element { get; }
48
49 private SourceLineNumber SourceLineNumbers { get; }
50
51 private void CalculateAndVerifyFields(CompilerPayload remotePayload = null)
52 {
53 if (String.IsNullOrEmpty(this.SourceFile))
54 {
55 if (String.IsNullOrEmpty(this.Name))
56 {
57 if (this.IsRequired)
58 {
59 this.Core.Write(ErrorMessages.ExpectedAttributesWithOtherAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Name", "SourceFile"));
60 }
61 }
62 else if (remotePayload == null)
63 {
64 this.SourceFile = Path.Combine("SourceDir", this.Name);
65 }
66 }
67 else if (remotePayload != null)
68 {
69 this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "RemotePayload", "SourceFile"));
70 }
71 else if (this.SourceFile.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
72 {
73 if (String.IsNullOrEmpty(this.Name))
74 {
75 this.Core.Write(ErrorMessages.ExpectedAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Name", "SourceFile", this.SourceFile));
76 }
77 else
78 {
79 this.SourceFile = Path.Combine(this.SourceFile, Path.GetFileName(this.Name));
80 }
81 }
82
83 if (remotePayload != null)
84 {
85 if (this.DownloadUrl == null)
86 {
87 this.Core.Write(ErrorMessages.ExpectedAttributeWithElement(this.SourceLineNumbers, this.Element.Name.LocalName, "DownloadUrl", "RemotePayload"));
88 }
89
90 if (YesNoDefaultType.No != this.Compressed)
91 {
92 this.Compressed = YesNoDefaultType.No;
93 this.Core.Write(WarningMessages.RemotePayloadsMustNotAlsoBeCompressed(this.SourceLineNumbers, this.Element.Name.LocalName));
94 }
95
96 this.Description = remotePayload.Description;
97 this.DisplayName = remotePayload.DisplayName;
98 this.Hash = remotePayload.Hash;
99 this.Size = remotePayload.Size;
100 this.Version = remotePayload.Version;
101 }
102 }
103
104 public WixBundlePayloadSymbol CreatePayloadSymbol(ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType = ComplexReferenceChildType.Unknown, string previousId = null)
105 {
106 WixBundlePayloadSymbol symbol = null;
107
108 if (parentType == ComplexReferenceParentType.Container && parentId == BurnConstants.BurnUXContainerName)
109 {
110 if (this.Compressed == YesNoDefaultType.No)
111 {
112 this.Core.Write(WarningMessages.UxPayloadsOnlySupportEmbedding(this.SourceLineNumbers, this.SourceFile));
113 }
114
115 if (!String.IsNullOrEmpty(this.DownloadUrl))
116 {
117 this.Core.Write(WarningMessages.DownloadUrlNotSupportedForEmbeddedPayloads(this.SourceLineNumbers, this.Id.Id));
118 }
119
120 this.Compressed = YesNoDefaultType.Yes;
121 this.DownloadUrl = null;
122 }
123
124 if (!this.Core.EncounteredError)
125 {
126 symbol = this.Core.AddSymbol(new WixBundlePayloadSymbol(this.SourceLineNumbers, this.Id)
127 {
128 Name = String.IsNullOrEmpty(this.Name) ? Path.GetFileName(this.SourceFile) : this.Name,
129 SourceFile = new IntermediateFieldPathValue { Path = this.SourceFile },
130 DownloadUrl = this.DownloadUrl,
131 Compressed = (this.Compressed == YesNoDefaultType.Yes) ? true : (this.Compressed == YesNoDefaultType.No) ? (bool?)false : null,
132 UnresolvedSourceFile = this.SourceFile, // duplicate of sourceFile but in a string column so it won't get resolved to a full path during binding.
133 DisplayName = this.DisplayName ?? this.ProductName,
134 Description = this.Description,
135 Hash = this.Hash,
136 FileSize = this.Size,
137 Version = this.Version,
138 });
139
140 this.Core.CreateGroupAndOrderingRows(this.SourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Payload, symbol.Id.Id, previousType, previousId);
141 }
142
143 return symbol;
144 }
145
146 public void FinishCompilingPackage(CompilerPayload remotePayload)
147 {
148 this.CalculateAndVerifyFields(remotePayload);
149 this.GenerateIdFromFilename();
150
151 if (this.Id == null)
152 {
153 this.Core.Write(ErrorMessages.ExpectedAttribute(this.SourceLineNumbers, this.Element.Name.LocalName, "Id"));
154 this.Id = Identifier.Invalid;
155 }
156 }
157
158 public void FinishCompilingPayload()
159 {
160 this.CalculateAndVerifyFields();
161 this.GenerateIdFromPrefix("pay");
162 }
163
164 private void GenerateIdFromFilename()
165 {
166 if (this.Id == null)
167 {
168 if (!String.IsNullOrEmpty(this.Name))
169 {
170 this.Id = this.Core.CreateIdentifierFromFilename(Path.GetFileName(this.Name));
171 }
172 else if (!String.IsNullOrEmpty(this.SourceFile))
173 {
174 this.Id = this.Core.CreateIdentifierFromFilename(Path.GetFileName(this.SourceFile));
175 }
176 }
177 }
178
179 private void GenerateIdFromPrefix(string prefix)
180 {
181 if (this.Id == null)
182 {
183 this.Id = this.Core.CreateIdentifier(prefix, this.SourceFile?.ToUpperInvariant() ?? String.Empty);
184 }
185 }
186
187 public void ParseCompressed(XAttribute attrib)
188 {
189 this.Compressed = this.Core.GetAttributeYesNoDefaultValue(this.SourceLineNumbers, attrib);
190 }
191
192 public void ParseDescription(XAttribute attrib)
193 {
194 this.Description = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib);
195 }
196
197 public void ParseDisplayName(XAttribute attrib)
198 {
199 this.DisplayName = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib);
200 }
201
202 public void ParseDownloadUrl(XAttribute attrib)
203 {
204 this.DownloadUrl = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib);
205 }
206
207 public void ParseHash(XAttribute attrib)
208 {
209 this.Hash = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib);
210 }
211
212 public void ParseId(XAttribute attrib)
213 {
214 this.Id = this.Core.GetAttributeIdentifier(this.SourceLineNumbers, attrib);
215 }
216
217 public void ParseName(XAttribute attrib)
218 {
219 this.Name = this.Core.GetAttributeLongFilename(this.SourceLineNumbers, attrib, false, true);
220 if (!this.Core.IsValidLongFilename(this.Name, false, true))
221 {
222 this.Core.Write(ErrorMessages.IllegalLongFilename(this.SourceLineNumbers, this.Element.Name.LocalName, "Name", this.Name));
223 }
224 }
225
226 public void ParseProductName(XAttribute attrib)
227 {
228 this.ProductName = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib);
229 }
230
231 public void ParseSize(XAttribute attrib)
232 {
233 this.Size = this.Core.GetAttributeLongValue(this.SourceLineNumbers, attrib, 1, Int64.MaxValue);
234 }
235
236 public void ParseSourceFile(XAttribute attrib)
237 {
238 this.SourceFile = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib);
239 }
240
241 public void ParseVersion(XAttribute attrib)
242 {
243 this.Version = this.Core.GetAttributeValue(this.SourceLineNumbers, attrib);
244 }
245
246 }
247}
diff --git a/src/WixToolset.Core/CompilerCore.cs b/src/WixToolset.Core/CompilerCore.cs
index 53e0f3fc..cfe3082e 100644
--- a/src/WixToolset.Core/CompilerCore.cs
+++ b/src/WixToolset.Core/CompilerCore.cs
@@ -341,6 +341,46 @@ namespace WixToolset.Core
341 } 341 }
342 342
343 /// <summary> 343 /// <summary>
344 /// Creates group and ordering information.
345 /// </summary>
346 /// <param name="sourceLineNumbers">Source line numbers.</param>
347 /// <param name="parentType">Type of parent group, if known.</param>
348 /// <param name="parentId">Identifier of parent group, if known.</param>
349 /// <param name="type">Type of this item.</param>
350 /// <param name="id">Identifier for this item.</param>
351 /// <param name="previousType">Type of previous item, if known.</param>
352 /// <param name="previousId">Identifier of previous item, if known</param>
353 public void CreateGroupAndOrderingRows(SourceLineNumber sourceLineNumbers,
354 ComplexReferenceParentType parentType, string parentId,
355 ComplexReferenceChildType type, string id,
356 ComplexReferenceChildType previousType, string previousId)
357 {
358 if (this.EncounteredError)
359 {
360 return;
361 }
362
363 if (parentType != ComplexReferenceParentType.Unknown && parentId != null)
364 {
365 this.CreateWixGroupRow(sourceLineNumbers, parentType, parentId, type, id);
366 }
367
368 if (previousType != ComplexReferenceChildType.Unknown && previousId != null)
369 {
370 // TODO: Should we define our own enum for this, just to ensure there's no "cross-contamination"?
371 // TODO: Also, we could potentially include an 'Attributes' field to track things like
372 // 'before' vs. 'after', and explicit vs. inferred dependencies.
373 this.AddSymbol(new WixOrderingSymbol(sourceLineNumbers)
374 {
375 ItemType = type,
376 ItemIdRef = id,
377 DependsOnType = previousType,
378 DependsOnIdRef = previousId,
379 });
380 }
381 }
382
383 /// <summary>
344 /// Creates a version 3 name-based UUID. 384 /// Creates a version 3 name-based UUID.
345 /// </summary> 385 /// </summary>
346 /// <param name="namespaceGuid">The namespace UUID.</param> 386 /// <param name="namespaceGuid">The namespace UUID.</param>
diff --git a/src/WixToolset.Core/Compiler_Bundle.cs b/src/WixToolset.Core/Compiler_Bundle.cs
index 189ac9b5..64fe2acc 100644
--- a/src/WixToolset.Core/Compiler_Bundle.cs
+++ b/src/WixToolset.Core/Compiler_Bundle.cs
@@ -651,7 +651,7 @@ namespace WixToolset.Core
651 switch (child.Name.LocalName) 651 switch (child.Name.LocalName)
652 { 652 {
653 case "BootstrapperApplicationDll": 653 case "BootstrapperApplicationDll":
654 previousId = this.ParseBootstrapperApplicationDllElement(child, previousType, previousId); 654 previousId = this.ParseBootstrapperApplicationDllElement(child, id, previousType, previousId);
655 previousType = ComplexReferenceChildType.Payload; 655 previousType = ComplexReferenceChildType.Payload;
656 break; 656 break;
657 case "Payload": 657 case "Payload":
@@ -683,15 +683,21 @@ namespace WixToolset.Core
683 /// Parse the BoostrapperApplication element. 683 /// Parse the BoostrapperApplication element.
684 /// </summary> 684 /// </summary>
685 /// <param name="node">Element to parse</param> 685 /// <param name="node">Element to parse</param>
686 /// <param name="defaultId"></param>
686 /// <param name="previousType"></param> 687 /// <param name="previousType"></param>
687 /// <param name="previousId"></param> 688 /// <param name="previousId"></param>
688 private Identifier ParseBootstrapperApplicationDllElement(XElement node, ComplexReferenceChildType previousType, Identifier previousId) 689 private Identifier ParseBootstrapperApplicationDllElement(XElement node, Identifier defaultId, ComplexReferenceChildType previousType, Identifier previousId)
689 { 690 {
690 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); 691 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
692 var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node)
693 {
694 Id = defaultId,
695 };
691 var dpiAwareness = WixBootstrapperApplicationDpiAwarenessType.PerMonitorV2; 696 var dpiAwareness = WixBootstrapperApplicationDpiAwarenessType.PerMonitorV2;
692 697
693 // The BootstrapperApplicationDll element acts like a Payload element so delegate to the "Payload" attribute parsing code to parse and create a Payload entry. 698 // This list lets us evaluate extension attributes *after* all core attributes
694 this.ParsePayloadElementContent(node, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId, true, out var id); 699 // have been parsed and dealt with, regardless of authoring order.
700 var extensionAttributes = new List<XAttribute>();
695 701
696 foreach (var attrib in node.Attributes()) 702 foreach (var attrib in node.Attributes())
697 { 703 {
@@ -699,6 +705,15 @@ namespace WixToolset.Core
699 { 705 {
700 switch (attrib.Name.LocalName) 706 switch (attrib.Name.LocalName)
701 { 707 {
708 case "Id":
709 compilerPayload.ParseId(attrib);
710 break;
711 case "Name":
712 compilerPayload.ParseName(attrib);
713 break;
714 case "SourceFile":
715 compilerPayload.ParseSourceFile(attrib);
716 break;
702 case "DpiAwareness": 717 case "DpiAwareness":
703 var dpiAwarenessValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 718 var dpiAwarenessValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
704 switch (dpiAwarenessValue) 719 switch (dpiAwarenessValue)
@@ -723,8 +738,28 @@ namespace WixToolset.Core
723 break; 738 break;
724 } 739 }
725 break; 740 break;
741 default:
742 this.Core.UnexpectedAttribute(node, attrib);
743 break;
726 } 744 }
727 } 745 }
746 else
747 {
748 extensionAttributes.Add(attrib);
749 }
750 }
751
752 compilerPayload.FinishCompilingPayload();
753
754 // Now that the Id is known, we can parse the extension attributes.
755 var context = new Dictionary<string, string>
756 {
757 ["Id"] = compilerPayload.Id.Id,
758 };
759
760 foreach (var extensionAttribute in extensionAttributes)
761 {
762 this.Core.ParseExtensionAttribute(node, extensionAttribute, context);
728 } 763 }
729 764
730 foreach (var child in node.Elements()) 765 foreach (var child in node.Elements())
@@ -746,19 +781,20 @@ namespace WixToolset.Core
746 781
747 if (!this.Core.EncounteredError) 782 if (!this.Core.EncounteredError)
748 { 783 {
784 compilerPayload.CreatePayloadSymbol(ComplexReferenceParentType.Container, Compiler.BurnUXContainerId.Id, previousType, previousId?.Id);
749 this.Core.AddSymbol(new WixBundleContainerSymbol(sourceLineNumbers, Compiler.BurnUXContainerId) 785 this.Core.AddSymbol(new WixBundleContainerSymbol(sourceLineNumbers, Compiler.BurnUXContainerId)
750 { 786 {
751 Name = "bundle-ux.cab", 787 Name = "bundle-ux.cab",
752 Type = ContainerType.Attached 788 Type = ContainerType.Attached
753 }); 789 });
754 790
755 this.Core.AddSymbol(new WixBootstrapperApplicationDllSymbol(sourceLineNumbers, id) 791 this.Core.AddSymbol(new WixBootstrapperApplicationDllSymbol(sourceLineNumbers, compilerPayload.Id)
756 { 792 {
757 DpiAwareness = dpiAwareness, 793 DpiAwareness = dpiAwareness,
758 }); 794 });
759 } 795 }
760 796
761 return id; 797 return compilerPayload.Id;
762 } 798 }
763 799
764 /// <summary> 800 /// <summary>
@@ -1134,16 +1170,57 @@ namespace WixToolset.Core
1134 private void ParseBundleExtensionElement(XElement node) 1170 private void ParseBundleExtensionElement(XElement node)
1135 { 1171 {
1136 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); 1172 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1173 var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node);
1137 Identifier previousId = null; 1174 Identifier previousId = null;
1138 var previousType = ComplexReferenceChildType.Unknown; 1175 var previousType = ComplexReferenceChildType.Unknown;
1139 1176
1140 // The BundleExtension element acts like a Payload element so delegate to the "Payload" attribute parsing code to parse and create a Payload entry. 1177 // This list lets us evaluate extension attributes *after* all core attributes
1141 if (this.ParsePayloadElementContent(node, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId, true, out var id)) 1178 // have been parsed and dealt with, regardless of authoring order.
1179 var extensionAttributes = new List<XAttribute>();
1180
1181 foreach (var attrib in node.Attributes())
1142 { 1182 {
1143 previousId = id; 1183 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1144 previousType = ComplexReferenceChildType.Payload; 1184 {
1185 switch (attrib.Name.LocalName)
1186 {
1187 case "Id":
1188 compilerPayload.ParseId(attrib);
1189 break;
1190 case "Name":
1191 compilerPayload.ParseName(attrib);
1192 break;
1193 case "SourceFile":
1194 compilerPayload.ParseSourceFile(attrib);
1195 break;
1196 default:
1197 this.Core.UnexpectedAttribute(node, attrib);
1198 break;
1199 }
1200 }
1201 else
1202 {
1203 extensionAttributes.Add(attrib);
1204 }
1145 } 1205 }
1146 1206
1207 compilerPayload.FinishCompilingPayload();
1208
1209 // Now that the Id is known, we can parse the extension attributes.
1210 var context = new Dictionary<string, string>
1211 {
1212 ["Id"] = compilerPayload.Id.Id,
1213 };
1214
1215 foreach (var extensionAttribute in extensionAttributes)
1216 {
1217 this.Core.ParseExtensionAttribute(node, extensionAttribute, context);
1218 }
1219
1220 compilerPayload.CreatePayloadSymbol(ComplexReferenceParentType.Container, Compiler.BurnUXContainerId.Id, previousType, previousId?.Id);
1221 previousId = compilerPayload.Id;
1222 previousType = ComplexReferenceChildType.Payload;
1223
1147 foreach (var child in node.Elements()) 1224 foreach (var child in node.Elements())
1148 { 1225 {
1149 if (CompilerCore.WixNamespace == child.Name.Namespace) 1226 if (CompilerCore.WixNamespace == child.Name.Namespace)
@@ -1172,9 +1249,9 @@ namespace WixToolset.Core
1172 // Add the BundleExtension. 1249 // Add the BundleExtension.
1173 if (!this.Core.EncounteredError) 1250 if (!this.Core.EncounteredError)
1174 { 1251 {
1175 this.Core.AddSymbol(new WixBundleExtensionSymbol(sourceLineNumbers, id) 1252 this.Core.AddSymbol(new WixBundleExtensionSymbol(sourceLineNumbers, compilerPayload.Id)
1176 { 1253 {
1177 PayloadRef = id.Id, 1254 PayloadRef = compilerPayload.Id.Id,
1178 }); 1255 });
1179 } 1256 }
1180 } 1257 }
@@ -1294,53 +1371,8 @@ namespace WixToolset.Core
1294 Debug.Assert(ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType); 1371 Debug.Assert(ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType);
1295 Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PayloadGroup == previousType || ComplexReferenceChildType.Payload == previousType); 1372 Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PayloadGroup == previousType || ComplexReferenceChildType.Payload == previousType);
1296 1373
1297 this.ParsePayloadElementContent(node, parentType, parentId, previousType, previousId, true, out var id);
1298 var context = new Dictionary<string, string>
1299 {
1300 ["Id"] = id?.Id
1301 };
1302
1303 foreach (var child in node.Elements())
1304 {
1305 if (CompilerCore.WixNamespace == child.Name.Namespace)
1306 {
1307 switch (child.Name.LocalName)
1308 {
1309 default:
1310 this.Core.UnexpectedElement(node, child);
1311 break;
1312 }
1313 }
1314 else
1315 {
1316 this.Core.ParseExtensionElement(node, child, context);
1317 }
1318 }
1319
1320 return id;
1321 }
1322
1323 /// <summary>
1324 /// Parse the attributes of the Payload element.
1325 /// </summary>
1326 /// <param name="node">Element to parse</param>
1327 /// <param name="parentType">ComplexReferenceParentType of parent element.</param>
1328 /// <param name="parentId">Identifier of parent element.</param>
1329 /// <param name="previousType"></param>
1330 /// <param name="previousId"></param>
1331 /// <param name="required"></param>
1332 /// <param name="id"></param>
1333 /// <returns>Whether SourceFile was specified.</returns>
1334 private bool ParsePayloadElementContent(XElement node, ComplexReferenceParentType parentType, Identifier parentId, ComplexReferenceChildType previousType, Identifier previousId, bool required, out Identifier id)
1335 {
1336 Debug.Assert(ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType);
1337
1338 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); 1374 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1339 var compressed = YesNoDefaultType.Default; 1375 var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node);
1340 id = null;
1341 string name = null;
1342 string sourceFile = null;
1343 string downloadUrl = null;
1344 1376
1345 // This list lets us evaluate extension attributes *after* all core attributes 1377 // This list lets us evaluate extension attributes *after* all core attributes
1346 // have been parsed and dealt with, regardless of authoring order. 1378 // have been parsed and dealt with, regardless of authoring order.
@@ -1350,32 +1382,32 @@ namespace WixToolset.Core
1350 { 1382 {
1351 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) 1383 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1352 { 1384 {
1385 var allowed = true;
1353 switch (attrib.Name.LocalName) 1386 switch (attrib.Name.LocalName)
1354 { 1387 {
1355 case "Id": 1388 case "Id":
1356 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); 1389 compilerPayload.ParseId(attrib);
1357 break; 1390 break;
1358 case "Compressed": 1391 case "Compressed":
1359 compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); 1392 compilerPayload.ParseCompressed(attrib);
1360 break; 1393 break;
1361 case "Name": 1394 case "Name":
1362 name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false, true); 1395 compilerPayload.ParseName(attrib);
1363 break; 1396 break;
1364 case "SourceFile": 1397 case "SourceFile":
1365 sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 1398 compilerPayload.ParseSourceFile(attrib);
1366 break; 1399 break;
1367 case "DownloadUrl": 1400 case "DownloadUrl":
1368 downloadUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 1401 compilerPayload.ParseDownloadUrl(attrib);
1369 break; 1402 break;
1370 case "DpiAwareness": 1403 default:
1371 if (node.Name.LocalName != "BootstrapperApplicationDll") 1404 allowed = false;
1372 { 1405 break;
1373 this.Core.UnexpectedAttribute(node, attrib); 1406 }
1374 } 1407
1375 break; 1408 if (!allowed)
1376 default: 1409 {
1377 this.Core.UnexpectedAttribute(node, attrib); 1410 this.Core.UnexpectedAttribute(node, attrib);
1378 break;
1379 } 1411 }
1380 } 1412 }
1381 else 1413 else
@@ -1384,15 +1416,12 @@ namespace WixToolset.Core
1384 } 1416 }
1385 } 1417 }
1386 1418
1387 if (null == id) 1419 compilerPayload.FinishCompilingPayload();
1388 {
1389 id = this.Core.CreateIdentifier("pay", sourceFile?.ToUpperInvariant() ?? String.Empty);
1390 }
1391 1420
1392 // Now that the PayloadId is known, we can parse the extension attributes. 1421 // Now that the PayloadId is known, we can parse the extension attributes.
1393 var context = new Dictionary<string, string> 1422 var context = new Dictionary<string, string>
1394 { 1423 {
1395 ["Id"] = id.Id 1424 ["Id"] = compilerPayload.Id.Id,
1396 }; 1425 };
1397 1426
1398 foreach (var extensionAttribute in extensionAttributes) 1427 foreach (var extensionAttribute in extensionAttributes)
@@ -1400,36 +1429,32 @@ namespace WixToolset.Core
1400 this.Core.ParseExtensionAttribute(node, extensionAttribute, context); 1429 this.Core.ParseExtensionAttribute(node, extensionAttribute, context);
1401 } 1430 }
1402 1431
1403 // Let caller handle the children. 1432 foreach (var child in node.Elements())
1404
1405 if (Compiler.BurnUXContainerId == parentId)
1406 { 1433 {
1407 if (compressed == YesNoDefaultType.No) 1434 if (CompilerCore.WixNamespace == child.Name.Namespace)
1408 { 1435 {
1409 this.Core.Write(WarningMessages.UxPayloadsOnlySupportEmbedding(sourceLineNumbers, sourceFile)); 1436 switch (child.Name.LocalName)
1437 {
1438 default:
1439 this.Core.UnexpectedElement(node, child);
1440 break;
1441 }
1410 } 1442 }
1411 1443 else
1412 compressed = YesNoDefaultType.Yes;
1413 }
1414
1415 if (sourceFile == null)
1416 {
1417 if (required)
1418 { 1444 {
1419 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile")); 1445 this.Core.ParseExtensionElement(node, child, context);
1420 } 1446 }
1421 return false;
1422 } 1447 }
1423 1448
1424 this.CreatePayloadRow(sourceLineNumbers, id, name, sourceFile, downloadUrl, parentType, parentId, previousType, previousId, compressed, null, null, null); 1449 compilerPayload.CreatePayloadSymbol(parentType, parentId?.Id, previousType, previousId?.Id);
1425 1450
1426 return true; 1451 return compilerPayload.Id;
1427 } 1452 }
1428 1453
1429 private RemotePayload ParseRemotePayloadElement(XElement node) 1454 private CompilerPayload ParseRemotePayloadElement(XElement node)
1430 { 1455 {
1431 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); 1456 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1432 var remotePayload = new RemotePayload(); 1457 var remotePayload = new CompilerPayload(this.Core, sourceLineNumbers, node);
1433 1458
1434 foreach (var attrib in node.Attributes()) 1459 foreach (var attrib in node.Attributes())
1435 { 1460 {
@@ -1438,19 +1463,19 @@ namespace WixToolset.Core
1438 switch (attrib.Name.LocalName) 1463 switch (attrib.Name.LocalName)
1439 { 1464 {
1440 case "Description": 1465 case "Description":
1441 remotePayload.Description = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 1466 remotePayload.ParseDescription(attrib);
1442 break; 1467 break;
1443 case "Hash": 1468 case "Hash":
1444 remotePayload.Hash = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 1469 remotePayload.ParseHash(attrib);
1445 break; 1470 break;
1446 case "ProductName": 1471 case "ProductName":
1447 remotePayload.ProductName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 1472 remotePayload.ParseProductName(attrib);
1448 break; 1473 break;
1449 case "Size": 1474 case "Size":
1450 remotePayload.Size = this.Core.GetAttributeLongValue(sourceLineNumbers, attrib, 0, Int64.MaxValue); 1475 remotePayload.ParseSize(attrib);
1451 break; 1476 break;
1452 case "Version": 1477 case "Version":
1453 remotePayload.Version = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 1478 remotePayload.ParseVersion(attrib);
1454 break; 1479 break;
1455 default: 1480 default:
1456 this.Core.UnexpectedAttribute(node, attrib); 1481 this.Core.UnexpectedAttribute(node, attrib);
@@ -1492,57 +1517,6 @@ namespace WixToolset.Core
1492 } 1517 }
1493 1518
1494 /// <summary> 1519 /// <summary>
1495 /// Creates the row for a Payload.
1496 /// </summary>
1497 /// <param name="sourceLineNumbers"></param>
1498 /// <param name="id"></param>
1499 /// <param name="name"></param>
1500 /// <param name="sourceFile"></param>
1501 /// <param name="downloadUrl"></param>
1502 /// <param name="parentType">ComplexReferenceParentType of parent element</param>
1503 /// <param name="parentId">Identifier of parent element.</param>
1504 /// <param name="previousType"></param>
1505 /// <param name="previousId"></param>
1506 /// <param name="compressed"></param>
1507 /// <param name="displayName"></param>
1508 /// <param name="description"></param>
1509 /// <param name="remotePayload"></param>
1510 /// <returns></returns>
1511 private WixBundlePayloadSymbol CreatePayloadRow(SourceLineNumber sourceLineNumbers, Identifier id, string name, string sourceFile, string downloadUrl, ComplexReferenceParentType parentType,
1512 Identifier parentId, ComplexReferenceChildType previousType, Identifier previousId, YesNoDefaultType compressed, string displayName, string description,
1513 RemotePayload remotePayload)
1514 {
1515 WixBundlePayloadSymbol symbol = null;
1516
1517 if (!this.Core.EncounteredError)
1518 {
1519 symbol = this.Core.AddSymbol(new WixBundlePayloadSymbol(sourceLineNumbers, id)
1520 {
1521 Name = String.IsNullOrEmpty(name) ? Path.GetFileName(sourceFile) : name,
1522 SourceFile = new IntermediateFieldPathValue { Path = sourceFile },
1523 DownloadUrl = downloadUrl,
1524 Compressed = (compressed == YesNoDefaultType.Yes) ? true : (compressed == YesNoDefaultType.No) ? (bool?)false : null,
1525 UnresolvedSourceFile = sourceFile, // duplicate of sourceFile but in a string column so it won't get resolved to a full path during binding.
1526 DisplayName = displayName,
1527 Description = description,
1528 });
1529
1530 if (null != remotePayload)
1531 {
1532 symbol.Description = remotePayload.Description;
1533 symbol.DisplayName = remotePayload.ProductName;
1534 symbol.Hash = remotePayload.Hash;
1535 symbol.FileSize = remotePayload.Size;
1536 symbol.Version = remotePayload.Version;
1537 }
1538
1539 this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId.Id, ComplexReferenceChildType.Payload, id.Id, previousType, previousId?.Id);
1540 }
1541
1542 return symbol;
1543 }
1544
1545 /// <summary>
1546 /// Parse PayloadGroup element. 1520 /// Parse PayloadGroup element.
1547 /// </summary> 1521 /// </summary>
1548 /// <param name="node">Element to parse</param> 1522 /// <param name="node">Element to parse</param>
@@ -1613,7 +1587,7 @@ namespace WixToolset.Core
1613 { 1587 {
1614 this.Core.AddSymbol(new WixBundlePayloadGroupSymbol(sourceLineNumbers, id)); 1588 this.Core.AddSymbol(new WixBundlePayloadGroupSymbol(sourceLineNumbers, id));
1615 1589
1616 this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId?.Id, ComplexReferenceChildType.PayloadGroup, id.Id, ComplexReferenceChildType.Unknown, null); 1590 this.Core.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId?.Id, ComplexReferenceChildType.PayloadGroup, id.Id, ComplexReferenceChildType.Unknown, null);
1617 } 1591 }
1618 } 1592 }
1619 1593
@@ -1661,52 +1635,12 @@ namespace WixToolset.Core
1661 1635
1662 this.Core.ParseForExtensionElements(node); 1636 this.Core.ParseForExtensionElements(node);
1663 1637
1664 this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId?.Id, ComplexReferenceChildType.PayloadGroup, id?.Id, previousType, previousId?.Id); 1638 this.Core.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId?.Id, ComplexReferenceChildType.PayloadGroup, id?.Id, previousType, previousId?.Id);
1665 1639
1666 return id; 1640 return id;
1667 } 1641 }
1668 1642
1669 /// <summary> 1643 /// <summary>
1670 /// Creates group and ordering information.
1671 /// </summary>
1672 /// <param name="sourceLineNumbers">Source line numbers.</param>
1673 /// <param name="parentType">Type of parent group, if known.</param>
1674 /// <param name="parentId">Identifier of parent group, if known.</param>
1675 /// <param name="type">Type of this item.</param>
1676 /// <param name="id">Identifier for this item.</param>
1677 /// <param name="previousType">Type of previous item, if known.</param>
1678 /// <param name="previousId">Identifier of previous item, if known</param>
1679 private void CreateGroupAndOrderingRows(SourceLineNumber sourceLineNumbers,
1680 ComplexReferenceParentType parentType, string parentId,
1681 ComplexReferenceChildType type, string id,
1682 ComplexReferenceChildType previousType, string previousId)
1683 {
1684 if (this.Core.EncounteredError)
1685 {
1686 return;
1687 }
1688
1689 if (ComplexReferenceParentType.Unknown != parentType && null != parentId)
1690 {
1691 this.Core.CreateWixGroupRow(sourceLineNumbers, parentType, parentId, type, id);
1692 }
1693
1694 if (ComplexReferenceChildType.Unknown != previousType && null != previousId)
1695 {
1696 // TODO: Should we define our own enum for this, just to ensure there's no "cross-contamination"?
1697 // TODO: Also, we could potentially include an 'Attributes' field to track things like
1698 // 'before' vs. 'after', and explicit vs. inferred dependencies.
1699 this.Core.AddSymbol(new WixOrderingSymbol(sourceLineNumbers)
1700 {
1701 ItemType = type,
1702 ItemIdRef = id,
1703 DependsOnType = previousType,
1704 DependsOnIdRef = previousId
1705 });
1706 }
1707 }
1708
1709 /// <summary>
1710 /// Parse ExitCode element. 1644 /// Parse ExitCode element.
1711 /// </summary> 1645 /// </summary>
1712 /// <param name="node">Element to parse</param> 1646 /// <param name="node">Element to parse</param>
@@ -2050,17 +1984,15 @@ namespace WixToolset.Core
2050 Debug.Assert(ComplexReferenceParentType.PackageGroup == parentType); 1984 Debug.Assert(ComplexReferenceParentType.PackageGroup == parentType);
2051 Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType); 1985 Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType);
2052 1986
2053 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); 1987 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);;
2054 Identifier id = null; 1988 var compilerPayload = new CompilerPayload(this.Core, sourceLineNumbers, node)
2055 string name = null; 1989 {
2056 string sourceFile = null; 1990 IsRequired = false,
2057 string downloadUrl = null; 1991 };
2058 string after = null; 1992 string after = null;
2059 string installCondition = null; 1993 string installCondition = null;
2060 var cache = YesNoAlwaysType.Yes; // the default is to cache everything in tradeoff for stability over disk space. 1994 var cache = YesNoAlwaysType.Yes; // the default is to cache everything in tradeoff for stability over disk space.
2061 string cacheId = null; 1995 string cacheId = null;
2062 string description = null;
2063 string displayName = null;
2064 var logPathVariable = (packageType == WixBundlePackageType.Msu) ? String.Empty : null; 1996 var logPathVariable = (packageType == WixBundlePackageType.Msu) ? String.Empty : null;
2065 var rollbackPathVariable = (packageType == WixBundlePackageType.Msu) ? String.Empty : null; 1997 var rollbackPathVariable = (packageType == WixBundlePackageType.Msu) ? String.Empty : null;
2066 var permanent = YesNoType.NotSet; 1998 var permanent = YesNoType.NotSet;
@@ -2074,10 +2006,9 @@ namespace WixToolset.Core
2074 string protocol = null; 2006 string protocol = null;
2075 var installSize = CompilerConstants.IntegerNotSet; 2007 var installSize = CompilerConstants.IntegerNotSet;
2076 string msuKB = null; 2008 string msuKB = null;
2077 var compressed = YesNoDefaultType.Default;
2078 var enableFeatureSelection = YesNoType.NotSet; 2009 var enableFeatureSelection = YesNoType.NotSet;
2079 var forcePerMachine = YesNoType.NotSet; 2010 var forcePerMachine = YesNoType.NotSet;
2080 RemotePayload remotePayload = null; 2011 CompilerPayload remotePayload = null;
2081 var slipstream = YesNoType.NotSet; 2012 var slipstream = YesNoType.NotSet;
2082 2013
2083 var expectedNetFx4Args = new string[] { "/q", "/norestart", "/chainingpackage" }; 2014 var expectedNetFx4Args = new string[] { "/q", "/norestart", "/chainingpackage" };
@@ -2094,20 +2025,16 @@ namespace WixToolset.Core
2094 switch (attrib.Name.LocalName) 2025 switch (attrib.Name.LocalName)
2095 { 2026 {
2096 case "Id": 2027 case "Id":
2097 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); 2028 compilerPayload.ParseId(attrib);
2098 break; 2029 break;
2099 case "Name": 2030 case "Name":
2100 name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false, true); 2031 compilerPayload.ParseName(attrib);
2101 if (!this.Core.IsValidLongFilename(name, false, true))
2102 {
2103 this.Core.Write(ErrorMessages.IllegalLongFilename(sourceLineNumbers, node.Name.LocalName, "Name", name));
2104 }
2105 break; 2032 break;
2106 case "SourceFile": 2033 case "SourceFile":
2107 sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 2034 compilerPayload.ParseSourceFile(attrib);
2108 break; 2035 break;
2109 case "DownloadUrl": 2036 case "DownloadUrl":
2110 downloadUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 2037 compilerPayload.ParseDownloadUrl(attrib);
2111 break; 2038 break;
2112 case "After": 2039 case "After":
2113 after = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 2040 after = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
@@ -2139,10 +2066,10 @@ namespace WixToolset.Core
2139 cacheId = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 2066 cacheId = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2140 break; 2067 break;
2141 case "Description": 2068 case "Description":
2142 description = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 2069 compilerPayload.ParseDescription(attrib);
2143 break; 2070 break;
2144 case "DisplayName": 2071 case "DisplayName":
2145 displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 2072 compilerPayload.ParseDisplayName(attrib);
2146 break; 2073 break;
2147 case "EnableFeatureSelection": 2074 case "EnableFeatureSelection":
2148 enableFeatureSelection = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); 2075 enableFeatureSelection = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
@@ -2200,7 +2127,7 @@ namespace WixToolset.Core
2200 allowed = (packageType == WixBundlePackageType.Msu); 2127 allowed = (packageType == WixBundlePackageType.Msu);
2201 break; 2128 break;
2202 case "Compressed": 2129 case "Compressed":
2203 compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); 2130 compilerPayload.ParseCompressed(attrib);
2204 break; 2131 break;
2205 case "Slipstream": 2132 case "Slipstream":
2206 slipstream = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); 2133 slipstream = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
@@ -2242,65 +2169,8 @@ namespace WixToolset.Core
2242 remotePayload = this.ParseRemotePayloadElement(child); 2169 remotePayload = this.ParseRemotePayloadElement(child);
2243 } 2170 }
2244 2171
2245 if (String.IsNullOrEmpty(sourceFile)) 2172 compilerPayload.FinishCompilingPackage(remotePayload);
2246 { 2173 var id = compilerPayload.Id;
2247 if (String.IsNullOrEmpty(name))
2248 {
2249 this.Core.Write(ErrorMessages.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Name", "SourceFile"));
2250 }
2251 else if (null == remotePayload)
2252 {
2253 sourceFile = Path.Combine("SourceDir", name);
2254 }
2255 }
2256 else if (null != remotePayload)
2257 {
2258 this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, "RemotePayload", "SourceFile"));
2259 }
2260 else if (sourceFile.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
2261 {
2262 if (String.IsNullOrEmpty(name))
2263 {
2264 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name", "SourceFile", sourceFile));
2265 }
2266 else
2267 {
2268 sourceFile = Path.Combine(sourceFile, Path.GetFileName(name));
2269 }
2270 }
2271
2272 if (null == downloadUrl && null != remotePayload)
2273 {
2274 this.Core.Write(ErrorMessages.ExpectedAttributeWithElement(sourceLineNumbers, node.Name.LocalName, "DownloadUrl", "RemotePayload"));
2275 }
2276
2277 if (YesNoDefaultType.No != compressed && null != remotePayload)
2278 {
2279 compressed = YesNoDefaultType.No;
2280 this.Core.Write(WarningMessages.RemotePayloadsMustNotAlsoBeCompressed(sourceLineNumbers, node.Name.LocalName));
2281 }
2282
2283 if (null == id)
2284 {
2285 if (!String.IsNullOrEmpty(name))
2286 {
2287 id = this.Core.CreateIdentifierFromFilename(Path.GetFileName(name));
2288 }
2289 else if (!String.IsNullOrEmpty(sourceFile))
2290 {
2291 id = this.Core.CreateIdentifierFromFilename(Path.GetFileName(sourceFile));
2292 }
2293
2294 if (null == id)
2295 {
2296 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
2297 id = Identifier.Invalid;
2298 }
2299 else if (!Common.IsIdentifier(id.Id))
2300 {
2301 this.Core.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
2302 }
2303 }
2304 2174
2305 if (null == logPathVariable) 2175 if (null == logPathVariable)
2306 { 2176 {
@@ -2424,7 +2294,7 @@ namespace WixToolset.Core
2424 } 2294 }
2425 else 2295 else
2426 { 2296 {
2427 var context = new Dictionary<string, string>() { { "Id", id?.Id } }; 2297 var context = new Dictionary<string, string>() { { "Id", id.Id } };
2428 this.Core.ParseExtensionElement(node, child, context); 2298 this.Core.ParseExtensionElement(node, child, context);
2429 } 2299 }
2430 } 2300 }
@@ -2432,8 +2302,7 @@ namespace WixToolset.Core
2432 if (!this.Core.EncounteredError) 2302 if (!this.Core.EncounteredError)
2433 { 2303 {
2434 // We create the package contents as a payload with this package as the parent 2304 // We create the package contents as a payload with this package as the parent
2435 this.CreatePayloadRow(sourceLineNumbers, id, name, sourceFile, downloadUrl, ComplexReferenceParentType.Package, id, 2305 compilerPayload.CreatePayloadSymbol(ComplexReferenceParentType.Package, id.Id);
2436 ComplexReferenceChildType.Unknown, null, compressed, displayName, description, remotePayload);
2437 2306
2438 this.Core.AddSymbol(new WixChainItemSymbol(sourceLineNumbers, id)); 2307 this.Core.AddSymbol(new WixChainItemSymbol(sourceLineNumbers, id));
2439 2308
@@ -2801,7 +2670,7 @@ namespace WixToolset.Core
2801 previousId = afterId; 2670 previousId = afterId;
2802 } 2671 }
2803 2672
2804 this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId, type, id, previousType, previousId); 2673 this.Core.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId, type, id, previousType, previousId);
2805 } 2674 }
2806 2675
2807 /// <summary> 2676 /// <summary>
@@ -3251,18 +3120,5 @@ namespace WixToolset.Core
3251 3120
3252 return WixBundleVariableType.String; 3121 return WixBundleVariableType.String;
3253 } 3122 }
3254
3255 private class RemotePayload
3256 {
3257 public string Description { get; set; }
3258
3259 public string Hash { get; set; }
3260
3261 public string ProductName { get; set; }
3262
3263 public long Size { get; set; }
3264
3265 public string Version { get; set; }
3266 }
3267 } 3123 }
3268} 3124}