aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs')
-rw-r--r--src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs860
1 files changed, 242 insertions, 618 deletions
diff --git a/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs b/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
index 61aa5189..6b4b9d68 100644
--- a/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+++ b/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
@@ -8,78 +8,51 @@ namespace WixToolset.Core.Burn
8 using System.Globalization; 8 using System.Globalization;
9 using System.IO; 9 using System.IO;
10 using System.Linq; 10 using System.Linq;
11 using System.Reflection;
12 using WixToolset.Core.Bind; 11 using WixToolset.Core.Bind;
12 using WixToolset.Core.Burn.Bind;
13 using WixToolset.Core.Burn.Bundles; 13 using WixToolset.Core.Burn.Bundles;
14 using WixToolset.Data; 14 using WixToolset.Data;
15 using WixToolset.Data.Bind; 15 using WixToolset.Data.Burn;
16 using WixToolset.Data.Tuples;
16 using WixToolset.Extensibility; 17 using WixToolset.Extensibility;
17 using WixToolset.Extensibility.Data; 18 using WixToolset.Extensibility.Data;
18 using WixToolset.Extensibility.Services; 19 using WixToolset.Extensibility.Services;
19 20
20 // TODO: (4.0) Refactor so that these don't need to be copied.
21 // Copied verbatim from ext\UtilExtension\wixext\UtilCompiler.cs
22 [Flags]
23 internal enum WixFileSearchAttributes
24 {
25 Default = 0x001,
26 MinVersionInclusive = 0x002,
27 MaxVersionInclusive = 0x004,
28 MinSizeInclusive = 0x008,
29 MaxSizeInclusive = 0x010,
30 MinDateInclusive = 0x020,
31 MaxDateInclusive = 0x040,
32 WantVersion = 0x080,
33 WantExists = 0x100,
34 IsDirectory = 0x200,
35 }
36
37 [Flags]
38 internal enum WixRegistrySearchAttributes
39 {
40 Raw = 0x01,
41 Compatible = 0x02,
42 ExpandEnvironmentVariables = 0x04,
43 WantValue = 0x08,
44 WantExists = 0x10,
45 Win64 = 0x20,
46 }
47
48 internal enum WixComponentSearchAttributes
49 {
50 KeyPath = 0x1,
51 State = 0x2,
52 WantDirectory = 0x4,
53 }
54
55 [Flags]
56 internal enum WixProductSearchAttributes
57 {
58 Version = 0x1,
59 Language = 0x2,
60 State = 0x4,
61 Assignment = 0x8,
62 UpgradeCode = 0x10,
63 }
64
65 /// <summary> 21 /// <summary>
66 /// Binds a this.bundle. 22 /// Binds a this.bundle.
67 /// </summary> 23 /// </summary>
68 internal class BindBundleCommand 24 internal class BindBundleCommand
69 { 25 {
70 public BindBundleCommand(IBindContext context) 26 public BindBundleCommand(IBindContext context, IEnumerable<IBurnBackendExtension> backedExtensions)
71 { 27 {
72 //this.TableDefinitions = WindowsInstallerStandard.GetTableDefinitions(); 28 this.ServiceProvider = context.ServiceProvider;
29
30 this.Messaging = context.ServiceProvider.GetService<IMessaging>();
31
32 this.BackendHelper = context.ServiceProvider.GetService<IBackendHelper>();
73 33
34 this.BurnStubPath = context.BurnStubPath;
35 this.DefaultCompressionLevel = context.DefaultCompressionLevel;
74 this.DelayedFields = context.DelayedFields; 36 this.DelayedFields = context.DelayedFields;
75 this.ExpectedEmbeddedFiles = context.ExpectedEmbeddedFiles; 37 this.ExpectedEmbeddedFiles = context.ExpectedEmbeddedFiles;
38 this.IntermediateFolder = context.IntermediateFolder;
39 this.Output = context.IntermediateRepresentation;
40 this.OutputPath = context.OutputPath;
41 this.OutputPdbPath = context.OutputPdbPath;
42 //this.VariableResolver = context.VariableResolver;
76 43
77 var extensionManager = context.ServiceProvider.GetService<IExtensionManager>(); 44 this.BackendExtensions = backedExtensions;
78
79 this.BackendExtensions = extensionManager.GetServices<IBurnBackendExtension>();
80 } 45 }
81 46
82 public CompressionLevel DefaultCompressionLevel { private get; set; } 47 private IServiceProvider ServiceProvider { get; }
48
49 private IMessaging Messaging { get; }
50
51 private IBackendHelper BackendHelper { get; }
52
53 private string BurnStubPath { get; }
54
55 private CompressionLevel? DefaultCompressionLevel { get; }
83 56
84 public IEnumerable<IDelayedField> DelayedFields { get; } 57 public IEnumerable<IDelayedField> DelayedFields { get; }
85 58
@@ -87,17 +60,15 @@ namespace WixToolset.Core.Burn
87 60
88 private IEnumerable<IBurnBackendExtension> BackendExtensions { get; } 61 private IEnumerable<IBurnBackendExtension> BackendExtensions { get; }
89 62
90 public Intermediate Output { private get; set; } 63 private Intermediate Output { get; }
91
92 public string OutputPath { private get; set; }
93 64
94 public string PdbFile { private get; set; } 65 private string OutputPath { get; }
95 66
96 //public TableDefinitionCollection TableDefinitions { private get; set; } 67 private string OutputPdbPath { get; }
97 68
98 public string IntermediateFolder { private get; set; } 69 private string IntermediateFolder { get; }
99 70
100 public IVariableResolver VariableResolver { private get; set; } 71 private IVariableResolver VariableResolver { get; }
101 72
102 public IEnumerable<IFileTransfer> FileTransfers { get; private set; } 73 public IEnumerable<IFileTransfer> FileTransfers { get; private set; }
103 74
@@ -105,196 +76,177 @@ namespace WixToolset.Core.Burn
105 76
106 public void Execute() 77 public void Execute()
107 { 78 {
108 throw new NotImplementedException(); 79 var section = this.Output.Sections.Single();
109#if TODO 80
110 this.FileTransfers = Enumerable.Empty<FileTransfer>(); 81 var fileTransfers = new List<IFileTransfer>();
111 this.ContentFilePaths = Enumerable.Empty<string>(); 82 var trackedFiles = new List<ITrackedFile>();
112 83
113 // First look for data we expect to find... Chain, WixGroups, etc. 84 // First look for data we expect to find... Chain, WixGroups, etc.
114 85
115 // We shouldn't really get past the linker phase if there are 86 // We shouldn't really get past the linker phase if there are
116 // no group items... that means that there's no UX, no Chain, 87 // no group items... that means that there's no UX, no Chain,
117 // *and* no Containers! 88 // *and* no Containers!
118 Table chainPackageTable = this.GetRequiredTable("WixBundlePackage"); 89 var chainPackageTuples = this.GetRequiredTuples<WixBundlePackageTuple>();
119 90
120 Table wixGroupTable = this.GetRequiredTable("WixGroup"); 91 var wixGroupTuples = this.GetRequiredTuples<WixGroupTuple>();
121 92
122 // Ensure there is one and only one row in the WixBundle table. 93 // Ensure there is one and only one row in the WixBundle table.
123 // The compiler and linker behavior should have colluded to get 94 // The compiler and linker behavior should have colluded to get
124 // this behavior. 95 // this behavior.
125 WixBundleRow bundleRow = (WixBundleRow)this.GetSingleRowTable("WixBundle"); 96 var bundleTuple = this.GetSingleTuple<WixBundleTuple>();
97
98 bundleTuple.BundleId = Guid.NewGuid().ToString("B").ToUpperInvariant();
126 99
127 bundleRow.PerMachine = true; // default to per-machine but the first-per user package wil flip the bundle per-user. 100 bundleTuple.Attributes |= WixBundleAttributes.PerMachine; // default to per-machine but the first-per user package wil flip the bundle per-user.
128 101
129 // Ensure there is one and only one row in the WixBootstrapperApplication table. 102 // Ensure there is one and only one row in the WixBootstrapperApplication table.
130 // The compiler and linker behavior should have colluded to get 103 // The compiler and linker behavior should have colluded to get
131 // this behavior. 104 // this behavior.
132 Row baRow = this.GetSingleRowTable("WixBootstrapperApplication"); 105 var bundleApplicationTuple = this.GetSingleTuple<WixBootstrapperApplicationTuple>();
133 106
134 // Ensure there is one and only one row in the WixChain table. 107 // Ensure there is one and only one row in the WixChain table.
135 // The compiler and linker behavior should have colluded to get 108 // The compiler and linker behavior should have colluded to get
136 // this behavior. 109 // this behavior.
137 WixChainRow chainRow = (WixChainRow)this.GetSingleRowTable("WixChain"); 110 var chainTuple = this.GetSingleTuple<WixChainTuple>();
138 111
139 if (Messaging.Instance.EncounteredError) 112 if (this.Messaging.EncounteredError)
140 { 113 {
141 return; 114 return;
142 } 115 }
143 116
144 // If there are any fields to resolve later, create the cache to populate during bind. 117 // If there are any fields to resolve later, create the cache to populate during bind.
145 IDictionary<string, string> variableCache = null; 118 var variableCache = this.DelayedFields.Any() ? new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) : null;
146 if (this.DelayedFields.Any())
147 {
148 variableCache = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
149 }
150 119
151 // TODO: Although the WixSearch tables are defined in the Util extension, 120 // TODO: Although the WixSearch tables are defined in the Util extension,
152 // the Bundle Binder has to know all about them. We hope to revisit all 121 // the Bundle Binder has to know all about them. We hope to revisit all
153 // of this in the 4.0 timeframe. 122 // of this in the 4.0 timeframe.
154 IEnumerable<WixSearchInfo> orderedSearches = this.OrderSearches(); 123 var orderedSearches = this.OrderSearches(section);
155 124
125#if THIS_SHOULD_BE_DELETED_SINCE_RESOLVE_DOES_THIS_NOW
156 // Extract files that come from cabinet files (this does not extract files from merge modules). 126 // Extract files that come from cabinet files (this does not extract files from merge modules).
157 { 127 {
158 var extractEmbeddedFilesCommand = new ExtractEmbeddedFilesCommand(); 128 var extractEmbeddedFilesCommand = new ExtractEmbeddedFilesCommand();
159 extractEmbeddedFilesCommand.FilesWithEmbeddedFiles = ExpectedEmbeddedFiles; 129 extractEmbeddedFilesCommand.FilesWithEmbeddedFiles = ExpectedEmbeddedFiles;
160 extractEmbeddedFilesCommand.Execute(); 130 extractEmbeddedFilesCommand.Execute();
161 } 131 }
132#endif
162 133
163 // Get the explicit payloads. 134 // Get the explicit payloads.
164 RowDictionary<WixBundlePayloadRow> payloads = new RowDictionary<WixBundlePayloadRow>(this.Output.Tables["WixBundlePayload"]); 135 var payloadTuples = section.Tuples.OfType<WixBundlePayloadTuple>().ToDictionary(t => t.Id.Id);
165 136
166 // Update explicitly authored payloads with their parent package and container (as appropriate) 137 // Update explicitly authored payloads with their parent package and container (as appropriate)
167 // to make it easier to gather the payloads later. 138 // to make it easier to gather the payloads later.
168 foreach (WixGroupRow row in wixGroupTable.RowsAs<WixGroupRow>()) 139 foreach (var groupTuple in wixGroupTuples)
169 { 140 {
170 if (ComplexReferenceChildType.Payload == row.ChildType) 141 if (ComplexReferenceChildType.Payload == groupTuple.ChildType)
171 { 142 {
172 WixBundlePayloadRow payload = payloads.Get(row.ChildId); 143 var payloadTuple = payloadTuples[groupTuple.ChildId];
173 144
174 if (ComplexReferenceParentType.Package == row.ParentType) 145 if (ComplexReferenceParentType.Package == groupTuple.ParentType)
175 { 146 {
176 Debug.Assert(String.IsNullOrEmpty(payload.Package)); 147 Debug.Assert(String.IsNullOrEmpty(payloadTuple.PackageRef));
177 payload.Package = row.ParentId; 148 payloadTuple.PackageRef = groupTuple.ParentId;
178 } 149 }
179 else if (ComplexReferenceParentType.Container == row.ParentType) 150 else if (ComplexReferenceParentType.Container == groupTuple.ParentType)
180 { 151 {
181 Debug.Assert(String.IsNullOrEmpty(payload.Container)); 152 Debug.Assert(String.IsNullOrEmpty(payloadTuple.ContainerRef));
182 payload.Container = row.ParentId; 153 payloadTuple.ContainerRef = groupTuple.ParentId;
183 } 154 }
184 else if (ComplexReferenceParentType.Layout == row.ParentType) 155 else if (ComplexReferenceParentType.Layout == groupTuple.ParentType)
185 { 156 {
186 payload.LayoutOnly = true; 157 payloadTuple.LayoutOnly = true;
187 } 158 }
188 } 159 }
189 } 160 }
190 161
191 List<FileTransfer> fileTransfers = new List<FileTransfer>(); 162 var layoutDirectory = Path.GetDirectoryName(this.OutputPath);
192 string layoutDirectory = Path.GetDirectoryName(this.OutputPath);
193 163
194 // Process the explicitly authored payloads. 164 // Process the explicitly authored payloads.
195 ISet<string> processedPayloads; 165 ISet<string> processedPayloads;
196 { 166 {
197 ProcessPayloadsCommand command = new ProcessPayloadsCommand(); 167 var command = new ProcessPayloadsCommand(this.ServiceProvider, this.BackendHelper, payloadTuples.Values, bundleTuple.DefaultPackagingType, layoutDirectory);
198 command.Payloads = payloads.Values;
199 command.DefaultPackaging = bundleRow.DefaultPackagingType;
200 command.LayoutDirectory = layoutDirectory;
201 command.Execute(); 168 command.Execute();
202 169
203 fileTransfers.AddRange(command.FileTransfers); 170 fileTransfers.AddRange(command.FileTransfers);
204 171
205 processedPayloads = new HashSet<string>(payloads.Keys); 172 processedPayloads = new HashSet<string>(payloadTuples.Keys);
206 } 173 }
207 174
208 IDictionary<string, PackageFacade> facades; 175 IDictionary<string, PackageFacade> facades;
209 { 176 {
210 GetPackageFacadesCommand command = new GetPackageFacadesCommand(); 177 var command = new GetPackageFacadesCommand(chainPackageTuples, section);
211 command.PackageTable = chainPackageTable;
212 command.ExePackageTable = this.Output.Tables["WixBundleExePackage"];
213 command.MsiPackageTable = this.Output.Tables["WixBundleMsiPackage"];
214 command.MspPackageTable = this.Output.Tables["WixBundleMspPackage"];
215 command.MsuPackageTable = this.Output.Tables["WixBundleMsuPackage"];
216 command.Execute(); 178 command.Execute();
217 179
218 facades = command.PackageFacades; 180 facades = command.PackageFacades;
219 } 181 }
220 182
221 // Process each package facade. Note this is likely to add payloads and other rows to tables so 183 // Process each package facade. Note this is likely to add payloads and other tuples so
222 // note that any indexes created above may be out of date now. 184 // note that any indexes created above may be out of date now.
223 foreach (PackageFacade facade in facades.Values) 185 foreach (var facade in facades.Values)
224 { 186 {
225 switch (facade.Package.Type) 187 switch (facade.PackageTuple.Type)
226 { 188 {
227 case WixBundlePackageType.Exe: 189 case WixBundlePackageType.Exe:
228 { 190 {
229 ProcessExePackageCommand command = new ProcessExePackageCommand(); 191 var command = new ProcessExePackageCommand(facade, payloadTuples);
230 command.AuthoredPayloads = payloads; 192 command.Execute();
231 command.Facade = facade; 193 }
232 command.Execute(); 194 break;
233
234 // ? variableCache.Add(String.Concat("packageManufacturer.", facade.Package.WixChainItemId), facade.ExePackage.Manufacturer);
235 }
236 break;
237 195
238 case WixBundlePackageType.Msi: 196 case WixBundlePackageType.Msi:
239 { 197 {
240 var command = new ProcessMsiPackageCommand(); 198 var command = new ProcessMsiPackageCommand(this.ServiceProvider, this.BackendExtensions, section, facade, payloadTuples);
241 command.AuthoredPayloads = payloads; 199 command.Execute();
242 command.Facade = facade;
243 command.BackendExtensions = this.BackendExtensions;
244 command.MsiFeatureTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleMsiFeature"]);
245 command.MsiPropertyTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleMsiProperty"]);
246 command.PayloadTable = this.Output.Tables["WixBundlePayload"];
247 command.RelatedPackageTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleRelatedPackage"]);
248 command.Execute();
249
250 if (null != variableCache)
251 {
252 variableCache.Add(String.Concat("packageLanguage.", facade.Package.WixChainItemId), facade.MsiPackage.ProductLanguage.ToString());
253
254 if (null != facade.MsiPackage.Manufacturer)
255 {
256 variableCache.Add(String.Concat("packageManufacturer.", facade.Package.WixChainItemId), facade.MsiPackage.Manufacturer);
257 }
258 }
259 200
260 } 201 if (null != variableCache)
261 break; 202 {
203 var msiPackage = (WixBundleMsiPackageTuple)facade.SpecificPackageTuple;
204 variableCache.Add(String.Concat("packageLanguage.", facade.PackageId), msiPackage.ProductLanguage.ToString());
262 205
263 case WixBundlePackageType.Msp: 206 if (null != msiPackage.Manufacturer)
264 { 207 {
265 ProcessMspPackageCommand command = new ProcessMspPackageCommand(); 208 variableCache.Add(String.Concat("packageManufacturer.", facade.PackageId), msiPackage.Manufacturer);
266 command.AuthoredPayloads = payloads;
267 command.Facade = facade;
268 command.WixBundlePatchTargetCodeTable = this.Output.EnsureTable(this.TableDefinitions["WixBundlePatchTargetCode"]);
269 command.Execute();
270 } 209 }
271 break; 210 }
272 211
273 case WixBundlePackageType.Msu: 212 }
274 { 213 break;
275 ProcessMsuPackageCommand command = new ProcessMsuPackageCommand(); 214
276 command.Facade = facade; 215 case WixBundlePackageType.Msp:
277 command.Execute(); 216 {
278 } 217 var command = new ProcessMspPackageCommand(this.Messaging, section, facade, payloadTuples);
279 break; 218 command.Execute();
219 }
220 break;
221
222 case WixBundlePackageType.Msu:
223 {
224 var command = new ProcessMsuPackageCommand(facade, payloadTuples);
225 command.Execute();
226 }
227 break;
280 } 228 }
281 229
282 if (null != variableCache) 230 if (null != variableCache)
283 { 231 {
284 BindBundleCommand.PopulatePackageVariableCache(facade.Package, variableCache); 232 BindBundleCommand.PopulatePackageVariableCache(facade.PackageTuple, variableCache);
285 } 233 }
286 } 234 }
287 235
236 if (this.Messaging.EncounteredError)
237 {
238 return;
239 }
240
288 // Reindex the payloads now that all the payloads (minus the manifest payloads that will be created later) 241 // Reindex the payloads now that all the payloads (minus the manifest payloads that will be created later)
289 // are present. 242 // are present.
290 payloads = new RowDictionary<WixBundlePayloadRow>(this.Output.Tables["WixBundlePayload"]); 243 payloadTuples = section.Tuples.OfType<WixBundlePayloadTuple>().ToDictionary(t => t.Id.Id);
291 244
292 // Process the payloads that were added by processing the packages. 245 // Process the payloads that were added by processing the packages.
293 { 246 {
294 ProcessPayloadsCommand command = new ProcessPayloadsCommand(); 247 var toProcess = payloadTuples.Values.Where(r => !processedPayloads.Contains(r.Id.Id)).ToList();
295 command.Payloads = payloads.Values.Where(r => !processedPayloads.Contains(r.Id)).ToList(); 248
296 command.DefaultPackaging = bundleRow.DefaultPackagingType; 249 var command = new ProcessPayloadsCommand(this.ServiceProvider, this.BackendHelper, toProcess, bundleTuple.DefaultPackagingType, layoutDirectory);
297 command.LayoutDirectory = layoutDirectory;
298 command.Execute(); 250 command.Execute();
299 251
300 fileTransfers.AddRange(command.FileTransfers); 252 fileTransfers.AddRange(command.FileTransfers);
@@ -303,45 +255,43 @@ namespace WixToolset.Core.Burn
303 } 255 }
304 256
305 // Set the package metadata from the payloads now that we have the complete payload information. 257 // Set the package metadata from the payloads now that we have the complete payload information.
306 ILookup<string, WixBundlePayloadRow> payloadsByPackage = payloads.Values.ToLookup(p => p.Package);
307
308 { 258 {
309 foreach (PackageFacade facade in facades.Values) 259 var payloadsByPackageId = payloadTuples.Values.ToLookup(p => p.PackageRef);
260
261 foreach (var facade in facades.Values)
310 { 262 {
311 facade.Package.Size = 0; 263 facade.PackageTuple.Size = 0;
312 264
313 IEnumerable<WixBundlePayloadRow> packagePayloads = payloadsByPackage[facade.Package.WixChainItemId]; 265 var packagePayloads = payloadsByPackageId[facade.PackageId];
314 266
315 foreach (WixBundlePayloadRow payload in packagePayloads) 267 foreach (var payload in packagePayloads)
316 { 268 {
317 facade.Package.Size += payload.FileSize; 269 facade.PackageTuple.Size += payload.FileSize;
318 } 270 }
319 271
320 if (!facade.Package.InstallSize.HasValue) 272 if (!facade.PackageTuple.InstallSize.HasValue)
321 { 273 {
322 facade.Package.InstallSize = facade.Package.Size; 274 facade.PackageTuple.InstallSize = facade.PackageTuple.Size;
323
324 } 275 }
325 276
326 WixBundlePayloadRow packagePayload = payloads[facade.Package.PackagePayload]; 277 var packagePayload = payloadTuples[facade.PackageTuple.PayloadRef];
327 278
328 if (String.IsNullOrEmpty(facade.Package.Description)) 279 if (String.IsNullOrEmpty(facade.PackageTuple.Description))
329 { 280 {
330 facade.Package.Description = packagePayload.Description; 281 facade.PackageTuple.Description = packagePayload.Description;
331 } 282 }
332 283
333 if (String.IsNullOrEmpty(facade.Package.DisplayName)) 284 if (String.IsNullOrEmpty(facade.PackageTuple.DisplayName))
334 { 285 {
335 facade.Package.DisplayName = packagePayload.DisplayName; 286 facade.PackageTuple.DisplayName = packagePayload.DisplayName;
336 } 287 }
337 } 288 }
338 } 289 }
339 290
340
341 // Give the UX payloads their embedded IDs... 291 // Give the UX payloads their embedded IDs...
342 int uxPayloadIndex = 0; 292 var uxPayloadIndex = 0;
343 { 293 {
344 foreach (WixBundlePayloadRow payload in payloads.Values.Where(p => Compiler.BurnUXContainerId == p.Container)) 294 foreach (var payload in payloadTuples.Values.Where(p => BurnConstants.BurnUXContainerName == p.ContainerRef))
345 { 295 {
346 // In theory, UX payloads could be embedded in the UX CAB, external to the bundle EXE, or even 296 // In theory, UX payloads could be embedded in the UX CAB, external to the bundle EXE, or even
347 // downloaded. The current engine requires the UX to be fully present before any downloading starts, 297 // downloaded. The current engine requires the UX to be fully present before any downloading starts,
@@ -349,7 +299,7 @@ namespace WixToolset.Core.Burn
349 // into the temporary UX directory correctly, so we don't allow external either. 299 // into the temporary UX directory correctly, so we don't allow external either.
350 if (PackagingType.Embedded != payload.Packaging) 300 if (PackagingType.Embedded != payload.Packaging)
351 { 301 {
352 Messaging.Instance.OnMessage(WixWarnings.UxPayloadsOnlySupportEmbedding(payload.SourceLineNumbers, payload.FullFileName)); 302 this.Messaging.Write(WarningMessages.UxPayloadsOnlySupportEmbedding(payload.SourceLineNumbers, payload.SourceFile.Path));
353 payload.Packaging = PackagingType.Embedded; 303 payload.Packaging = PackagingType.Embedded;
354 } 304 }
355 305
@@ -360,12 +310,12 @@ namespace WixToolset.Core.Burn
360 if (0 == uxPayloadIndex) 310 if (0 == uxPayloadIndex)
361 { 311 {
362 // If we didn't get any UX payloads, it's an error! 312 // If we didn't get any UX payloads, it's an error!
363 throw new WixException(WixErrors.MissingBundleInformation("BootstrapperApplication")); 313 throw new WixException(ErrorMessages.MissingBundleInformation("BootstrapperApplication"));
364 } 314 }
365 315
366 // Give the embedded payloads without an embedded id yet an embedded id. 316 // Give the embedded payloads without an embedded id yet an embedded id.
367 int payloadIndex = 0; 317 var payloadIndex = 0;
368 foreach (WixBundlePayloadRow payload in payloads.Values) 318 foreach (var payload in payloadTuples.Values)
369 { 319 {
370 Debug.Assert(PackagingType.Unknown != payload.Packaging); 320 Debug.Assert(PackagingType.Unknown != payload.Packaging);
371 321
@@ -377,38 +327,38 @@ namespace WixToolset.Core.Burn
377 } 327 }
378 } 328 }
379 329
330 if (this.Messaging.EncounteredError)
331 {
332 return;
333 }
334
380 // Determine patches to automatically slipstream. 335 // Determine patches to automatically slipstream.
381 { 336 {
382 AutomaticallySlipstreamPatchesCommand command = new AutomaticallySlipstreamPatchesCommand(); 337 var command = new AutomaticallySlipstreamPatchesCommand(section, facades.Values);
383 command.PackageFacades = facades.Values;
384 command.SlipstreamMspTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleSlipstreamMsp"]);
385 command.WixBundlePatchTargetCodeTable = this.Output.EnsureTable(this.TableDefinitions["WixBundlePatchTargetCode"]);
386 command.Execute(); 338 command.Execute();
387 } 339 }
388 340
389 // If catalog files exist, non-embedded payloads should validate with the catalogs. 341 // If catalog files exist, non-embedded payloads should validate with the catalogs.
390 IEnumerable<WixBundleCatalogRow> catalogs = this.Output.Tables["WixBundleCatalog"].RowsAs<WixBundleCatalogRow>(); 342 var catalogs = section.Tuples.OfType<WixBundleCatalogTuple>().ToList();
391 343
392 if (catalogs.Any()) 344 if (catalogs.Count > 0)
393 { 345 {
394 VerifyPayloadsWithCatalogCommand command = new VerifyPayloadsWithCatalogCommand(); 346 var command = new VerifyPayloadsWithCatalogCommand(this.Messaging, catalogs, payloadTuples.Values);
395 command.Catalogs = catalogs;
396 command.Payloads = payloads.Values;
397 command.Execute(); 347 command.Execute();
398 } 348 }
399 349
400 if (Messaging.Instance.EncounteredError) 350 if (this.Messaging.EncounteredError)
401 { 351 {
402 return; 352 return;
403 } 353 }
404 354
405 IEnumerable<PackageFacade> orderedFacades; 355 IEnumerable<PackageFacade> orderedFacades;
406 IEnumerable<WixBundleRollbackBoundaryRow> boundaries; 356 IEnumerable<WixBundleRollbackBoundaryTuple> boundaries;
407 { 357 {
408 OrderPackagesAndRollbackBoundariesCommand command = new OrderPackagesAndRollbackBoundariesCommand(); 358 var groupTuples = section.Tuples.OfType<WixGroupTuple>();
409 command.Boundaries = new RowDictionary<WixBundleRollbackBoundaryRow>(this.Output.Tables["WixBundleRollbackBoundary"]); 359 var boundaryTuplesById = section.Tuples.OfType<WixBundleRollbackBoundaryTuple>().ToDictionary(b => b.Id.Id);
410 command.PackageFacades = facades; 360
411 command.WixGroupTable = wixGroupTable; 361 var command = new OrderPackagesAndRollbackBoundariesCommand(this.Messaging, groupTuples, boundaryTuplesById, facades);
412 command.Execute(); 362 command.Execute();
413 363
414 orderedFacades = command.OrderedPackageFacades; 364 orderedFacades = command.OrderedPackageFacades;
@@ -418,280 +368,122 @@ namespace WixToolset.Core.Burn
418 // Resolve any delayed fields before generating the manifest. 368 // Resolve any delayed fields before generating the manifest.
419 if (this.DelayedFields.Any()) 369 if (this.DelayedFields.Any())
420 { 370 {
421 var resolveDelayedFieldsCommand = new ResolveDelayedFieldsCommand(); 371 var resolveDelayedFieldsCommand = new ResolveDelayedFieldsCommand(this.Messaging, this.DelayedFields, variableCache);
422 resolveDelayedFieldsCommand.OutputType = this.Output.Type;
423 resolveDelayedFieldsCommand.DelayedFields = this.DelayedFields;
424 resolveDelayedFieldsCommand.ModularizationGuid = null;
425 resolveDelayedFieldsCommand.VariableCache = variableCache;
426 resolveDelayedFieldsCommand.Execute(); 372 resolveDelayedFieldsCommand.Execute();
427 } 373 }
428 374
429 // Set the overridable bundle provider key. 375 Dictionary<string, ProvidesDependencyTuple> dependencyTuplesByKey;
430 this.SetBundleProviderKey(this.Output, bundleRow); 376 {
377 var command = new ProcessDependencyProvidersCommand(this.Messaging, section, facades);
378 command.Execute();
431 379
432 // Import or generate dependency providers for packages in the manifest. 380 bundleTuple.ProviderKey = command.BundleProviderKey; // set the overridable bundle provider key.
433 this.ProcessDependencyProviders(this.Output, facades); 381 dependencyTuplesByKey = command.DependencyTuplesByKey;
382 }
434 383
435 // Update the bundle per-machine/per-user scope based on the chained packages. 384 // Update the bundle per-machine/per-user scope based on the chained packages.
436 this.ResolveBundleInstallScope(bundleRow, orderedFacades); 385 this.ResolveBundleInstallScope(section, bundleTuple, orderedFacades);
437 386
438 // Generate the core-defined BA manifest tables... 387 // Generate the core-defined BA manifest tables...
439 { 388 {
440 CreateBootstrapperApplicationManifestCommand command = new CreateBootstrapperApplicationManifestCommand(); 389 var command = new CreateBootstrapperApplicationManifestCommand(section, bundleTuple, orderedFacades, uxPayloadIndex, payloadTuples, this.IntermediateFolder);
441 command.BundleRow = bundleRow;
442 command.ChainPackages = orderedFacades;
443 command.LastUXPayloadIndex = uxPayloadIndex;
444 command.MsiFeatures = this.Output.Tables["WixBundleMsiFeature"].RowsAs<WixBundleMsiFeatureRow>();
445 command.Output = this.Output;
446 command.Payloads = payloads;
447 command.TableDefinitions = this.TableDefinitions;
448 command.TempFilesLocation = this.IntermediateFolder;
449 command.Execute(); 390 command.Execute();
450 391
451 WixBundlePayloadRow baManifestPayload = command.BootstrapperApplicationManifestPayloadRow; 392 var baManifestPayload = command.BootstrapperApplicationManifestPayloadRow;
452 payloads.Add(baManifestPayload); 393 payloadTuples.Add(baManifestPayload.Id.Id, baManifestPayload);
453 } 394 }
454 395
455 //foreach (BinderExtension extension in this.Extensions) 396#if TODO
456 //{ 397 foreach (BinderExtension extension in this.Extensions)
457 // extension.PostBind(this.Context); 398 {
458 //} 399 extension.PostBind(this.Context);
400 }
401#endif
459 402
460 // Create all the containers except the UX container first so the manifest (that goes in the UX container) 403 // Create all the containers except the UX container first so the manifest (that goes in the UX container)
461 // can contain all size and hash information about the non-UX containers. 404 // can contain all size and hash information about the non-UX containers.
462 RowDictionary<WixBundleContainerRow> containers = new RowDictionary<WixBundleContainerRow>(this.Output.Tables["WixBundleContainer"]); 405 WixBundleContainerTuple uxContainer;
463 406 IEnumerable<WixBundlePayloadTuple> uxPayloads;
464 ILookup<string, WixBundlePayloadRow> payloadsByContainer = payloads.Values.ToLookup(p => p.Container); 407 IEnumerable<WixBundleContainerTuple> containers;
465
466 int attachedContainerIndex = 1; // count starts at one because UX container is "0".
467
468 IEnumerable<WixBundlePayloadRow> uxContainerPayloads = Enumerable.Empty<WixBundlePayloadRow>();
469
470 foreach (WixBundleContainerRow container in containers.Values)
471 { 408 {
472 IEnumerable<WixBundlePayloadRow> containerPayloads = payloadsByContainer[container.Id]; 409 var command = new CreateNonUXContainers(this.BackendHelper, section, bundleApplicationTuple, payloadTuples, this.IntermediateFolder, layoutDirectory, this.DefaultCompressionLevel);
473 410 command.Execute();
474 if (!containerPayloads.Any())
475 {
476 if (container.Id != Compiler.BurnDefaultAttachedContainerId)
477 {
478 // TODO: display warning that we're ignoring container that ended up with no paylods in it.
479 }
480 }
481 else if (Compiler.BurnUXContainerId == container.Id)
482 {
483 container.WorkingPath = Path.Combine(this.IntermediateFolder, container.Name);
484 container.AttachedContainerIndex = 0;
485
486 // Gather the list of UX payloads but ensure the BootstrapperApplication Payload is the first
487 // in the list since that is the Payload that Burn attempts to load.
488 List<WixBundlePayloadRow> uxPayloads = new List<WixBundlePayloadRow>();
489
490 string baPayloadId = baRow.FieldAsString(0);
491
492 foreach (WixBundlePayloadRow uxPayload in containerPayloads)
493 {
494 if (uxPayload.Id == baPayloadId)
495 {
496 uxPayloads.Insert(0, uxPayload);
497 }
498 else
499 {
500 uxPayloads.Add(uxPayload);
501 }
502 }
503 411
504 uxContainerPayloads = uxPayloads; 412 fileTransfers.AddRange(command.FileTransfers);
505 }
506 else
507 {
508 container.WorkingPath = Path.Combine(this.IntermediateFolder, container.Name);
509 413
510 // Add detached containers to the list of file transfers. 414 uxContainer = command.UXContainer;
511 if (ContainerType.Detached == container.Type) 415 uxPayloads = command.UXContainerPayloads;
512 { 416 containers = command.Containers;
513 FileTransfer transfer; 417 }
514 if (FileTransfer.TryCreate(container.WorkingPath, Path.Combine(layoutDirectory, container.Name), true, "Container", container.SourceLineNumbers, out transfer)) 418
515 { 419 // Create the bundle manifest.
516 transfer.Built = true; 420 string manifestPath;
517 fileTransfers.Add(transfer); 421 {
518 } 422 var executableName = Path.GetFileName(this.OutputPath);
519 }
520 else // update the attached container index.
521 {
522 Debug.Assert(ContainerType.Attached == container.Type);
523 423
524 container.AttachedContainerIndex = attachedContainerIndex; 424 var command = new CreateBurnManifestCommand(this.Messaging, this.BackendExtensions, executableName, section, bundleTuple, containers, chainTuple, orderedFacades, boundaries, uxPayloads, payloadTuples, orderedSearches, catalogs, this.IntermediateFolder);
525 ++attachedContainerIndex; 425 command.Execute();
526 }
527 426
528 this.CreateContainer(container, containerPayloads, null); 427 manifestPath = command.OutputPath;
529 }
530 } 428 }
531 429
532 // Create the bundle manifest then UX container. 430 // Create the UX container.
533 string manifestPath = Path.Combine(this.IntermediateFolder, "bundle-manifest.xml");
534 { 431 {
535 var command = new CreateBurnManifestCommand(); 432 var command = new CreateContainerCommand(manifestPath, uxPayloads, uxContainer.WorkingPath, this.DefaultCompressionLevel);
536 command.BackendExtensions = this.BackendExtensions;
537 command.Output = this.Output;
538
539 command.BundleInfo = bundleRow;
540 command.Chain = chainRow;
541 command.Containers = containers;
542 command.Catalogs = catalogs;
543 command.ExecutableName = Path.GetFileName(this.OutputPath);
544 command.OrderedPackages = orderedFacades;
545 command.OutputPath = manifestPath;
546 command.RollbackBoundaries = boundaries;
547 command.OrderedSearches = orderedSearches;
548 command.Payloads = payloads;
549 command.UXContainerPayloads = uxContainerPayloads;
550 command.Execute(); 433 command.Execute();
551 }
552
553 WixBundleContainerRow uxContainer = containers[Compiler.BurnUXContainerId];
554 this.CreateContainer(uxContainer, uxContainerPayloads, manifestPath);
555
556 // Copy the burn.exe to a writable location then mark it to be moved to its final build location. Note
557 // that today, the x64 Burn uses the x86 stub.
558 string stubPlatform = (Platform.X64 == bundleRow.Platform) ? "x86" : bundleRow.Platform.ToString();
559
560 string stubFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), stubPlatform, "burn.exe");
561 string bundleTempPath = Path.Combine(this.IntermediateFolder, Path.GetFileName(this.OutputPath));
562
563 Messaging.Instance.OnMessage(WixVerboses.GeneratingBundle(bundleTempPath, stubFile));
564 434
565 string bundleFilename = Path.GetFileName(this.OutputPath); 435 uxContainer.Hash = command.Hash;
566 if ("setup.exe".Equals(bundleFilename, StringComparison.OrdinalIgnoreCase)) 436 uxContainer.Size = command.Size;
567 {
568 Messaging.Instance.OnMessage(WixErrors.InsecureBundleFilename(bundleFilename));
569 } 437 }
570 438
571 FileTransfer bundleTransfer;
572 if (FileTransfer.TryCreate(bundleTempPath, this.OutputPath, true, "Bundle", bundleRow.SourceLineNumbers, out bundleTransfer))
573 { 439 {
574 bundleTransfer.Built = true; 440 var command = new CreateBundleExeCommand(this.Messaging, this.BackendHelper, this.IntermediateFolder, this.OutputPath, bundleTuple, uxContainer, containers, this.BurnStubPath);
575 fileTransfers.Add(bundleTransfer); 441 command.Execute();
576 }
577 442
578 File.Copy(stubFile, bundleTempPath, true); 443 fileTransfers.Add(command.Transfer);
579 File.SetAttributes(bundleTempPath, FileAttributes.Normal); 444 }
580 445
581 this.UpdateBurnResources(bundleTempPath, this.OutputPath, bundleRow); 446#if TODO
447 this.Pdb = new Pdb { Output = output };
582 448
583 // Update the .wixburn section to point to at the UX and attached container(s) then attach the containers 449 if (!String.IsNullOrEmpty(this.OutputPdbPath))
584 // if they should be attached.
585 using (BurnWriter writer = BurnWriter.Open(bundleTempPath))
586 { 450 {
587 FileInfo burnStubFile = new FileInfo(bundleTempPath); 451 var trackPdb = this.BackendHelper.TrackFile(this.OutputPdbPath, TrackedFileType.Final);
588 writer.InitializeBundleSectionData(burnStubFile.Length, bundleRow.BundleId); 452 trackedFiles.Add(trackPdb);
589
590 // Always attach the UX container first
591 writer.AppendContainer(uxContainer.WorkingPath, BurnWriter.Container.UX);
592 453
593 // Now append all other attached containers 454 this.Pdb.Save(trackPdb.Path);
594 foreach (WixBundleContainerRow container in containers.Values)
595 {
596 if (ContainerType.Attached == container.Type)
597 {
598 // The container was only created if it had payloads.
599 if (!String.IsNullOrEmpty(container.WorkingPath) && Compiler.BurnUXContainerId != container.Id)
600 {
601 writer.AppendContainer(container.WorkingPath, BurnWriter.Container.Attached);
602 }
603 }
604 }
605 }
606
607 if (null != this.PdbFile)
608 {
609 Pdb pdb = new Pdb();
610 pdb.Output = Output;
611 pdb.Save(this.PdbFile);
612 } 455 }
456#endif
613 457
458#if TODO // does this need to come back, or do they only need to be in TrackedFiles?
459 this.ContentFilePaths = payloadTuples.Values.Where(p => p.ContentFile).Select(p => p.FullFileName).ToList();
460#endif
614 this.FileTransfers = fileTransfers; 461 this.FileTransfers = fileTransfers;
615 this.ContentFilePaths = payloads.Values.Where(p => p.ContentFile).Select(p => p.FullFileName).ToList(); 462 this.TrackedFiles = trackedFiles;
616 }
617
618 private Table GetRequiredTable(string tableName)
619 {
620 Table table = this.Output.Tables[tableName];
621 if (null == table || 0 == table.Rows.Count)
622 {
623 throw new WixException(WixErrors.MissingBundleInformation(tableName));
624 }
625
626 return table;
627 }
628
629 private Row GetSingleRowTable(string tableName)
630 {
631 Table table = this.Output.Tables[tableName];
632 if (null == table || 1 != table.Rows.Count)
633 {
634 throw new WixException(WixErrors.MissingBundleInformation(tableName));
635 }
636 463
637 return table.Rows[0]; 464 // TODO: Eventually this gets removed
465 var intermediate = new Intermediate(this.Output.Id, new[] { section }, this.Output.Localizations.ToDictionary(l => l.Culture, StringComparer.OrdinalIgnoreCase), this.Output.EmbedFilePaths);
466 var trackIntermediate = this.BackendHelper.TrackFile(Path.Combine(this.IntermediateFolder, Path.GetFileName(Path.ChangeExtension(this.OutputPath, "wir"))), TrackedFileType.Intermediate);
467 intermediate.Save(trackIntermediate.Path);
468 trackedFiles.Add(trackIntermediate);
638 } 469 }
639 470
640 private List<WixSearchInfo> OrderSearches() 471 private IEnumerable<SearchFacade> OrderSearches(IntermediateSection section)
641 { 472 {
642 Dictionary<string, WixSearchInfo> allSearches = new Dictionary<string, WixSearchInfo>(); 473 var searchesById = section.Tuples
643 Table wixFileSearchTable = this.Output.Tables["WixFileSearch"]; 474 .Where(t => t.Definition.Type == TupleDefinitionType.WixComponentSearch ||
644 if (null != wixFileSearchTable && 0 < wixFileSearchTable.Rows.Count) 475 t.Definition.Type == TupleDefinitionType.WixFileSearch ||
645 { 476 t.Definition.Type == TupleDefinitionType.WixProductSearch ||
646 foreach (Row row in wixFileSearchTable.Rows) 477 t.Definition.Type == TupleDefinitionType.WixRegistrySearch)
647 { 478 .ToDictionary(t => t.Id.Id);
648 WixFileSearchInfo fileSearchInfo = new WixFileSearchInfo(row);
649 allSearches.Add(fileSearchInfo.Id, fileSearchInfo);
650 }
651 }
652
653 Table wixRegistrySearchTable = this.Output.Tables["WixRegistrySearch"];
654 if (null != wixRegistrySearchTable && 0 < wixRegistrySearchTable.Rows.Count)
655 {
656 foreach (Row row in wixRegistrySearchTable.Rows)
657 {
658 WixRegistrySearchInfo registrySearchInfo = new WixRegistrySearchInfo(row);
659 allSearches.Add(registrySearchInfo.Id, registrySearchInfo);
660 }
661 }
662
663 Table wixComponentSearchTable = this.Output.Tables["WixComponentSearch"];
664 if (null != wixComponentSearchTable && 0 < wixComponentSearchTable.Rows.Count)
665 {
666 foreach (Row row in wixComponentSearchTable.Rows)
667 {
668 WixComponentSearchInfo componentSearchInfo = new WixComponentSearchInfo(row);
669 allSearches.Add(componentSearchInfo.Id, componentSearchInfo);
670 }
671 }
672 479
673 Table wixProductSearchTable = this.Output.Tables["WixProductSearch"]; 480 var orderedSearches = new List<SearchFacade>(searchesById.Keys.Count);
674 if (null != wixProductSearchTable && 0 < wixProductSearchTable.Rows.Count)
675 {
676 foreach (Row row in wixProductSearchTable.Rows)
677 {
678 WixProductSearchInfo productSearchInfo = new WixProductSearchInfo(row);
679 allSearches.Add(productSearchInfo.Id, productSearchInfo);
680 }
681 }
682 481
683 // Merge in the variable/condition info and get the canonical ordering for 482 foreach (var searchTuple in section.Tuples.OfType<WixSearchTuple>())
684 // the searches.
685 List<WixSearchInfo> orderedSearches = new List<WixSearchInfo>();
686 Table wixSearchTable = this.Output.Tables["WixSearch"];
687 if (null != wixSearchTable && 0 < wixSearchTable.Rows.Count)
688 { 483 {
689 orderedSearches.Capacity = wixSearchTable.Rows.Count; 484 if (searchesById.TryGetValue(searchTuple.Id.Id, out var specificSearchTuple))
690 foreach (Row row in wixSearchTable.Rows)
691 { 485 {
692 WixSearchInfo searchInfo = allSearches[(string)row[0]]; 486 orderedSearches.Add(new SearchFacade(searchTuple, specificSearchTuple));
693 searchInfo.AddWixSearchRowInfo(row);
694 orderedSearches.Add(searchInfo);
695 } 487 }
696 } 488 }
697 489
@@ -703,9 +495,9 @@ namespace WixToolset.Core.Burn
703 /// </summary> 495 /// </summary>
704 /// <param name="package">The package with properties to cache.</param> 496 /// <param name="package">The package with properties to cache.</param>
705 /// <param name="variableCache">The property cache.</param> 497 /// <param name="variableCache">The property cache.</param>
706 private static void PopulatePackageVariableCache(WixBundlePackageRow package, IDictionary<string, string> variableCache) 498 private static void PopulatePackageVariableCache(WixBundlePackageTuple package, IDictionary<string, string> variableCache)
707 { 499 {
708 string id = package.WixChainItemId; 500 var id = package.Id.Id;
709 501
710 variableCache.Add(String.Concat("packageDescription.", id), package.Description); 502 variableCache.Add(String.Concat("packageDescription.", id), package.Description);
711 //variableCache.Add(String.Concat("packageLanguage.", id), package.Language); 503 //variableCache.Add(String.Concat("packageLanguage.", id), package.Language);
@@ -714,231 +506,63 @@ namespace WixToolset.Core.Burn
714 variableCache.Add(String.Concat("packageVersion.", id), package.Version); 506 variableCache.Add(String.Concat("packageVersion.", id), package.Version);
715 } 507 }
716 508
717 private void CreateContainer(WixBundleContainerRow container, IEnumerable<WixBundlePayloadRow> containerPayloads, string manifestFile) 509 private void ResolveBundleInstallScope(IntermediateSection section, WixBundleTuple bundleTuple, IEnumerable<PackageFacade> facades)
718 { 510 {
719 CreateContainerCommand command = new CreateContainerCommand(); 511 var dependencyTuplesById = section.Tuples.OfType<ProvidesDependencyTuple>().ToDictionary(t => t.Id.Id);
720 command.DefaultCompressionLevel = this.DefaultCompressionLevel;
721 command.Payloads = containerPayloads;
722 command.ManifestFile = manifestFile;
723 command.OutputPath = container.WorkingPath;
724 command.Execute();
725
726 container.Hash = command.Hash;
727 container.Size = command.Size;
728 }
729 512
730 private void ResolveBundleInstallScope(WixBundleRow bundleInfo, IEnumerable<PackageFacade> facades) 513 foreach (var facade in facades)
731 {
732 foreach (PackageFacade facade in facades)
733 { 514 {
734 if (bundleInfo.PerMachine && YesNoDefaultType.No == facade.Package.PerMachine) 515 if (bundleTuple.PerMachine && YesNoDefaultType.No == facade.PackageTuple.PerMachine)
735 { 516 {
736 Messaging.Instance.OnMessage(WixVerboses.SwitchingToPerUserPackage(facade.Package.SourceLineNumbers, facade.Package.WixChainItemId)); 517 this.Messaging.Write(VerboseMessages.SwitchingToPerUserPackage(facade.PackageTuple.SourceLineNumbers, facade.PackageId));
737 518
738 bundleInfo.PerMachine = false; 519 bundleTuple.Attributes &= ~WixBundleAttributes.PerMachine;
739 break; 520 break;
740 } 521 }
741 } 522 }
742 523
743 foreach (PackageFacade facade in facades) 524 foreach (var facade in facades)
744 { 525 {
745 // Update package scope from bundle scope if default. 526 // Update package scope from bundle scope if default.
746 if (YesNoDefaultType.Default == facade.Package.PerMachine) 527 if (YesNoDefaultType.Default == facade.PackageTuple.PerMachine)
747 { 528 {
748 facade.Package.PerMachine = bundleInfo.PerMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No; 529 facade.PackageTuple.PerMachine = bundleTuple.PerMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No;
749 } 530 }
750 531
751 // We will only register packages in the same scope as the bundle. Warn if any packages with providers 532 // We will only register packages in the same scope as the bundle. Warn if any packages with providers
752 // are in a different scope and not permanent (permanents typically don't need a ref-count). 533 // are in a different scope and not permanent (permanents typically don't need a ref-count).
753 if (!bundleInfo.PerMachine && YesNoDefaultType.Yes == facade.Package.PerMachine && !facade.Package.Permanent && 0 < facade.Provides.Count) 534 if (!bundleTuple.PerMachine &&
535 YesNoDefaultType.Yes == facade.PackageTuple.PerMachine &&
536 !facade.PackageTuple.Permanent &&
537 dependencyTuplesById.ContainsKey(facade.PackageId))
754 { 538 {
755 Messaging.Instance.OnMessage(WixWarnings.NoPerMachineDependencies(facade.Package.SourceLineNumbers, facade.Package.WixChainItemId)); 539 this.Messaging.Write(WarningMessages.NoPerMachineDependencies(facade.PackageTuple.SourceLineNumbers, facade.PackageId));
756 } 540 }
757 } 541 }
758 } 542 }
759 543
760 private void UpdateBurnResources(string bundleTempPath, string outputPath, WixBundleRow bundleInfo) 544 private IEnumerable<T> GetRequiredTuples<T>() where T : IntermediateTuple
761 { 545 {
762 WixToolset.Dtf.Resources.ResourceCollection resources = new WixToolset.Dtf.Resources.ResourceCollection(); 546 var tuples = this.Output.Sections.Single().Tuples.OfType<T>().ToList();
763 WixToolset.Dtf.Resources.VersionResource version = new WixToolset.Dtf.Resources.VersionResource("#1", 1033);
764 547
765 version.Load(bundleTempPath); 548 if (0 == tuples.Count)
766 resources.Add(version);
767
768 // Ensure the bundle info provides a full four part version.
769 Version fourPartVersion = new Version(bundleInfo.Version);
770 int major = (fourPartVersion.Major < 0) ? 0 : fourPartVersion.Major;
771 int minor = (fourPartVersion.Minor < 0) ? 0 : fourPartVersion.Minor;
772 int build = (fourPartVersion.Build < 0) ? 0 : fourPartVersion.Build;
773 int revision = (fourPartVersion.Revision < 0) ? 0 : fourPartVersion.Revision;
774
775 if (UInt16.MaxValue < major || UInt16.MaxValue < minor || UInt16.MaxValue < build || UInt16.MaxValue < revision)
776 {
777 throw new WixException(WixErrors.InvalidModuleOrBundleVersion(bundleInfo.SourceLineNumbers, "Bundle", bundleInfo.Version));
778 }
779
780 fourPartVersion = new Version(major, minor, build, revision);
781 version.FileVersion = fourPartVersion;
782 version.ProductVersion = fourPartVersion;
783
784 WixToolset.Dtf.Resources.VersionStringTable strings = version[1033];
785 strings["LegalCopyright"] = bundleInfo.Copyright;
786 strings["OriginalFilename"] = Path.GetFileName(outputPath);
787 strings["FileVersion"] = bundleInfo.Version; // string versions do not have to be four parts.
788 strings["ProductVersion"] = bundleInfo.Version; // string versions do not have to be four parts.
789
790 if (!String.IsNullOrEmpty(bundleInfo.Name))
791 { 549 {
792 strings["ProductName"] = bundleInfo.Name; 550 throw new WixException(ErrorMessages.MissingBundleInformation(nameof(T)));
793 strings["FileDescription"] = bundleInfo.Name;
794 } 551 }
795 552
796 if (!String.IsNullOrEmpty(bundleInfo.Publisher)) 553 return tuples;
797 {
798 strings["CompanyName"] = bundleInfo.Publisher;
799 }
800 else
801 {
802 strings["CompanyName"] = String.Empty;
803 }
804
805 if (!String.IsNullOrEmpty(bundleInfo.IconPath))
806 {
807 Dtf.Resources.GroupIconResource iconGroup = new Dtf.Resources.GroupIconResource("#1", 1033);
808 iconGroup.ReadFromFile(bundleInfo.IconPath);
809 resources.Add(iconGroup);
810
811 foreach (Dtf.Resources.Resource icon in iconGroup.Icons)
812 {
813 resources.Add(icon);
814 }
815 }
816
817 if (!String.IsNullOrEmpty(bundleInfo.SplashScreenBitmapPath))
818 {
819 Dtf.Resources.BitmapResource bitmap = new Dtf.Resources.BitmapResource("#1", 1033);
820 bitmap.ReadFromFile(bundleInfo.SplashScreenBitmapPath);
821 resources.Add(bitmap);
822 }
823
824 resources.Save(bundleTempPath);
825 } 554 }
826 555
827//#region DependencyExtension 556 private T GetSingleTuple<T>() where T : IntermediateTuple
828 /// <summary>
829 /// Imports authored dependency providers for each package in the manifest,
830 /// and generates dependency providers for certain package types that do not
831 /// have a provider defined.
832 /// </summary>
833 /// <param name="bundle">The <see cref="Output"/> object for the bundle.</param>
834 /// <param name="facades">An indexed collection of chained packages.</param>
835 private void ProcessDependencyProviders(Output bundle, IDictionary<string, PackageFacade> facades)
836 { 557 {
837 // First import any authored dependencies. These may merge with imported provides from MSI packages. 558 var tuples = this.Output.Sections.Single().Tuples.OfType<T>().ToList();
838 Table wixDependencyProviderTable = bundle.Tables["WixDependencyProvider"];
839 if (null != wixDependencyProviderTable && 0 < wixDependencyProviderTable.Rows.Count)
840 {
841 // Add package information for each dependency provider authored into the manifest.
842 foreach (Row wixDependencyProviderRow in wixDependencyProviderTable.Rows)
843 {
844 string packageId = (string)wixDependencyProviderRow[1];
845
846 PackageFacade facade = null;
847 if (facades.TryGetValue(packageId, out facade))
848 {
849 ProvidesDependency dependency = new ProvidesDependency(wixDependencyProviderRow);
850
851 if (String.IsNullOrEmpty(dependency.Key))
852 {
853 switch (facade.Package.Type)
854 {
855 // The WixDependencyExtension allows an empty Key for MSIs and MSPs.
856 case WixBundlePackageType.Msi:
857 dependency.Key = facade.MsiPackage.ProductCode;
858 break;
859 case WixBundlePackageType.Msp:
860 dependency.Key = facade.MspPackage.PatchCode;
861 break;
862 }
863 }
864
865 if (String.IsNullOrEmpty(dependency.Version))
866 {
867 dependency.Version = facade.Package.Version;
868 }
869
870 // If the version is still missing, a version could not be harvested from the package and was not authored.
871 if (String.IsNullOrEmpty(dependency.Version))
872 {
873 Messaging.Instance.OnMessage(WixErrors.MissingDependencyVersion(facade.Package.WixChainItemId));
874 }
875
876 if (String.IsNullOrEmpty(dependency.DisplayName))
877 {
878 dependency.DisplayName = facade.Package.DisplayName;
879 }
880 559
881 if (!facade.Provides.Merge(dependency)) 560 if (1 != tuples.Count)
882 {
883 Messaging.Instance.OnMessage(WixErrors.DuplicateProviderDependencyKey(dependency.Key, facade.Package.WixChainItemId));
884 }
885 }
886 }
887 }
888
889 // Generate providers for MSI packages that still do not have providers.
890 foreach (PackageFacade facade in facades.Values)
891 { 561 {
892 string key = null; 562 throw new WixException(ErrorMessages.MissingBundleInformation(nameof(T)));
893
894 if (WixBundlePackageType.Msi == facade.Package.Type && 0 == facade.Provides.Count)
895 {
896 key = facade.MsiPackage.ProductCode;
897 }
898 else if (WixBundlePackageType.Msp == facade.Package.Type && 0 == facade.Provides.Count)
899 {
900 key = facade.MspPackage.PatchCode;
901 }
902
903 if (!String.IsNullOrEmpty(key))
904 {
905 ProvidesDependency dependency = new ProvidesDependency(key, facade.Package.Version, facade.Package.DisplayName, 0);
906
907 if (!facade.Provides.Merge(dependency))
908 {
909 Messaging.Instance.OnMessage(WixErrors.DuplicateProviderDependencyKey(dependency.Key, facade.Package.WixChainItemId));
910 }
911 }
912 } 563 }
913 }
914 564
915 /// <summary> 565 return tuples[0];
916 /// Sets the provider key for the bundle.
917 /// </summary>
918 /// <param name="bundle">The <see cref="Output"/> object for the bundle.</param>
919 /// <param name="bundleInfo">The <see cref="BundleInfo"/> containing the provider key and other information for the bundle.</param>
920 private void SetBundleProviderKey(Output bundle, WixBundleRow bundleInfo)
921 {
922 // From DependencyCommon.cs in the WixDependencyExtension.
923 const int ProvidesAttributesBundle = 0x10000;
924
925 Table wixDependencyProviderTable = bundle.Tables["WixDependencyProvider"];
926 if (null != wixDependencyProviderTable && 0 < wixDependencyProviderTable.Rows.Count)
927 {
928 // Search the WixDependencyProvider table for the single bundle provider key.
929 foreach (Row wixDependencyProviderRow in wixDependencyProviderTable.Rows)
930 {
931 object attributes = wixDependencyProviderRow[5];
932 if (null != attributes && 0 != (ProvidesAttributesBundle & (int)attributes))
933 {
934 bundleInfo.ProviderKey = (string)wixDependencyProviderRow[2];
935 break;
936 }
937 }
938 }
939
940 // Defaults to the bundle ID as the provider key.
941#endif
942 } 566 }
943 } 567 }
944} 568}