@joebigelow / wix / commits / 860676fa

Implement Bundle build

Rob Mensching committed Oct 7, 2019 at 11:18 UTC 860676fa5b40a1904478151e9b4934c004e7db63
62 files changed +2240 -2151
appveyor.cmd
+2 -2
@@ -2,13 +2,13 @@
2 @pushd %~dp0
3 @set _P=%~dp0build\Release\publish
4
5 +dotnet test -c Release src\test\WixToolsetTest.CoreIntegration
6 +
7 dotnet pack -c Release src\WixToolset.Core
8 dotnet pack -c Release src\WixToolset.Core.Burn
9 dotnet pack -c Release src\WixToolset.Core.WindowsInstaller
10
11 dotnet pack -c Release src\WixToolset.Core.TestPackage
12
11 -dotnet build -c Release src\test\WixToolsetTest.CoreIntegration
12 -
13 @popd
14 @endlocal
src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+242 -618
@@ -8,78 +8,51 @@ namespace WixToolset.Core.Burn
8 using System.Globalization;
9 using System.IO;
10 using System.Linq;
11 - using System.Reflection;
11 using WixToolset.Core.Bind;
12 + using WixToolset.Core.Burn.Bind;
13 using WixToolset.Core.Burn.Bundles;
14 using WixToolset.Data;
15 - using WixToolset.Data.Bind;
15 + using WixToolset.Data.Burn;
16 + using WixToolset.Data.Tuples;
17 using WixToolset.Extensibility;
18 using WixToolset.Extensibility.Data;
19 using WixToolset.Extensibility.Services;
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 -
21 /// <summary>
22 /// Binds a this.bundle.
23 /// </summary>
24 internal class BindBundleCommand
25 {
70 - public BindBundleCommand(IBindContext context)
26 + public BindBundleCommand(IBindContext context, IEnumerable<IBurnBackendExtension> backedExtensions)
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>();
33
34 + this.BurnStubPath = context.BurnStubPath;
35 + this.DefaultCompressionLevel = context.DefaultCompressionLevel;
36 this.DelayedFields = context.DelayedFields;
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;
43
77 - var extensionManager = context.ServiceProvider.GetService<IExtensionManager>();
78 -
79 - this.BackendExtensions = extensionManager.GetServices<IBurnBackendExtension>();
44 + this.BackendExtensions = backedExtensions;
45 }
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; }
56
57 public IEnumerable<IDelayedField> DelayedFields { get; }
58
@@ -87,17 +60,15 @@ namespace WixToolset.Core.Burn
60
61 private IEnumerable<IBurnBackendExtension> BackendExtensions { get; }
62
90 - public Intermediate Output { private get; set; }
91 -
92 - public string OutputPath { private get; set; }
63 + private Intermediate Output { get; }
64
94 - public string PdbFile { private get; set; }
65 + private string OutputPath { get; }
66
96 - //public TableDefinitionCollection TableDefinitions { private get; set; }
67 + private string OutputPdbPath { get; }
68
98 - public string IntermediateFolder { private get; set; }
69 + private string IntermediateFolder { get; }
70
100 - public IVariableResolver VariableResolver { private get; set; }
71 + private IVariableResolver VariableResolver { get; }
72
73 public IEnumerable<IFileTransfer> FileTransfers { get; private set; }
74
@@ -105,196 +76,177 @@ namespace WixToolset.Core.Burn
76
77 public void Execute()
78 {
108 - throw new NotImplementedException();
109 -#if TODO
110 - this.FileTransfers = Enumerable.Empty<FileTransfer>();
111 - this.ContentFilePaths = Enumerable.Empty<string>();
79 + var section = this.Output.Sections.Single();
80 +
81 + var fileTransfers = new List<IFileTransfer>();
82 + var trackedFiles = new List<ITrackedFile>();
83
84 // First look for data we expect to find... Chain, WixGroups, etc.
85
86 // We shouldn't really get past the linker phase if there are
87 // no group items... that means that there's no UX, no Chain,
88 // *and* no Containers!
118 - Table chainPackageTable = this.GetRequiredTable("WixBundlePackage");
89 + var chainPackageTuples = this.GetRequiredTuples<WixBundlePackageTuple>();
90
120 - Table wixGroupTable = this.GetRequiredTable("WixGroup");
91 + var wixGroupTuples = this.GetRequiredTuples<WixGroupTuple>();
92
93 // Ensure there is one and only one row in the WixBundle table.
94 // The compiler and linker behavior should have colluded to get
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();
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.
101
102 // Ensure there is one and only one row in the WixBootstrapperApplication table.
103 // The compiler and linker behavior should have colluded to get
104 // this behavior.
132 - Row baRow = this.GetSingleRowTable("WixBootstrapperApplication");
105 + var bundleApplicationTuple = this.GetSingleTuple<WixBootstrapperApplicationTuple>();
106
107 // Ensure there is one and only one row in the WixChain table.
108 // The compiler and linker behavior should have colluded to get
109 // this behavior.
137 - WixChainRow chainRow = (WixChainRow)this.GetSingleRowTable("WixChain");
110 + var chainTuple = this.GetSingleTuple<WixChainTuple>();
111
139 - if (Messaging.Instance.EncounteredError)
112 + if (this.Messaging.EncounteredError)
113 {
114 return;
115 }
116
117 // If there are any fields to resolve later, create the cache to populate during bind.
145 - IDictionary<string, string> variableCache = null;
146 - if (this.DelayedFields.Any())
147 - {
148 - variableCache = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
149 - }
118 + var variableCache = this.DelayedFields.Any() ? new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) : null;
119
120 // TODO: Although the WixSearch tables are defined in the Util extension,
121 // the Bundle Binder has to know all about them. We hope to revisit all
122 // of this in the 4.0 timeframe.
154 - IEnumerable<WixSearchInfo> orderedSearches = this.OrderSearches();
123 + var orderedSearches = this.OrderSearches(section);
124
125 +#if THIS_SHOULD_BE_DELETED_SINCE_RESOLVE_DOES_THIS_NOW
126 // Extract files that come from cabinet files (this does not extract files from merge modules).
127 {
128 var extractEmbeddedFilesCommand = new ExtractEmbeddedFilesCommand();
129 extractEmbeddedFilesCommand.FilesWithEmbeddedFiles = ExpectedEmbeddedFiles;
130 extractEmbeddedFilesCommand.Execute();
131 }
132 +#endif
133
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);
136
137 // Update explicitly authored payloads with their parent package and container (as appropriate)
138 // to make it easier to gather the payloads later.
168 - foreach (WixGroupRow row in wixGroupTable.RowsAs<WixGroupRow>())
139 + foreach (var groupTuple in wixGroupTuples)
140 {
170 - if (ComplexReferenceChildType.Payload == row.ChildType)
141 + if (ComplexReferenceChildType.Payload == groupTuple.ChildType)
142 {
172 - WixBundlePayloadRow payload = payloads.Get(row.ChildId);
143 + var payloadTuple = payloadTuples[groupTuple.ChildId];
144
174 - if (ComplexReferenceParentType.Package == row.ParentType)
145 + if (ComplexReferenceParentType.Package == groupTuple.ParentType)
146 {
176 - Debug.Assert(String.IsNullOrEmpty(payload.Package));
177 - payload.Package = row.ParentId;
147 + Debug.Assert(String.IsNullOrEmpty(payloadTuple.PackageRef));
148 + payloadTuple.PackageRef = groupTuple.ParentId;
149 }
179 - else if (ComplexReferenceParentType.Container == row.ParentType)
150 + else if (ComplexReferenceParentType.Container == groupTuple.ParentType)
151 {
181 - Debug.Assert(String.IsNullOrEmpty(payload.Container));
182 - payload.Container = row.ParentId;
152 + Debug.Assert(String.IsNullOrEmpty(payloadTuple.ContainerRef));
153 + payloadTuple.ContainerRef = groupTuple.ParentId;
154 }
184 - else if (ComplexReferenceParentType.Layout == row.ParentType)
155 + else if (ComplexReferenceParentType.Layout == groupTuple.ParentType)
156 {
186 - payload.LayoutOnly = true;
157 + payloadTuple.LayoutOnly = true;
158 }
159 }
160 }
161
191 - List<FileTransfer> fileTransfers = new List<FileTransfer>();
192 - string layoutDirectory = Path.GetDirectoryName(this.OutputPath);
162 + var layoutDirectory = Path.GetDirectoryName(this.OutputPath);
163
164 // Process the explicitly authored payloads.
165 ISet<string> processedPayloads;
166 {
197 - ProcessPayloadsCommand command = new ProcessPayloadsCommand();
198 - command.Payloads = payloads.Values;
199 - command.DefaultPackaging = bundleRow.DefaultPackagingType;
200 - command.LayoutDirectory = layoutDirectory;
167 + var command = new ProcessPayloadsCommand(this.ServiceProvider, this.BackendHelper, payloadTuples.Values, bundleTuple.DefaultPackagingType, layoutDirectory);
168 command.Execute();
169
170 fileTransfers.AddRange(command.FileTransfers);
171
205 - processedPayloads = new HashSet<string>(payloads.Keys);
172 + processedPayloads = new HashSet<string>(payloadTuples.Keys);
173 }
174
175 IDictionary<string, PackageFacade> facades;
176 {
210 - GetPackageFacadesCommand command = new GetPackageFacadesCommand();
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"];
177 + var command = new GetPackageFacadesCommand(chainPackageTuples, section);
178 command.Execute();
179
180 facades = command.PackageFacades;
181 }
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
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)
186 {
225 - switch (facade.Package.Type)
187 + switch (facade.PackageTuple.Type)
188 {
227 - case WixBundlePackageType.Exe:
228 - {
229 - ProcessExePackageCommand command = new ProcessExePackageCommand();
230 - command.AuthoredPayloads = payloads;
231 - command.Facade = facade;
232 - command.Execute();
233 -
234 - // ? variableCache.Add(String.Concat("packageManufacturer.", facade.Package.WixChainItemId), facade.ExePackage.Manufacturer);
235 - }
236 - break;
189 + case WixBundlePackageType.Exe:
190 + {
191 + var command = new ProcessExePackageCommand(facade, payloadTuples);
192 + command.Execute();
193 + }
194 + break;
195
238 - case WixBundlePackageType.Msi:
239 - {
240 - var command = new ProcessMsiPackageCommand();
241 - command.AuthoredPayloads = payloads;
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 - }
196 + case WixBundlePackageType.Msi:
197 + {
198 + var command = new ProcessMsiPackageCommand(this.ServiceProvider, this.BackendExtensions, section, facade, payloadTuples);
199 + command.Execute();
200
260 - }
261 - break;
201 + if (null != variableCache)
202 + {
203 + var msiPackage = (WixBundleMsiPackageTuple)facade.SpecificPackageTuple;
204 + variableCache.Add(String.Concat("packageLanguage.", facade.PackageId), msiPackage.ProductLanguage.ToString());
205
263 - case WixBundlePackageType.Msp:
206 + if (null != msiPackage.Manufacturer)
207 {
265 - ProcessMspPackageCommand command = new ProcessMspPackageCommand();
266 - command.AuthoredPayloads = payloads;
267 - command.Facade = facade;
268 - command.WixBundlePatchTargetCodeTable = this.Output.EnsureTable(this.TableDefinitions["WixBundlePatchTargetCode"]);
269 - command.Execute();
208 + variableCache.Add(String.Concat("packageManufacturer.", facade.PackageId), msiPackage.Manufacturer);
209 }
271 - break;
210 + }
211
273 - case WixBundlePackageType.Msu:
274 - {
275 - ProcessMsuPackageCommand command = new ProcessMsuPackageCommand();
276 - command.Facade = facade;
277 - command.Execute();
278 - }
279 - break;
212 + }
213 + break;
214 +
215 + case WixBundlePackageType.Msp:
216 + {
217 + var command = new ProcessMspPackageCommand(this.Messaging, section, facade, payloadTuples);
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;
228 }
229
230 if (null != variableCache)
231 {
284 - BindBundleCommand.PopulatePackageVariableCache(facade.Package, variableCache);
232 + BindBundleCommand.PopulatePackageVariableCache(facade.PackageTuple, variableCache);
233 }
234 }
235
236 + if (this.Messaging.EncounteredError)
237 + {
238 + return;
239 + }
240 +
241 // Reindex the payloads now that all the payloads (minus the manifest payloads that will be created later)
242 // are present.
290 - payloads = new RowDictionary<WixBundlePayloadRow>(this.Output.Tables["WixBundlePayload"]);
243 + payloadTuples = section.Tuples.OfType<WixBundlePayloadTuple>().ToDictionary(t => t.Id.Id);
244
245 // Process the payloads that were added by processing the packages.
246 {
294 - ProcessPayloadsCommand command = new ProcessPayloadsCommand();
295 - command.Payloads = payloads.Values.Where(r => !processedPayloads.Contains(r.Id)).ToList();
296 - command.DefaultPackaging = bundleRow.DefaultPackagingType;
297 - command.LayoutDirectory = layoutDirectory;
247 + var toProcess = payloadTuples.Values.Where(r => !processedPayloads.Contains(r.Id.Id)).ToList();
248 +
249 + var command = new ProcessPayloadsCommand(this.ServiceProvider, this.BackendHelper, toProcess, bundleTuple.DefaultPackagingType, layoutDirectory);
250 command.Execute();
251
252 fileTransfers.AddRange(command.FileTransfers);
@@ -303,45 +255,43 @@ namespace WixToolset.Core.Burn
255 }
256
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 -
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)
262 {
311 - facade.Package.Size = 0;
263 + facade.PackageTuple.Size = 0;
264
313 - IEnumerable<WixBundlePayloadRow> packagePayloads = payloadsByPackage[facade.Package.WixChainItemId];
265 + var packagePayloads = payloadsByPackageId[facade.PackageId];
266
315 - foreach (WixBundlePayloadRow payload in packagePayloads)
267 + foreach (var payload in packagePayloads)
268 {
317 - facade.Package.Size += payload.FileSize;
269 + facade.PackageTuple.Size += payload.FileSize;
270 }
271
320 - if (!facade.Package.InstallSize.HasValue)
272 + if (!facade.PackageTuple.InstallSize.HasValue)
273 {
322 - facade.Package.InstallSize = facade.Package.Size;
323 -
274 + facade.PackageTuple.InstallSize = facade.PackageTuple.Size;
275 }
276
326 - WixBundlePayloadRow packagePayload = payloads[facade.Package.PackagePayload];
277 + var packagePayload = payloadTuples[facade.PackageTuple.PayloadRef];
278
328 - if (String.IsNullOrEmpty(facade.Package.Description))
279 + if (String.IsNullOrEmpty(facade.PackageTuple.Description))
280 {
330 - facade.Package.Description = packagePayload.Description;
281 + facade.PackageTuple.Description = packagePayload.Description;
282 }
283
333 - if (String.IsNullOrEmpty(facade.Package.DisplayName))
284 + if (String.IsNullOrEmpty(facade.PackageTuple.DisplayName))
285 {
335 - facade.Package.DisplayName = packagePayload.DisplayName;
286 + facade.PackageTuple.DisplayName = packagePayload.DisplayName;
287 }
288 }
289 }
290
340 -
291 // Give the UX payloads their embedded IDs...
342 - int uxPayloadIndex = 0;
292 + var uxPayloadIndex = 0;
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))
295 {
296 // In theory, UX payloads could be embedded in the UX CAB, external to the bundle EXE, or even
297 // downloaded. The current engine requires the UX to be fully present before any downloading starts,
@@ -349,7 +299,7 @@ namespace WixToolset.Core.Burn
299 // into the temporary UX directory correctly, so we don't allow external either.
300 if (PackagingType.Embedded != payload.Packaging)
301 {
352 - Messaging.Instance.OnMessage(WixWarnings.UxPayloadsOnlySupportEmbedding(payload.SourceLineNumbers, payload.FullFileName));
302 + this.Messaging.Write(WarningMessages.UxPayloadsOnlySupportEmbedding(payload.SourceLineNumbers, payload.SourceFile.Path));
303 payload.Packaging = PackagingType.Embedded;
304 }
305
@@ -360,12 +310,12 @@ namespace WixToolset.Core.Burn
310 if (0 == uxPayloadIndex)
311 {
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"));
314 }
315
316 // Give the embedded payloads without an embedded id yet an embedded id.
367 - int payloadIndex = 0;
368 - foreach (WixBundlePayloadRow payload in payloads.Values)
317 + var payloadIndex = 0;
318 + foreach (var payload in payloadTuples.Values)
319 {
320 Debug.Assert(PackagingType.Unknown != payload.Packaging);
321
@@ -377,38 +327,38 @@ namespace WixToolset.Core.Burn
327 }
328 }
329
330 + if (this.Messaging.EncounteredError)
331 + {
332 + return;
333 + }
334 +
335 // Determine patches to automatically slipstream.
336 {
382 - AutomaticallySlipstreamPatchesCommand command = new AutomaticallySlipstreamPatchesCommand();
383 - command.PackageFacades = facades.Values;
384 - command.SlipstreamMspTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleSlipstreamMsp"]);
385 - command.WixBundlePatchTargetCodeTable = this.Output.EnsureTable(this.TableDefinitions["WixBundlePatchTargetCode"]);
337 + var command = new AutomaticallySlipstreamPatchesCommand(section, facades.Values);
338 command.Execute();
339 }
340
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();
343
392 - if (catalogs.Any())
344 + if (catalogs.Count > 0)
345 {
394 - VerifyPayloadsWithCatalogCommand command = new VerifyPayloadsWithCatalogCommand();
395 - command.Catalogs = catalogs;
396 - command.Payloads = payloads.Values;
346 + var command = new VerifyPayloadsWithCatalogCommand(this.Messaging, catalogs, payloadTuples.Values);
347 command.Execute();
348 }
349
400 - if (Messaging.Instance.EncounteredError)
350 + if (this.Messaging.EncounteredError)
351 {
352 return;
353 }
354
355 IEnumerable<PackageFacade> orderedFacades;
406 - IEnumerable<WixBundleRollbackBoundaryRow> boundaries;
356 + IEnumerable<WixBundleRollbackBoundaryTuple> boundaries;
357 {
408 - OrderPackagesAndRollbackBoundariesCommand command = new OrderPackagesAndRollbackBoundariesCommand();
409 - command.Boundaries = new RowDictionary<WixBundleRollbackBoundaryRow>(this.Output.Tables["WixBundleRollbackBoundary"]);
410 - command.PackageFacades = facades;
411 - command.WixGroupTable = wixGroupTable;
358 + var groupTuples = section.Tuples.OfType<WixGroupTuple>();
359 + var boundaryTuplesById = section.Tuples.OfType<WixBundleRollbackBoundaryTuple>().ToDictionary(b => b.Id.Id);
360 +
361 + var command = new OrderPackagesAndRollbackBoundariesCommand(this.Messaging, groupTuples, boundaryTuplesById, facades);
362 command.Execute();
363
364 orderedFacades = command.OrderedPackageFacades;
@@ -418,280 +368,122 @@ namespace WixToolset.Core.Burn
368 // Resolve any delayed fields before generating the manifest.
369 if (this.DelayedFields.Any())
370 {
421 - var resolveDelayedFieldsCommand = new ResolveDelayedFieldsCommand();
422 - resolveDelayedFieldsCommand.OutputType = this.Output.Type;
423 - resolveDelayedFieldsCommand.DelayedFields = this.DelayedFields;
424 - resolveDelayedFieldsCommand.ModularizationGuid = null;
425 - resolveDelayedFieldsCommand.VariableCache = variableCache;
371 + var resolveDelayedFieldsCommand = new ResolveDelayedFieldsCommand(this.Messaging, this.DelayedFields, variableCache);
372 resolveDelayedFieldsCommand.Execute();
373 }
374
429 - // Set the overridable bundle provider key.
430 - this.SetBundleProviderKey(this.Output, bundleRow);
375 + Dictionary<string, ProvidesDependencyTuple> dependencyTuplesByKey;
376 + {
377 + var command = new ProcessDependencyProvidersCommand(this.Messaging, section, facades);
378 + command.Execute();
379
432 - // Import or generate dependency providers for packages in the manifest.
433 - this.ProcessDependencyProviders(this.Output, facades);
380 + bundleTuple.ProviderKey = command.BundleProviderKey; // set the overridable bundle provider key.
381 + dependencyTuplesByKey = command.DependencyTuplesByKey;
382 + }
383
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);
386
387 // Generate the core-defined BA manifest tables...
388 {
440 - CreateBootstrapperApplicationManifestCommand command = new CreateBootstrapperApplicationManifestCommand();
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;
389 + var command = new CreateBootstrapperApplicationManifestCommand(section, bundleTuple, orderedFacades, uxPayloadIndex, payloadTuples, this.IntermediateFolder);
390 command.Execute();
391
451 - WixBundlePayloadRow baManifestPayload = command.BootstrapperApplicationManifestPayloadRow;
452 - payloads.Add(baManifestPayload);
392 + var baManifestPayload = command.BootstrapperApplicationManifestPayloadRow;
393 + payloadTuples.Add(baManifestPayload.Id.Id, baManifestPayload);
394 }
395
455 - //foreach (BinderExtension extension in this.Extensions)
456 - //{
457 - // extension.PostBind(this.Context);
458 - //}
396 +#if TODO
397 + foreach (BinderExtension extension in this.Extensions)
398 + {
399 + extension.PostBind(this.Context);
400 + }
401 +#endif
402
403 // Create all the containers except the UX container first so the manifest (that goes in the UX container)
404 // can contain all size and hash information about the non-UX containers.
462 - RowDictionary<WixBundleContainerRow> containers = new RowDictionary<WixBundleContainerRow>(this.Output.Tables["WixBundleContainer"]);
463 -
464 - ILookup<string, WixBundlePayloadRow> payloadsByContainer = payloads.Values.ToLookup(p => p.Container);
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)
405 + WixBundleContainerTuple uxContainer;
406 + IEnumerable<WixBundlePayloadTuple> uxPayloads;
407 + IEnumerable<WixBundleContainerTuple> containers;
408 {
472 - IEnumerable<WixBundlePayloadRow> containerPayloads = payloadsByContainer[container.Id];
473 -
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 - }
409 + var command = new CreateNonUXContainers(this.BackendHelper, section, bundleApplicationTuple, payloadTuples, this.IntermediateFolder, layoutDirectory, this.DefaultCompressionLevel);
410 + command.Execute();
411
504 - uxContainerPayloads = uxPayloads;
505 - }
506 - else
507 - {
508 - container.WorkingPath = Path.Combine(this.IntermediateFolder, container.Name);
412 + fileTransfers.AddRange(command.FileTransfers);
413
510 - // Add detached containers to the list of file transfers.
511 - if (ContainerType.Detached == container.Type)
512 - {
513 - FileTransfer transfer;
514 - if (FileTransfer.TryCreate(container.WorkingPath, Path.Combine(layoutDirectory, container.Name), true, "Container", container.SourceLineNumbers, out transfer))
515 - {
516 - transfer.Built = true;
517 - fileTransfers.Add(transfer);
518 - }
519 - }
520 - else // update the attached container index.
521 - {
522 - Debug.Assert(ContainerType.Attached == container.Type);
414 + uxContainer = command.UXContainer;
415 + uxPayloads = command.UXContainerPayloads;
416 + containers = command.Containers;
417 + }
418 +
419 + // Create the bundle manifest.
420 + string manifestPath;
421 + {
422 + var executableName = Path.GetFileName(this.OutputPath);
423
524 - container.AttachedContainerIndex = attachedContainerIndex;
525 - ++attachedContainerIndex;
526 - }
424 + var command = new CreateBurnManifestCommand(this.Messaging, this.BackendExtensions, executableName, section, bundleTuple, containers, chainTuple, orderedFacades, boundaries, uxPayloads, payloadTuples, orderedSearches, catalogs, this.IntermediateFolder);
425 + command.Execute();
426
528 - this.CreateContainer(container, containerPayloads, null);
529 - }
427 + manifestPath = command.OutputPath;
428 }
429
532 - // Create the bundle manifest then UX container.
533 - string manifestPath = Path.Combine(this.IntermediateFolder, "bundle-manifest.xml");
430 + // Create the UX container.
431 {
535 - var command = new CreateBurnManifestCommand();
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;
432 + var command = new CreateContainerCommand(manifestPath, uxPayloads, uxContainer.WorkingPath, this.DefaultCompressionLevel);
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));
434
565 - string bundleFilename = Path.GetFileName(this.OutputPath);
566 - if ("setup.exe".Equals(bundleFilename, StringComparison.OrdinalIgnoreCase))
567 - {
568 - Messaging.Instance.OnMessage(WixErrors.InsecureBundleFilename(bundleFilename));
435 + uxContainer.Hash = command.Hash;
436 + uxContainer.Size = command.Size;
437 }
438
571 - FileTransfer bundleTransfer;
572 - if (FileTransfer.TryCreate(bundleTempPath, this.OutputPath, true, "Bundle", bundleRow.SourceLineNumbers, out bundleTransfer))
439 {
574 - bundleTransfer.Built = true;
575 - fileTransfers.Add(bundleTransfer);
576 - }
440 + var command = new CreateBundleExeCommand(this.Messaging, this.BackendHelper, this.IntermediateFolder, this.OutputPath, bundleTuple, uxContainer, containers, this.BurnStubPath);
441 + command.Execute();
442
578 - File.Copy(stubFile, bundleTempPath, true);
579 - File.SetAttributes(bundleTempPath, FileAttributes.Normal);
443 + fileTransfers.Add(command.Transfer);
444 + }
445
581 - this.UpdateBurnResources(bundleTempPath, this.OutputPath, bundleRow);
446 +#if TODO
447 + this.Pdb = new Pdb { Output = output };
448
583 - // Update the .wixburn section to point to at the UX and attached container(s) then attach the containers
584 - // if they should be attached.
585 - using (BurnWriter writer = BurnWriter.Open(bundleTempPath))
449 + if (!String.IsNullOrEmpty(this.OutputPdbPath))
450 {
587 - FileInfo burnStubFile = new FileInfo(bundleTempPath);
588 - writer.InitializeBundleSectionData(burnStubFile.Length, bundleRow.BundleId);
589 -
590 - // Always attach the UX container first
591 - writer.AppendContainer(uxContainer.WorkingPath, BurnWriter.Container.UX);
451 + var trackPdb = this.BackendHelper.TrackFile(this.OutputPdbPath, TrackedFileType.Final);
452 + trackedFiles.Add(trackPdb);
453
593 - // Now append all other attached containers
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);
454 + this.Pdb.Save(trackPdb.Path);
455 }
456 +#endif
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
461 this.FileTransfers = fileTransfers;
615 - this.ContentFilePaths = payloads.Values.Where(p => p.ContentFile).Select(p => p.FullFileName).ToList();
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 - }
462 + this.TrackedFiles = trackedFiles;
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);
469 }
470
640 - private List<WixSearchInfo> OrderSearches()
471 + private IEnumerable<SearchFacade> OrderSearches(IntermediateSection section)
472 {
642 - Dictionary<string, WixSearchInfo> allSearches = new Dictionary<string, WixSearchInfo>();
643 - Table wixFileSearchTable = this.Output.Tables["WixFileSearch"];
644 - if (null != wixFileSearchTable && 0 < wixFileSearchTable.Rows.Count)
645 - {
646 - foreach (Row row in wixFileSearchTable.Rows)
647 - {
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 - }
473 + var searchesById = section.Tuples
474 + .Where(t => t.Definition.Type == TupleDefinitionType.WixComponentSearch ||
475 + t.Definition.Type == TupleDefinitionType.WixFileSearch ||
476 + t.Definition.Type == TupleDefinitionType.WixProductSearch ||
477 + t.Definition.Type == TupleDefinitionType.WixRegistrySearch)
478 + .ToDictionary(t => t.Id.Id);
479
673 - Table wixProductSearchTable = this.Output.Tables["WixProductSearch"];
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 - }
480 + var orderedSearches = new List<SearchFacade>(searchesById.Keys.Count);
481
683 - // Merge in the variable/condition info and get the canonical ordering for
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)
482 + foreach (var searchTuple in section.Tuples.OfType<WixSearchTuple>())
483 {
689 - orderedSearches.Capacity = wixSearchTable.Rows.Count;
690 - foreach (Row row in wixSearchTable.Rows)
484 + if (searchesById.TryGetValue(searchTuple.Id.Id, out var specificSearchTuple))
485 {
692 - WixSearchInfo searchInfo = allSearches[(string)row[0]];
693 - searchInfo.AddWixSearchRowInfo(row);
694 - orderedSearches.Add(searchInfo);
486 + orderedSearches.Add(new SearchFacade(searchTuple, specificSearchTuple));
487 }
488 }
489
@@ -703,9 +495,9 @@ namespace WixToolset.Core.Burn
495 /// </summary>
496 /// <param name="package">The package with properties to 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)
499 {
708 - string id = package.WixChainItemId;
500 + var id = package.Id.Id;
501
502 variableCache.Add(String.Concat("packageDescription.", id), package.Description);
503 //variableCache.Add(String.Concat("packageLanguage.", id), package.Language);
@@ -714,231 +506,63 @@ namespace WixToolset.Core.Burn
506 variableCache.Add(String.Concat("packageVersion.", id), package.Version);
507 }
508
717 - private void CreateContainer(WixBundleContainerRow container, IEnumerable<WixBundlePayloadRow> containerPayloads, string manifestFile)
509 + private void ResolveBundleInstallScope(IntermediateSection section, WixBundleTuple bundleTuple, IEnumerable<PackageFacade> facades)
510 {
719 - CreateContainerCommand command = new CreateContainerCommand();
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 - }
511 + var dependencyTuplesById = section.Tuples.OfType<ProvidesDependencyTuple>().ToDictionary(t => t.Id.Id);
512
730 - private void ResolveBundleInstallScope(WixBundleRow bundleInfo, IEnumerable<PackageFacade> facades)
731 - {
732 - foreach (PackageFacade facade in facades)
513 + foreach (var facade in facades)
514 {
734 - if (bundleInfo.PerMachine && YesNoDefaultType.No == facade.Package.PerMachine)
515 + if (bundleTuple.PerMachine && YesNoDefaultType.No == facade.PackageTuple.PerMachine)
516 {
736 - Messaging.Instance.OnMessage(WixVerboses.SwitchingToPerUserPackage(facade.Package.SourceLineNumbers, facade.Package.WixChainItemId));
517 + this.Messaging.Write(VerboseMessages.SwitchingToPerUserPackage(facade.PackageTuple.SourceLineNumbers, facade.PackageId));
518
738 - bundleInfo.PerMachine = false;
519 + bundleTuple.Attributes &= ~WixBundleAttributes.PerMachine;
520 break;
521 }
522 }
523
743 - foreach (PackageFacade facade in facades)
524 + foreach (var facade in facades)
525 {
526 // Update package scope from bundle scope if default.
746 - if (YesNoDefaultType.Default == facade.Package.PerMachine)
527 + if (YesNoDefaultType.Default == facade.PackageTuple.PerMachine)
528 {
748 - facade.Package.PerMachine = bundleInfo.PerMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No;
529 + facade.PackageTuple.PerMachine = bundleTuple.PerMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No;
530 }
531
532 // We will only register packages in the same scope as the bundle. Warn if any packages with providers
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))
538 {
755 - Messaging.Instance.OnMessage(WixWarnings.NoPerMachineDependencies(facade.Package.SourceLineNumbers, facade.Package.WixChainItemId));
539 + this.Messaging.Write(WarningMessages.NoPerMachineDependencies(facade.PackageTuple.SourceLineNumbers, facade.PackageId));
540 }
541 }
542 }
543
760 - private void UpdateBurnResources(string bundleTempPath, string outputPath, WixBundleRow bundleInfo)
544 + private IEnumerable<T> GetRequiredTuples<T>() where T : IntermediateTuple
545 {
762 - WixToolset.Dtf.Resources.ResourceCollection resources = new WixToolset.Dtf.Resources.ResourceCollection();
763 - WixToolset.Dtf.Resources.VersionResource version = new WixToolset.Dtf.Resources.VersionResource("#1", 1033);
546 + var tuples = this.Output.Sections.Single().Tuples.OfType<T>().ToList();
547
765 - version.Load(bundleTempPath);
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))
548 + if (0 == tuples.Count)
549 {
792 - strings["ProductName"] = bundleInfo.Name;
793 - strings["FileDescription"] = bundleInfo.Name;
550 + throw new WixException(ErrorMessages.MissingBundleInformation(nameof(T)));
551 }
552
796 - if (!String.IsNullOrEmpty(bundleInfo.Publisher))
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);
553 + return tuples;
554 }
555
827 -//#region DependencyExtension
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)
556 + private T GetSingleTuple<T>() where T : IntermediateTuple
557 {
837 - // First import any authored dependencies. These may merge with imported provides from MSI packages.
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 - }
558 + var tuples = this.Output.Sections.Single().Tuples.OfType<T>().ToList();
559
881 - if (!facade.Provides.Merge(dependency))
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)
560 + if (1 != tuples.Count)
561 {
892 - string key = null;
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 - }
562 + throw new WixException(ErrorMessages.MissingBundleInformation(nameof(T)));
563 }
913 - }
564
915 - /// <summary>
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
565 + return tuples[0];
566 }
567 }
568 }
src/WixToolset.Core.Burn/Bind/ProcessDependencyProvidersCommand.cs new
+166
@@ -0,0 +1,166 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core.Burn.Bind
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using System.Linq;
8 + using WixToolset.Data;
9 + using WixToolset.Core.Burn.Bundles;
10 + using WixToolset.Extensibility.Services;
11 + using WixToolset.Data.Tuples;
12 +
13 + internal class ProcessDependencyProvidersCommand
14 + {
15 + public ProcessDependencyProvidersCommand(IMessaging messaging, IntermediateSection section, IDictionary<string, PackageFacade> facades)
16 + {
17 + this.Messaging = messaging;
18 + this.Section = section;
19 +
20 + this.Facades = facades;
21 + }
22 +
23 + public string BundleProviderKey { get; private set; }
24 +
25 + public Dictionary<string, ProvidesDependencyTuple> DependencyTuplesByKey { get; private set; }
26 +
27 + private IMessaging Messaging { get; }
28 +
29 + private IntermediateSection Section { get; }
30 +
31 + private IDictionary<string, PackageFacade> Facades { get; }
32 +
33 + /// <summary>
34 + /// Sets the explicitly provided bundle provider key, if provided. And...
35 + /// Imports authored dependency providers for each package in the manifest,
36 + /// and generates dependency providers for certain package types that do not
37 + /// have a provider defined.
38 + /// </summary>
39 + public void Execute()
40 + {
41 + var wixDependencyProviderTuples = this.Section.Tuples.OfType<WixDependencyProviderTuple>();
42 +
43 + foreach (var wixDependencyProviderTuple in wixDependencyProviderTuples)
44 + {
45 + // Sets the provider key for the bundle, if it is not set already.
46 + if (String.IsNullOrEmpty(this.BundleProviderKey))
47 + {
48 + if (wixDependencyProviderTuple.Bundle)
49 + {
50 + this.BundleProviderKey = wixDependencyProviderTuple.ProviderKey;
51 + }
52 + }
53 +
54 + // Import any authored dependencies. These may merge with imported provides from MSI packages.
55 + var packageId = wixDependencyProviderTuple.Id.Id;
56 +
57 + if (this.Facades.TryGetValue(packageId, out var facade))
58 + {
59 + var dependency = new ProvidesDependencyTuple(wixDependencyProviderTuple.SourceLineNumbers, wixDependencyProviderTuple.Id)
60 + {
61 + PackageRef = packageId,
62 + Key = wixDependencyProviderTuple.ProviderKey,
63 + Version = wixDependencyProviderTuple.Version,
64 + DisplayName = wixDependencyProviderTuple.DisplayName,
65 + Attributes = (int)wixDependencyProviderTuple.Attributes
66 + };
67 +
68 + if (String.IsNullOrEmpty(dependency.Key))
69 + {
70 + switch (facade.SpecificPackageTuple)
71 + {
72 + // The WixDependencyExtension allows an empty Key for MSIs and MSPs.
73 + case WixBundleMsiPackageTuple msiPackage:
74 + dependency.Key = msiPackage.ProductCode;
75 + break;
76 + case WixBundleMspPackageTuple mspPackage:
77 + dependency.Key = mspPackage.PatchCode;
78 + break;
79 + }
80 + }
81 +
82 + if (String.IsNullOrEmpty(dependency.Version))
83 + {
84 + dependency.Version = facade.PackageTuple.Version;
85 + }
86 +
87 + // If the version is still missing, a version could not be harvested from the package and was not authored.
88 + if (String.IsNullOrEmpty(dependency.Version))
89 + {
90 + this.Messaging.Write(ErrorMessages.MissingDependencyVersion(facade.PackageId));
91 + }
92 +
93 + if (String.IsNullOrEmpty(dependency.DisplayName))
94 + {
95 + dependency.DisplayName = facade.PackageTuple.DisplayName;
96 + }
97 +
98 + this.Section.Tuples.Add(dependency);
99 + }
100 + }
101 +
102 + this.DependencyTuplesByKey = this.GetDependencyTuplesByKey();
103 +
104 + // Generate providers for MSI and MSP packages that still do not have providers.
105 + foreach (var facade in this.Facades.Values)
106 + {
107 + string key = null;
108 +
109 + //if (WixBundlePackageType.Msi == facade.PackageTuple.Type)
110 + if (facade.SpecificPackageTuple is WixBundleMsiPackageTuple msiPackage)
111 + {
112 + //var msiPackage = (WixBundleMsiPackageTuple)facade.SpecificPackageTuple;
113 + key = msiPackage.ProductCode;
114 + }
115 + //else if (WixBundlePackageType.Msp == facade.PackageTuple.Type)
116 + else if (facade.SpecificPackageTuple is WixBundleMspPackageTuple mspPackage)
117 + {
118 + //var mspPackage = (WixBundleMspPackageTuple)facade.SpecificPackageTuple;
119 + key = mspPackage.PatchCode;
120 + }
121 +
122 + if (!String.IsNullOrEmpty(key) && !this.DependencyTuplesByKey.ContainsKey(key))
123 + {
124 + var dependency = new ProvidesDependencyTuple(facade.PackageTuple.SourceLineNumbers, facade.PackageTuple.Id)
125 + {
126 + PackageRef = facade.PackageId,
127 + Key = key,
128 + Version = facade.PackageTuple.Version,
129 + DisplayName = facade.PackageTuple.DisplayName
130 + };
131 +
132 + this.Section.Tuples.Add(dependency);
133 +
134 + this.DependencyTuplesByKey.Add(dependency.Key, dependency);
135 + }
136 + }
137 + }
138 +
139 + private Dictionary<string, ProvidesDependencyTuple> GetDependencyTuplesByKey()
140 + {
141 + var dependencyTuplesByKey = new Dictionary<string, ProvidesDependencyTuple>();
142 +
143 + var dependencyTuples = this.Section.Tuples.OfType<ProvidesDependencyTuple>();
144 +
145 + foreach (var dependency in dependencyTuples)
146 + {
147 + if (dependencyTuplesByKey.TryGetValue(dependency.Key, out var collision))
148 + {
149 + // If not a perfect dependency collision, display an error.
150 + if (dependency.Key != collision.Key ||
151 + dependency.Version != collision.Version ||
152 + dependency.DisplayName != collision.DisplayName)
153 + {
154 + this.Messaging.Write(ErrorMessages.DuplicateProviderDependencyKey(dependency.Key, dependency.PackageRef));
155 + }
156 + }
157 + else
158 + {
159 + dependencyTuplesByKey.Add(dependency.Key, dependency);
160 + }
161 + }
162 +
163 + return dependencyTuplesByKey;
164 + }
165 + }
166 +}
src/WixToolset.Core.Burn/Bind/ProvidesDependency.cs deleted
-110
@@ -1,110 +0,0 @@
1 -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 -
3 -namespace WixToolset.Core.Burn
4 -{
5 - using System;
6 - using System.Xml;
7 - using WixToolset.Data;
8 -
9 - /// <summary>
10 - /// Represents an authored or imported dependency provider.
11 - /// </summary>
12 - internal sealed class ProvidesDependency
13 - {
14 -#if TODO
15 - /// <summary>
16 - /// Creates a new instance of the <see cref="ProviderDependency"/> class from a <see cref="Row"/>.
17 - /// </summary>
18 - /// <param name="row">The <see cref="Row"/> from which data is imported.</param>
19 - internal ProvidesDependency(Row row)
20 - : this((string)row[2], (string)row[3], (string)row[4], (int?)row[5])
21 - {
22 - }
23 -#endif
24 -
25 - /// <summary>
26 - /// Creates a new instance of the <see cref="ProviderDependency"/> class.
27 - /// </summary>
28 - /// <param name="key">The unique key of the dependency.</param>
29 - /// <param name="attributes">Additional attributes for the dependency.</param>
30 - internal ProvidesDependency(string key, string version, string displayName, int? attributes)
31 - {
32 - this.Key = key;
33 - this.Version = version;
34 - this.DisplayName = displayName;
35 - this.Attributes = attributes;
36 - }
37 -
38 - /// <summary>
39 - /// Gets or sets the unique key of the package provider.
40 - /// </summary>
41 - internal string Key { get; set; }
42 -
43 - /// <summary>
44 - /// Gets or sets the version of the package provider.
45 - /// </summary>
46 - internal string Version { get; set; }
47 -
48 - /// <summary>
49 - /// Gets or sets the display name of the package provider.
50 - /// </summary>
51 - internal string DisplayName { get; set; }
52 -
53 - /// <summary>
54 - /// Gets or sets the attributes for the dependency.
55 - /// </summary>
56 - internal int? Attributes { get; set; }
57 -
58 - /// <summary>
59 - /// Gets or sets whether the dependency was imported from the package.
60 - /// </summary>
61 - internal bool Imported { get; set; }
62 -
63 - /// <summary>
64 - /// Gets whether certain properties are the same.
65 - /// </summary>
66 - /// <param name="other">Another <see cref="ProvidesDependency"/> to compare.</param>
67 - /// <remarks>This is not the same as object equality, but only checks a subset of properties
68 - /// to determine if the objects are similar and could be merged into a collection.</remarks>
69 - /// <returns>True if certain properties are the same.</returns>
70 - internal bool Equals(ProvidesDependency other)
71 - {
72 - if (null != other)
73 - {
74 - return this.Key == other.Key &&
75 - this.Version == other.Version &&
76 - this.DisplayName == other.DisplayName;
77 - }
78 -
79 - return false;
80 - }
81 -
82 - /// <summary>
83 - /// Writes the dependency to the bundle XML manifest.
84 - /// </summary>
85 - /// <param name="writer">The <see cref="XmlTextWriter"/> for the bundle XML manifest.</param>
86 - internal void WriteXml(XmlTextWriter writer)
87 - {
88 - writer.WriteStartElement("Provides");
89 - writer.WriteAttributeString("Key", this.Key);
90 -
91 - if (!String.IsNullOrEmpty(this.Version))
92 - {
93 - writer.WriteAttributeString("Version", this.Version);
94 - }
95 -
96 - if (!String.IsNullOrEmpty(this.DisplayName))
97 - {
98 - writer.WriteAttributeString("DisplayName", this.DisplayName);
99 - }
100 -
101 - if (this.Imported)
102 - {
103 - // The package dependency was explicitly authored into the manifest.
104 - writer.WriteAttributeString("Imported", "yes");
105 - }
106 -
107 - writer.WriteEndElement();
108 - }
109 - }
110 -}
src/WixToolset.Core.Burn/Bind/ProvidesDependencyCollection.cs deleted
-64
@@ -1,64 +0,0 @@
1 -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 -
3 -namespace WixToolset.Core.Burn
4 -{
5 - using System;
6 - using System.Collections.ObjectModel;
7 -
8 - /// <summary>
9 - /// A case-insensitive collection of unique <see cref="ProvidesDependency"/> objects.
10 - /// </summary>
11 - internal sealed class ProvidesDependencyCollection : KeyedCollection<string, ProvidesDependency>
12 - {
13 - /// <summary>
14 - /// Creates a case-insensitive collection of unique <see cref="ProvidesDependency"/> objects.
15 - /// </summary>
16 - internal ProvidesDependencyCollection()
17 - : base(StringComparer.InvariantCultureIgnoreCase)
18 - {
19 - }
20 -
21 - /// <summary>
22 - /// Adds the <see cref="ProvidesDependency"/> to the collection if it doesn't already exist.
23 - /// </summary>
24 - /// <param name="dependency">The <see cref="ProvidesDependency"/> to add to the collection.</param>
25 - /// <returns>True if the <see cref="ProvidesDependency"/> was added to the collection; otherwise, false.</returns>
26 - /// <exception cref="ArgumentNullException">The <paramref name="dependency"/> parameter is null.</exception>
27 - internal bool Merge(ProvidesDependency dependency)
28 - {
29 - if (null == dependency)
30 - {
31 - throw new ArgumentNullException("dependency");
32 - }
33 -
34 - // If the dependency key is already in the collection, verify equality for a subset of properties.
35 - if (this.Contains(dependency.Key))
36 - {
37 - ProvidesDependency current = this[dependency.Key];
38 - if (!current.Equals(dependency))
39 - {
40 - return false;
41 - }
42 - }
43 -
44 - base.Add(dependency);
45 - return true;
46 - }
47 -
48 - /// <summary>
49 - /// Gets the <see cref="ProvidesDependency.Key"/> for the <paramref name="dependency"/>.
50 - /// </summary>
51 - /// <param name="dependency">The dependency to index.</param>
52 - /// <exception cref="ArgumentNullException">The <paramref name="dependency"/> parameter is null.</exception>
53 - /// <returns>The <see cref="ProvidesDependency.Key"/> for the <paramref name="dependency"/>.</returns>
54 - protected override string GetKeyForItem(ProvidesDependency dependency)
55 - {
56 - if (null == dependency)
57 - {
58 - throw new ArgumentNullException("dependency");
59 - }
60 -
61 - return dependency.Key;
62 - }
63 - }
64 -}
src/WixToolset.Core.Burn/Bind/SearchFacade.cs new
+197
@@ -0,0 +1,197 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core.Burn
4 +{
5 + using System;
6 + using System.Xml;
7 + using WixToolset.Data;
8 + using WixToolset.Data.Tuples;
9 +
10 + internal class SearchFacade
11 + {
12 + public SearchFacade(WixSearchTuple searchTuple, IntermediateTuple searchSpecificTuple)
13 + {
14 + this.SearchTuple = searchTuple;
15 + this.SearchSpecificTuple = searchSpecificTuple;
16 + }
17 +
18 + public WixSearchTuple SearchTuple { get; }
19 +
20 + public IntermediateTuple SearchSpecificTuple { get; }
21 +
22 + /// <summary>
23 + /// Generates Burn manifest and ParameterInfo-style markup a search.
24 + /// </summary>
25 + /// <param name="writer"></param>
26 + public void WriteXml(XmlTextWriter writer)
27 + {
28 + switch (this.SearchSpecificTuple)
29 + {
30 + case WixComponentSearchTuple tuple:
31 + this.WriteComponentSearchXml(writer, tuple);
32 + break;
33 + case WixFileSearchTuple tuple:
34 + this.WriteFileSearchXml(writer, tuple);
35 + break;
36 + case WixProductSearchTuple tuple:
37 + this.WriteProductSearchXml(writer, tuple);
38 + break;
39 + case WixRegistrySearchTuple tuple:
40 + this.WriteRegistrySearchXml(writer, tuple);
41 + break;
42 + }
43 + }
44 +
45 + private void WriteCommonAttributes(XmlTextWriter writer)
46 + {
47 + writer.WriteAttributeString("Id", this.SearchTuple.Id.Id);
48 + writer.WriteAttributeString("Variable", this.SearchTuple.Variable);
49 + if (!String.IsNullOrEmpty(this.SearchTuple.Condition))
50 + {
51 + writer.WriteAttributeString("Condition", this.SearchTuple.Condition);
52 + }
53 + }
54 +
55 + private void WriteComponentSearchXml(XmlTextWriter writer, WixComponentSearchTuple searchTuple)
56 + {
57 + writer.WriteStartElement("MsiComponentSearch");
58 +
59 + this.WriteCommonAttributes(writer);
60 +
61 + writer.WriteAttributeString("ComponentId", searchTuple.Guid);
62 +
63 + if (!String.IsNullOrEmpty(searchTuple.ProductCode))
64 + {
65 + writer.WriteAttributeString("ProductCode", searchTuple.ProductCode);
66 + }
67 +
68 + if (0 != (searchTuple.Attributes & WixComponentSearchAttributes.KeyPath))
69 + {
70 + writer.WriteAttributeString("Type", "keyPath");
71 + }
72 + else if (0 != (searchTuple.Attributes & WixComponentSearchAttributes.State))
73 + {
74 + writer.WriteAttributeString("Type", "state");
75 + }
76 + else if (0 != (searchTuple.Attributes & WixComponentSearchAttributes.WantDirectory))
77 + {
78 + writer.WriteAttributeString("Type", "directory");
79 + }
80 +
81 + writer.WriteEndElement();
82 + }
83 +
84 + private void WriteFileSearchXml(XmlTextWriter writer, WixFileSearchTuple searchTuple)
85 + {
86 + writer.WriteStartElement((0 == (searchTuple.Attributes & WixFileSearchAttributes.IsDirectory)) ? "FileSearch" : "DirectorySearch");
87 +
88 + this.WriteCommonAttributes(writer);
89 +
90 + writer.WriteAttributeString("Path", searchTuple.Path);
91 + if (WixFileSearchAttributes.WantExists == (searchTuple.Attributes & WixFileSearchAttributes.WantExists))
92 + {
93 + writer.WriteAttributeString("Type", "exists");
94 + }
95 + else if (WixFileSearchAttributes.WantVersion == (searchTuple.Attributes & WixFileSearchAttributes.WantVersion))
96 + {
97 + // Can never get here for DirectorySearch.
98 + writer.WriteAttributeString("Type", "version");
99 + }
100 + else
101 + {
102 + writer.WriteAttributeString("Type", "path");
103 + }
104 + writer.WriteEndElement();
105 + }
106 +
107 + private void WriteProductSearchXml(XmlTextWriter writer, WixProductSearchTuple tuple)
108 + {
109 + writer.WriteStartElement("MsiProductSearch");
110 +
111 + this.WriteCommonAttributes(writer);
112 +
113 + if (0 != (tuple.Attributes & WixProductSearchAttributes.UpgradeCode))
114 + {
115 + writer.WriteAttributeString("UpgradeCode", tuple.Guid);
116 + }
117 + else
118 + {
119 + writer.WriteAttributeString("ProductCode", tuple.Guid);
120 + }
121 +
122 + if (0 != (tuple.Attributes & WixProductSearchAttributes.Version))
123 + {
124 + writer.WriteAttributeString("Type", "version");
125 + }
126 + else if (0 != (tuple.Attributes & WixProductSearchAttributes.Language))
127 + {
128 + writer.WriteAttributeString("Type", "language");
129 + }
130 + else if (0 != (tuple.Attributes & WixProductSearchAttributes.State))
131 + {
132 + writer.WriteAttributeString("Type", "state");
133 + }
134 + else if (0 != (tuple.Attributes & WixProductSearchAttributes.Assignment))
135 + {
136 + writer.WriteAttributeString("Type", "assignment");
137 + }
138 +
139 + writer.WriteEndElement();
140 + }
141 +
142 + private void WriteRegistrySearchXml(XmlTextWriter writer, WixRegistrySearchTuple tuple)
143 + {
144 + writer.WriteStartElement("RegistrySearch");
145 +
146 + this.WriteCommonAttributes(writer);
147 +
148 + switch (tuple.Root)
149 + {
150 + case RegistryRootType.ClassesRoot:
151 + writer.WriteAttributeString("Root", "HKCR");
152 + break;
153 + case RegistryRootType.CurrentUser:
154 + writer.WriteAttributeString("Root", "HKCU");
155 + break;
156 + case RegistryRootType.LocalMachine:
157 + writer.WriteAttributeString("Root", "HKLM");
158 + break;
159 + case RegistryRootType.Users:
160 + writer.WriteAttributeString("Root", "HKU");
161 + break;
162 + }
163 +
164 + writer.WriteAttributeString("Key", tuple.Key);
165 +
166 + if (!String.IsNullOrEmpty(tuple.Value))
167 + {
168 + writer.WriteAttributeString("Value", tuple.Value);
169 + }
170 +
171 + var existenceOnly = 0 != (tuple.Attributes & WixRegistrySearchAttributes.WantExists);
172 +
173 + writer.WriteAttributeString("Type", existenceOnly ? "exists" : "value");
174 +
175 + if (0 != (tuple.Attributes & WixRegistrySearchAttributes.Win64))
176 + {
177 + writer.WriteAttributeString("Win64", "yes");
178 + }
179 +
180 + if (!existenceOnly)
181 + {
182 + if (0 != (tuple.Attributes & WixRegistrySearchAttributes.ExpandEnvironmentVariables))
183 + {
184 + writer.WriteAttributeString("ExpandEnvironment", "yes");
185 + }
186 +
187 + // We *always* say this is VariableType="string". If we end up
188 + // needing to be more specific, we will have to expand the "Format"
189 + // attribute to allow "number" and "version".
190 +
191 + writer.WriteAttributeString("VariableType", "string");
192 + }
193 +
194 + writer.WriteEndElement();
195 + }
196 + }
197 +}
src/WixToolset.Core.Burn/Bind/WixComponentSearchInfo.cs deleted
-65
@@ -1,65 +0,0 @@
1 -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 -
3 -namespace WixToolset.Core.Burn
4 -{
5 - using System;
6 - using System.Xml;
7 - using WixToolset.Data;
8 -
9 - /// <summary>
10 - /// Utility class for all WixComponentSearches.
11 - /// </summary>
12 - internal class WixComponentSearchInfo : WixSearchInfo
13 - {
14 -#if TODO
15 - public WixComponentSearchInfo(Row row)
16 - : this((string)row[0], (string)row[1], (string)row[2], (int)row[3])
17 - {
18 - }
19 -#endif
20 -
21 - public WixComponentSearchInfo(string id, string guid, string productCode, int attributes)
22 - : base(id)
23 - {
24 - this.Guid = guid;
25 - this.ProductCode = productCode;
26 - this.Attributes = (WixComponentSearchAttributes)attributes;
27 - }
28 -
29 - public string Guid { get; private set; }
30 - public string ProductCode { get; private set; }
31 - public WixComponentSearchAttributes Attributes { get; private set; }
32 -
33 - /// <summary>
34 - /// Generates Burn manifest and ParameterInfo-style markup for a component search.
35 - /// </summary>
36 - /// <param name="writer"></param>
37 - public override void WriteXml(XmlTextWriter writer)
38 - {
39 - writer.WriteStartElement("MsiComponentSearch");
40 - this.WriteWixSearchAttributes(writer);
41 -
42 - writer.WriteAttributeString("ComponentId", this.Guid);
43 -
44 - if (!String.IsNullOrEmpty(this.ProductCode))
45 - {
46 - writer.WriteAttributeString("ProductCode", this.ProductCode);
47 - }
48 -
49 - if (0 != (this.Attributes & WixComponentSearchAttributes.KeyPath))
50 - {
51 - writer.WriteAttributeString("Type", "keyPath");
52 - }
53 - else if (0 != (this.Attributes & WixComponentSearchAttributes.State))
54 - {
55 - writer.WriteAttributeString("Type", "state");
56 - }
57 - else if (0 != (this.Attributes & WixComponentSearchAttributes.WantDirectory))
58 - {
59 - writer.WriteAttributeString("Type", "directory");
60 - }
61 -
62 - writer.WriteEndElement();
63 - }
64 - }
65 -}
src/WixToolset.Core.Burn/Bind/WixFileSearchInfo.cs deleted
-56
@@ -1,56 +0,0 @@
1 -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 -
3 -namespace WixToolset.Core.Burn
4 -{
5 - using System;
6 - using System.Xml;
7 - using WixToolset.Data;
8 -
9 - /// <summary>
10 - /// Utility class for all WixFileSearches (file and directory searches).
11 - /// </summary>
12 - internal class WixFileSearchInfo : WixSearchInfo
13 - {
14 -#if TODO
15 - public WixFileSearchInfo(Row row)
16 - : this((string)row[0], (string)row[1], (int)row[9])
17 - {
18 - }
19 -#endif
20 -
21 - public WixFileSearchInfo(string id, string path, int attributes)
22 - : base(id)
23 - {
24 - this.Path = path;
25 - this.Attributes = (WixFileSearchAttributes)attributes;
26 - }
27 -
28 - public string Path { get; private set; }
29 - public WixFileSearchAttributes Attributes { get; private set; }
30 -
31 - /// <summary>
32 - /// Generates Burn manifest and ParameterInfo-style markup for a file/directory search.
33 - /// </summary>
34 - /// <param name="writer"></param>
35 - public override void WriteXml(XmlTextWriter writer)
36 - {
37 - writer.WriteStartElement((0 == (this.Attributes & WixFileSearchAttributes.IsDirectory)) ? "FileSearch" : "DirectorySearch");
38 - this.WriteWixSearchAttributes(writer);
39 - writer.WriteAttributeString("Path", this.Path);
40 - if (WixFileSearchAttributes.WantExists == (this.Attributes & WixFileSearchAttributes.WantExists))
41 - {
42 - writer.WriteAttributeString("Type", "exists");
43 - }
44 - else if (WixFileSearchAttributes.WantVersion == (this.Attributes & WixFileSearchAttributes.WantVersion))
45 - {
46 - // Can never get here for DirectorySearch.
47 - writer.WriteAttributeString("Type", "version");
48 - }
49 - else
50 - {
51 - writer.WriteAttributeString("Type", "path");
52 - }
53 - writer.WriteEndElement();
54 - }
55 - }
56 -}
src/WixToolset.Core.Burn/Bind/WixProductSearchInfo.cs deleted
-69
@@ -1,69 +0,0 @@
1 -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 -
3 -namespace WixToolset.Core.Burn
4 -{
5 - using System;
6 - using System.Xml;
7 - using WixToolset.Data;
8 -
9 - /// <summary>
10 - /// Utility class for all WixProductSearches.
11 - /// </summary>
12 - internal class WixProductSearchInfo : WixSearchInfo
13 - {
14 -#if TODO
15 - public WixProductSearchInfo(Row row)
16 - : this((string)row[0], (string)row[1], (int)row[2])
17 - {
18 - }
19 -#endif
20 -
21 - public WixProductSearchInfo(string id, string guid, int attributes)
22 - : base(id)
23 - {
24 - this.Guid = guid;
25 - this.Attributes = (WixProductSearchAttributes)attributes;
26 - }
27 -
28 - public string Guid { get; private set; }
29 - public WixProductSearchAttributes Attributes { get; private set; }
30 -
31 - /// <summary>
32 - /// Generates Burn manifest and ParameterInfo-style markup for a product search.
33 - /// </summary>
34 - /// <param name="writer"></param>
35 - public override void WriteXml(XmlTextWriter writer)
36 - {
37 - writer.WriteStartElement("MsiProductSearch");
38 - this.WriteWixSearchAttributes(writer);
39 -
40 - if (0 != (this.Attributes & WixProductSearchAttributes.UpgradeCode))
41 - {
42 - writer.WriteAttributeString("UpgradeCode", this.Guid);
43 - }
44 - else
45 - {
46 - writer.WriteAttributeString("ProductCode", this.Guid);
47 - }
48 -
49 - if (0 != (this.Attributes & WixProductSearchAttributes.Version))
50 - {
51 - writer.WriteAttributeString("Type", "version");
52 - }
53 - else if (0 != (this.Attributes & WixProductSearchAttributes.Language))
54 - {
55 - writer.WriteAttributeString("Type", "language");
56 - }
57 - else if (0 != (this.Attributes & WixProductSearchAttributes.State))
58 - {
59 - writer.WriteAttributeString("Type", "state");
60 - }
61 - else if (0 != (this.Attributes & WixProductSearchAttributes.Assignment))
62 - {
63 - writer.WriteAttributeString("Type", "assignment");
64 - }
65 -
66 - writer.WriteEndElement();
67 - }
68 - }
69 -}
src/WixToolset.Core.Burn/Bind/WixRegistrySearchInfo.cs deleted
-93
@@ -1,93 +0,0 @@
1 -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 -
3 -namespace WixToolset.Core.Burn
4 -{
5 - using System;
6 - using System.Xml;
7 - using WixToolset.Data.WindowsInstaller;
8 -
9 - /// <summary>
10 - /// Utility class for all WixRegistrySearches.
11 - /// </summary>
12 - internal class WixRegistrySearchInfo : WixSearchInfo
13 - {
14 -#if TODO
15 - public WixRegistrySearchInfo(Row row)
16 - : this((string)row[0], (int)row[1], (string)row[2], (string)row[3], (int)row[4])
17 - {
18 - }
19 -#endif
20 -
21 - public WixRegistrySearchInfo(string id, int root, string key, string value, int attributes)
22 - : base(id)
23 - {
24 - this.Root = root;
25 - this.Key = key;
26 - this.Value = value;
27 - this.Attributes = (WixRegistrySearchAttributes)attributes;
28 - }
29 -
30 - public int Root { get; private set; }
31 - public string Key { get; private set; }
32 - public string Value { get; private set; }
33 - public WixRegistrySearchAttributes Attributes { get; private set; }
34 -
35 - /// <summary>
36 - /// Generates Burn manifest and ParameterInfo-style markup for a registry search.
37 - /// </summary>
38 - /// <param name="writer"></param>
39 - public override void WriteXml(XmlTextWriter writer)
40 - {
41 - writer.WriteStartElement("RegistrySearch");
42 - this.WriteWixSearchAttributes(writer);
43 -
44 - switch (this.Root)
45 - {
46 - case WindowsInstallerConstants.MsidbRegistryRootClassesRoot:
47 - writer.WriteAttributeString("Root", "HKCR");
48 - break;
49 - case WindowsInstallerConstants.MsidbRegistryRootCurrentUser:
50 - writer.WriteAttributeString("Root", "HKCU");
51 - break;
52 - case WindowsInstallerConstants.MsidbRegistryRootLocalMachine:
53 - writer.WriteAttributeString("Root", "HKLM");
54 - break;
55 - case WindowsInstallerConstants.MsidbRegistryRootUsers:
56 - writer.WriteAttributeString("Root", "HKU");
57 - break;
58 - }
59 -
60 - writer.WriteAttributeString("Key", this.Key);
61 -
62 - if (!String.IsNullOrEmpty(this.Value))
63 - {
64 - writer.WriteAttributeString("Value", this.Value);
65 - }
66 -
67 - bool existenceOnly = 0 != (this.Attributes & WixRegistrySearchAttributes.WantExists);
68 -
69 - writer.WriteAttributeString("Type", existenceOnly ? "exists" : "value");
70 -
71 - if (0 != (this.Attributes & WixRegistrySearchAttributes.Win64))
72 - {
73 - writer.WriteAttributeString("Win64", "yes");
74 - }
75 -
76 - if (!existenceOnly)
77 - {
78 - if (0 != (this.Attributes & WixRegistrySearchAttributes.ExpandEnvironmentVariables))
79 - {
80 - writer.WriteAttributeString("ExpandEnvironment", "yes");
81 - }
82 -
83 - // We *always* say this is VariableType="string". If we end up
84 - // needing to be more specific, we will have to expand the "Format"
85 - // attribute to allow "number" and "version".
86 -
87 - writer.WriteAttributeString("VariableType", "string");
88 - }
89 -
90 - writer.WriteEndElement();
91 - }
92 - }
93 -}
src/WixToolset.Core.Burn/Bind/WixSearchInfo.cs deleted
-55
@@ -1,55 +0,0 @@
1 -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 -
3 -namespace WixToolset.Core.Burn
4 -{
5 - using System;
6 - using System.Diagnostics;
7 - using System.Xml;
8 - using WixToolset.Data;
9 -
10 - /// <summary>
11 - /// Utility base class for all WixSearches.
12 - /// </summary>
13 - internal abstract class WixSearchInfo
14 - {
15 - public WixSearchInfo(string id)
16 - {
17 - this.Id = id;
18 - }
19 -
20 -#if TODO
21 - public void AddWixSearchRowInfo(Row row)
22 - {
23 - Debug.Assert((string)row[0] == Id);
24 - Variable = (string)row[1];
25 - Condition = (string)row[2];
26 - }
27 -#endif
28 -
29 - public string Id { get; private set; }
30 - public string Variable { get; private set; }
31 - public string Condition { get; private set; }
32 -
33 - /// <summary>
34 - /// Generates Burn manifest and ParameterInfo-style markup a search.
35 - /// </summary>
36 - /// <param name="writer"></param>
37 - public virtual void WriteXml(XmlTextWriter writer)
38 - {
39 - }
40 -
41 - /// <summary>
42 - /// Writes attributes common to all WixSearch elements.
43 - /// </summary>
44 - /// <param name="writer"></param>
45 - protected void WriteWixSearchAttributes(XmlTextWriter writer)
46 - {
47 - writer.WriteAttributeString("Id", this.Id);
48 - writer.WriteAttributeString("Variable", this.Variable);
49 - if (!String.IsNullOrEmpty(this.Condition))
50 - {
51 - writer.WriteAttributeString("Condition", this.Condition);
52 - }
53 - }
54 - }
55 -}
src/WixToolset.Core.Burn/BundleBackend.cs
+17 -11
@@ -15,20 +15,26 @@ namespace WixToolset.Core.Burn
15 {
16 public IBindResult Bind(IBindContext context)
17 {
18 - BindBundleCommand command = new BindBundleCommand(context);
19 - //command.DefaultCompressionLevel = context.DefaultCompressionLevel;
20 - //command.Extensions = context.Extensions;
21 - //command.IntermediateFolder = context.IntermediateFolder;
22 - //command.Output = context.IntermediateRepresentation;
23 - //command.OutputPath = context.OutputPath;
24 - //command.PdbFile = context.OutputPdbPath;
25 - //command.WixVariableResolver = context.WixVariableResolver;
18 + var extensionManager = context.ServiceProvider.GetService<IExtensionManager>();
19 +
20 + var backendExtensions = extensionManager.GetServices<IBurnBackendExtension>();
21 +
22 + foreach (var extension in backendExtensions)
23 + {
24 + extension.PreBackendBind(context);
25 + }
26 +
27 + var command = new BindBundleCommand(context, backendExtensions);
28 command.Execute();
29
30 var result = context.ServiceProvider.GetService<IBindResult>();
31 result.FileTransfers = command.FileTransfers;
32 result.TrackedFiles = command.TrackedFiles;
33
34 + foreach (var extension in backendExtensions)
35 + {
36 + extension.PostBackendBind(result);
37 + }
38 return result;
39 }
40
@@ -53,10 +59,10 @@ namespace WixToolset.Core.Burn
59
60 public Intermediate Unbind(IUnbindContext context)
61 {
56 - string uxExtractPath = Path.Combine(context.ExportBasePath, "UX");
57 - string acExtractPath = Path.Combine(context.ExportBasePath, "AttachedContainer");
62 + var uxExtractPath = Path.Combine(context.ExportBasePath, "UX");
63 + var acExtractPath = Path.Combine(context.ExportBasePath, "AttachedContainer");
64
59 - using (BurnReader reader = BurnReader.Open(context.InputFilePath))
65 + using (var reader = BurnReader.Open(context.InputFilePath))
66 {
67 reader.ExtractUXContainer(uxExtractPath, context.IntermediateFolder);
68 reader.ExtractAttachedContainer(acExtractPath, context.IntermediateFolder);
src/WixToolset.Core.Burn/Bundles/AutomaticallySlipstreamPatchesCommand.cs
+65 -56
@@ -7,107 +7,116 @@ namespace WixToolset.Core.Burn.Bundles
7 using System.Diagnostics;
8 using System.Linq;
9 using WixToolset.Data;
10 + using WixToolset.Data.Tuples;
11
12 internal class AutomaticallySlipstreamPatchesCommand
13 {
13 -#if TODO
14 - public IEnumerable<PackageFacade> PackageFacades { private get; set; }
14 + public AutomaticallySlipstreamPatchesCommand(IntermediateSection section, ICollection<PackageFacade> packageFacades)
15 + {
16 + this.Section = section;
17 + this.PackageFacades = packageFacades;
18 + }
19
16 - public Table WixBundlePatchTargetCodeTable { private get; set; }
20 + private IntermediateSection Section { get; }
21
18 - public Table SlipstreamMspTable { private get; set; }
22 + private IEnumerable<PackageFacade> PackageFacades { get; }
23
24 public void Execute()
25 {
22 - List<WixBundleMsiPackageRow> msiPackages = new List<WixBundleMsiPackageRow>();
23 - Dictionary<string, List<WixBundlePatchTargetCodeRow>> targetsProductCode = new Dictionary<string, List<WixBundlePatchTargetCodeRow>>();
24 - Dictionary<string, List<WixBundlePatchTargetCodeRow>> targetsUpgradeCode = new Dictionary<string, List<WixBundlePatchTargetCodeRow>>();
26 + var msiPackages = new List<WixBundleMsiPackageTuple>();
27 + var targetsProductCode = new Dictionary<string, List<WixBundlePatchTargetCodeTuple>>();
28 + var targetsUpgradeCode = new Dictionary<string, List<WixBundlePatchTargetCodeTuple>>();
29
26 - foreach (PackageFacade facade in this.PackageFacades)
30 + foreach (var facade in this.PackageFacades)
31 {
28 - if (WixBundlePackageType.Msi == facade.Package.Type)
32 + // Keep track of all MSI packages.
33 + if (facade.SpecificPackageTuple is WixBundleMsiPackageTuple msiPackage)
34 {
30 - // Keep track of all MSI packages.
31 - msiPackages.Add(facade.MsiPackage);
35 + msiPackages.Add(msiPackage);
36 }
33 - else if (WixBundlePackageType.Msp == facade.Package.Type && facade.MspPackage.Slipstream)
37 + else if (facade.SpecificPackageTuple is WixBundleMspPackageTuple mspPackage && mspPackage.Slipstream)
38 {
35 - IEnumerable<WixBundlePatchTargetCodeRow> patchTargetCodeRows = this.WixBundlePatchTargetCodeTable.RowsAs<WixBundlePatchTargetCodeRow>().Where(r => r.MspPackageId == facade.Package.WixChainItemId);
39 + var patchTargetCodeTuples = this.Section.Tuples
40 + .OfType<WixBundlePatchTargetCodeTuple>()
41 + .Where(r => r.PackageRef == facade.PackageId);
42
43 // Index target ProductCodes and UpgradeCodes for slipstreamed MSPs.
38 - foreach (WixBundlePatchTargetCodeRow row in patchTargetCodeRows)
44 + foreach (var tuple in patchTargetCodeTuples)
45 {
40 - if (row.TargetsProductCode)
46 + if (tuple.TargetsProductCode)
47 {
42 - List<WixBundlePatchTargetCodeRow> rows;
43 - if (!targetsProductCode.TryGetValue(row.TargetCode, out rows))
48 + if (!targetsProductCode.TryGetValue(tuple.TargetCode, out var tuples))
49 {
45 - rows = new List<WixBundlePatchTargetCodeRow>();
46 - targetsProductCode.Add(row.TargetCode, rows);
50 + tuples = new List<WixBundlePatchTargetCodeTuple>();
51 + targetsProductCode.Add(tuple.TargetCode, tuples);
52 }
53
49 - rows.Add(row);
54 + tuples.Add(tuple);
55 }
51 - else if (row.TargetsUpgradeCode)
56 + else if (tuple.TargetsUpgradeCode)
57 {
53 - List<WixBundlePatchTargetCodeRow> rows;
54 - if (!targetsUpgradeCode.TryGetValue(row.TargetCode, out rows))
58 + if (!targetsUpgradeCode.TryGetValue(tuple.TargetCode, out var tuples))
59 {
56 - rows = new List<WixBundlePatchTargetCodeRow>();
57 - targetsUpgradeCode.Add(row.TargetCode, rows);
60 + tuples = new List<WixBundlePatchTargetCodeTuple>();
61 + targetsUpgradeCode.Add(tuple.TargetCode, tuples);
62 }
63 }
64 }
65 }
66 }
67
64 - RowIndexedList<Row> slipstreamMspRows = new RowIndexedList<Row>(SlipstreamMspTable);
68 + var slipstreamMspIds = new HashSet<string>();
69
70 // Loop through the MSI and slipstream patches targeting it.
67 - foreach (WixBundleMsiPackageRow msi in msiPackages)
71 + foreach (var msi in msiPackages)
72 {
69 - List<WixBundlePatchTargetCodeRow> rows;
70 - if (targetsProductCode.TryGetValue(msi.ProductCode, out rows))
73 + if (targetsProductCode.TryGetValue(msi.ProductCode, out var tuples))
74 {
72 - foreach (WixBundlePatchTargetCodeRow row in rows)
75 + foreach (var tuple in tuples)
76 {
74 - Debug.Assert(row.TargetsProductCode);
75 - Debug.Assert(!row.TargetsUpgradeCode);
76 -
77 - Row slipstreamMspRow = SlipstreamMspTable.CreateRow(row.SourceLineNumbers, false);
78 - slipstreamMspRow[0] = msi.ChainPackageId;
79 - slipstreamMspRow[1] = row.MspPackageId;
77 + Debug.Assert(tuple.TargetsProductCode);
78 + Debug.Assert(!tuple.TargetsUpgradeCode);
79
81 - if (slipstreamMspRows.TryAdd(slipstreamMspRow))
82 - {
83 - SlipstreamMspTable.Rows.Add(slipstreamMspRow);
84 - }
80 + this.TryAddSlipstreamTuple(slipstreamMspIds, msi, tuple);
81 }
86 -
87 - rows = null;
82 }
83
90 - if (!String.IsNullOrEmpty(msi.UpgradeCode) && targetsUpgradeCode.TryGetValue(msi.UpgradeCode, out rows))
84 + if (!String.IsNullOrEmpty(msi.UpgradeCode) && targetsUpgradeCode.TryGetValue(msi.UpgradeCode, out tuples))
85 {
92 - foreach (WixBundlePatchTargetCodeRow row in rows)
86 + foreach (var tuple in tuples)
87 {
94 - Debug.Assert(!row.TargetsProductCode);
95 - Debug.Assert(row.TargetsUpgradeCode);
96 -
97 - Row slipstreamMspRow = SlipstreamMspTable.CreateRow(row.SourceLineNumbers, false);
98 - slipstreamMspRow[0] = msi.ChainPackageId;
99 - slipstreamMspRow[1] = row.MspPackageId;
88 + Debug.Assert(!tuple.TargetsProductCode);
89 + Debug.Assert(tuple.TargetsUpgradeCode);
90
101 - if (slipstreamMspRows.TryAdd(slipstreamMspRow))
102 - {
103 - SlipstreamMspTable.Rows.Add(slipstreamMspRow);
104 - }
91 + this.TryAddSlipstreamTuple(slipstreamMspIds, msi, tuple);
92 }
93
107 - rows = null;
94 + tuples = null;
95 }
96 }
97 }
111 -#endif
98 +
99 + private bool TryAddSlipstreamTuple(HashSet<string> slipstreamMspIds, WixBundleMsiPackageTuple msiPackage, WixBundlePatchTargetCodeTuple patchTargetCode)
100 + {
101 + var id = new Identifier(AccessModifier.Private, msiPackage.Id.Id, patchTargetCode.PackageRef);
102 +
103 + if (slipstreamMspIds.Add(id.Id))
104 + {
105 + var slipstreamTuple = new WixBundleSlipstreamMspTuple(patchTargetCode.SourceLineNumbers)
106 + {
107 + TargetPackageRef = msiPackage.Id.Id,
108 + MspPackageRef = patchTargetCode.PackageRef
109 + };
110 +
111 + //var slipstreamMspRow = SlipstreamMspTable.CreateRow(tuple.SourceLineNumbers, false);
112 + //slipstreamMspRow[0] = msi.ChainPackageId;
113 + //slipstreamMspRow[1] = tuple.MspPackageId;
114 +
115 + this.Section.Tuples.Add(slipstreamTuple);
116 + return true;
117 + }
118 +
119 + return false;
120 + }
121 }
122 }
src/WixToolset.Core.Burn/Bundles/BundleHashAlgorithm.cs new
+30
@@ -0,0 +1,30 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core.Burn.Bundles
4 +{
5 + using System.IO;
6 + using System.Security.Cryptography;
7 + using System.Text;
8 +
9 + internal static class BundleHashAlgorithm
10 + {
11 + public static string Hash(FileInfo fileInfo)
12 + {
13 + byte[] hashBytes;
14 +
15 + using (var managed = new SHA1Managed())
16 + using (var stream = fileInfo.OpenRead())
17 + {
18 + hashBytes = managed.ComputeHash(stream);
19 + }
20 +
21 + var sb = new StringBuilder(hashBytes.Length * 2);
22 + for (var i = 0; i < hashBytes.Length; i++)
23 + {
24 + sb.AppendFormat("{0:X2}", hashBytes[i]);
25 + }
26 +
27 + return sb.ToString();
28 + }
29 + }
30 +}
src/WixToolset.Core.Burn/Bundles/BurnCommon.cs
+3 -3
@@ -128,7 +128,7 @@ namespace WixToolset.Core.Burn.Bundles
128
129 public void Dispose()
130 {
131 - Dispose(true);
131 + this.Dispose(true);
132
133 GC.SuppressFinalize(this);
134 }
@@ -238,7 +238,7 @@ namespace WixToolset.Core.Burn.Bundles
238 {
239 if (UInt32.MaxValue == this.wixburnDataOffset)
240 {
241 - if (!EnsureNTHeader(reader))
241 + if (!this.EnsureNTHeader(reader))
242 {
243 return false;
244 }
@@ -286,7 +286,7 @@ namespace WixToolset.Core.Burn.Bundles
286 {
287 if (UInt32.MaxValue == this.firstSectionOffset)
288 {
289 - if (!EnsureDosHeader(reader))
289 + if (!this.EnsureDosHeader(reader))
290 {
291 return false;
292 }
src/WixToolset.Core.Burn/Bundles/BurnWriter.cs
+6 -4
@@ -67,19 +67,21 @@ namespace WixToolset.Core.Burn.Bundles
67 /// <param name="stubSize">Size of the stub engine "burn.exe".</param>
68 /// <param name="bundleId">Unique identifier for this bundle.</param>
69 /// <returns></returns>
70 - public bool InitializeBundleSectionData(long stubSize, Guid bundleId)
70 + public bool InitializeBundleSectionData(long stubSize, string bundleId)
71 {
72 if (this.invalidBundle)
73 {
74 return false;
75 }
76
77 + var bundleGuid = Guid.Parse(bundleId);
78 +
79 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_MAGIC, BURN_SECTION_MAGIC);
80 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_VERSION, BURN_SECTION_VERSION);
81
80 - this.messaging.Write(VerboseMessages.BundleGuid(bundleId.ToString("B")));
82 + this.messaging.Write(VerboseMessages.BundleGuid(bundleId));
83 this.binaryWriter.BaseStream.Seek(this.wixburnDataOffset + BURN_SECTION_OFFSET_BUNDLEGUID, SeekOrigin.Begin);
82 - this.binaryWriter.Write(bundleId.ToByteArray());
84 + this.binaryWriter.Write(bundleGuid.ToByteArray());
85
86 this.StubSize = (uint)stubSize;
87
@@ -146,7 +148,7 @@ namespace WixToolset.Core.Burn.Bundles
148 return false;
149 }
150
149 - return AppendContainer(containerStream, (UInt32)containerSize, burnSectionOffsetSize, burnSectionCount);
151 + return this.AppendContainer(containerStream, (UInt32)containerSize, burnSectionOffsetSize, burnSectionCount);
152 }
153
154 public void RememberThenResetSignature()
src/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs
+172 -140
@@ -7,236 +7,268 @@ namespace WixToolset.Core.Burn.Bundles
7 using System.Diagnostics;
8 using System.Globalization;
9 using System.IO;
10 + using System.Linq;
11 using System.Text;
12 using System.Xml;
13 using WixToolset.Data;
14 + using WixToolset.Data.Burn;
15 + using WixToolset.Data.Tuples;
16
17 internal class CreateBootstrapperApplicationManifestCommand
18 {
16 -#if TODO
17 - public WixBundleRow BundleRow { private get; set; }
18 -
19 - public IEnumerable<PackageFacade> ChainPackages { private get; set; }
19 + public CreateBootstrapperApplicationManifestCommand(IntermediateSection section, WixBundleTuple bundleTuple, IEnumerable<PackageFacade> chainPackages, int lastUXPayloadIndex, Dictionary<string, WixBundlePayloadTuple> payloadTuples, string intermediateFolder)
20 + {
21 + this.Section = section;
22 + this.BundleTuple = bundleTuple;
23 + this.ChainPackages = chainPackages;
24 + this.LastUXPayloadIndex = lastUXPayloadIndex;
25 + this.Payloads = payloadTuples;
26 + this.IntermediateFolder = intermediateFolder;
27 + }
28
21 - public int LastUXPayloadIndex { private get; set; }
29 + private IntermediateSection Section { get; }
30
23 - public IEnumerable<WixBundleMsiFeatureRow> MsiFeatures { private get; set; }
31 + private WixBundleTuple BundleTuple { get; }
32
25 - public Output Output { private get; set; }
33 + private IEnumerable<PackageFacade> ChainPackages { get; }
34
27 - public RowDictionary<WixBundlePayloadRow> Payloads { private get; set; }
35 + private int LastUXPayloadIndex { get; }
36
29 - public TableDefinitionCollection TableDefinitions { private get; set; }
37 + private Dictionary<string, WixBundlePayloadTuple> Payloads { get; }
38
31 - public string TempFilesLocation { private get; set; }
39 + private string IntermediateFolder { get; }
40
33 - public WixBundlePayloadRow BootstrapperApplicationManifestPayloadRow { get; private set; }
41 + public WixBundlePayloadTuple BootstrapperApplicationManifestPayloadRow { get; private set; }
42
43 public void Execute()
44 {
37 - this.GenerateBAManifestBundleTables();
45 + var baManifestPath = this.CreateBootstrapperApplicationManifest();
46
39 - this.GenerateBAManifestMsiFeatureTables();
47 + this.BootstrapperApplicationManifestPayloadRow = this.CreateBootstrapperApplicationManifestPayloadRow(baManifestPath);
48 + }
49
41 - this.GenerateBAManifestPackageTables();
50 + private string CreateBootstrapperApplicationManifest()
51 + {
52 + var path = Path.Combine(this.IntermediateFolder, "wix-badata.xml");
53
43 - this.GenerateBAManifestPayloadTables();
54 + Directory.CreateDirectory(Path.GetDirectoryName(path));
55
45 - string baManifestPath = Path.Combine(this.TempFilesLocation, "wix-badata.xml");
56 + using (var writer = new XmlTextWriter(path, Encoding.Unicode))
57 + {
58 + writer.Formatting = Formatting.Indented;
59 + writer.WriteStartDocument();
60 + writer.WriteStartElement("BootstrapperApplicationData", "http://wixtoolset.org/schemas/v4/BootstrapperApplicationData");
61
47 - this.CreateBootstrapperApplicationManifest(baManifestPath);
62 + this.WriteBundleInfo(writer);
63
49 - this.BootstrapperApplicationManifestPayloadRow = this.CreateBootstrapperApplicationManifestPayloadRow(baManifestPath);
64 + this.WritePackageInfo(writer);
65 +
66 + this.WriteFeatureInfo(writer);
67 +
68 + this.WritePayloadInfo(writer);
69 +
70 + this.WriteCustomBootstrapperApplicationData(writer);
71 +
72 + writer.WriteEndElement();
73 + writer.WriteEndDocument();
74 + }
75 +
76 + return path;
77 }
78
52 - private void GenerateBAManifestBundleTables()
79 + private void WriteBundleInfo(XmlTextWriter writer)
80 {
54 - Table wixBundlePropertiesTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleProperties"]);
55 -
56 - Row row = wixBundlePropertiesTable.CreateRow(this.BundleRow.SourceLineNumbers);
57 - row[0] = this.BundleRow.Name;
58 - row[1] = this.BundleRow.LogPathVariable;
59 - row[2] = (YesNoDefaultType.Yes == this.BundleRow.Compressed) ? "yes" : "no";
60 - row[3] = this.BundleRow.BundleId.ToString("B");
61 - row[4] = this.BundleRow.UpgradeCode;
62 - row[5] = this.BundleRow.PerMachine ? "yes" : "no";
81 + writer.WriteStartElement("WixBundleProperties");
82 +
83 + writer.WriteAttributeString("DisplayName", this.BundleTuple.Name);
84 + writer.WriteAttributeString("LogPathVariable", this.BundleTuple.LogPathVariable);
85 + writer.WriteAttributeString("Compressed", this.BundleTuple.Compressed == true ? "yes" : "no");
86 + writer.WriteAttributeString("BundleId", this.BundleTuple.BundleId.ToUpperInvariant());
87 + writer.WriteAttributeString("UpgradeCode", this.BundleTuple.UpgradeCode);
88 + writer.WriteAttributeString("PerMachine", this.BundleTuple.PerMachine ? "yes" : "no");
89 +
90 + writer.WriteEndElement();
91 }
92
65 - private void GenerateBAManifestPackageTables()
93 + private void WritePackageInfo(XmlTextWriter writer)
94 {
67 - Table wixPackagePropertiesTable = this.Output.EnsureTable(this.TableDefinitions["WixPackageProperties"]);
68 -
69 - foreach (PackageFacade package in this.ChainPackages)
95 + foreach (var package in this.ChainPackages)
96 {
71 - WixBundlePayloadRow packagePayload = this.Payloads[package.Package.PackagePayload];
72 -
73 - Row row = wixPackagePropertiesTable.CreateRow(package.Package.SourceLineNumbers);
74 - row[0] = package.Package.WixChainItemId;
75 - row[1] = (YesNoType.Yes == package.Package.Vital) ? "yes" : "no";
76 - row[2] = package.Package.DisplayName;
77 - row[3] = package.Package.Description;
78 - row[4] = package.Package.Size.ToString(CultureInfo.InvariantCulture); // TODO: DownloadSize (compressed) (what does this mean when it's embedded?)
79 - row[5] = package.Package.Size.ToString(CultureInfo.InvariantCulture); // Package.Size (uncompressed)
80 - row[6] = package.Package.InstallSize.Value.ToString(CultureInfo.InvariantCulture); // InstallSize (required disk space)
81 - row[7] = package.Package.Type.ToString();
82 - row[8] = package.Package.Permanent ? "yes" : "no";
83 - row[9] = package.Package.LogPathVariable;
84 - row[10] = package.Package.RollbackLogPathVariable;
85 - row[11] = (PackagingType.Embedded == packagePayload.Packaging) ? "yes" : "no";
86 -
87 - if (WixBundlePackageType.Msi == package.Package.Type)
97 + var packagePayload = this.Payloads[package.PackageTuple.PayloadRef];
98 +
99 + var size = package.PackageTuple.Size.ToString(CultureInfo.InvariantCulture);
100 +
101 + writer.WriteStartElement("WixBundleProperties");
102 +
103 + writer.WriteAttributeString("Package", package.PackageId);
104 + writer.WriteAttributeString("Vital", package.PackageTuple.Vital == true ? "yes" : "no");
105 + writer.WriteAttributeString("DisplayName", package.PackageTuple.DisplayName);
106 + writer.WriteAttributeString("Description", package.PackageTuple.Description);
107 + writer.WriteAttributeString("DownloadSize", size);
108 + writer.WriteAttributeString("PackageSize", size);
109 + writer.WriteAttributeString("InstalledSize", package.PackageTuple.InstallSize?.ToString(CultureInfo.InvariantCulture) ?? size);
110 + writer.WriteAttributeString("PackageType", package.PackageTuple.Type.ToString());
111 + writer.WriteAttributeString("Permanent", package.PackageTuple.Permanent ? "yes" : "no");
112 + writer.WriteAttributeString("LogPathVariable", package.PackageTuple.LogPathVariable);
113 + writer.WriteAttributeString("RollbackLogPathVariable", package.PackageTuple.RollbackLogPathVariable);
114 + writer.WriteAttributeString("Compressed", packagePayload.Compressed == true ? "yes" : "no");
115 +
116 + if (package.SpecificPackageTuple is WixBundleMsiPackageTuple msiPackage)
117 {
89 - row[12] = package.MsiPackage.DisplayInternalUI ? "yes" : "no";
118 + writer.WriteAttributeString("DisplayInternalUI", msiPackage.DisplayInternalUI ? "yes" : "no");
119
91 - if (!String.IsNullOrEmpty(package.MsiPackage.ProductCode))
120 + if (!String.IsNullOrEmpty(msiPackage.ProductCode))
121 {
93 - row[13] = package.MsiPackage.ProductCode;
122 + writer.WriteAttributeString("ProductCode", msiPackage.ProductCode);
123 }
124
96 - if (!String.IsNullOrEmpty(package.MsiPackage.UpgradeCode))
125 + if (!String.IsNullOrEmpty(msiPackage.UpgradeCode))
126 {
98 - row[14] = package.MsiPackage.UpgradeCode;
127 + writer.WriteAttributeString("UpgradeCode", msiPackage.UpgradeCode);
128 }
129 }
101 - else if (WixBundlePackageType.Msp == package.Package.Type)
130 + else if (package.SpecificPackageTuple is WixBundleMspPackageTuple mspPackage)
131 {
103 - row[12] = package.MspPackage.DisplayInternalUI ? "yes" : "no";
132 + writer.WriteAttributeString("DisplayInternalUI", mspPackage.DisplayInternalUI ? "yes" : "no");
133
105 - if (!String.IsNullOrEmpty(package.MspPackage.PatchCode))
134 + if (!String.IsNullOrEmpty(mspPackage.PatchCode))
135 {
107 - row[13] = package.MspPackage.PatchCode;
136 + writer.WriteAttributeString("ProductCode", mspPackage.PatchCode);
137 }
138 }
139
111 - if (!String.IsNullOrEmpty(package.Package.Version))
140 + if (!String.IsNullOrEmpty(package.PackageTuple.Version))
141 {
113 - row[15] = package.Package.Version;
142 + writer.WriteAttributeString("Version", package.PackageTuple.Version);
143 }
144
116 - if (!String.IsNullOrEmpty(package.Package.InstallCondition))
145 + if (!String.IsNullOrEmpty(package.PackageTuple.InstallCondition))
146 {
118 - row[16] = package.Package.InstallCondition;
147 + writer.WriteAttributeString("InstallCondition", package.PackageTuple.InstallCondition);
148 }
149
121 - switch (package.Package.Cache)
150 + switch (package.PackageTuple.Cache)
151 {
152 case YesNoAlwaysType.No:
124 - row[17] = "no";
153 + writer.WriteAttributeString("Cache", "no");
154 break;
155 case YesNoAlwaysType.Yes:
127 - row[17] = "yes";
156 + writer.WriteAttributeString("Cache", "yes");
157 break;
158 case YesNoAlwaysType.Always:
130 - row[17] = "always";
159 + writer.WriteAttributeString("Cache", "always");
160 break;
161 }
162 +
163 + writer.WriteEndElement();
164 }
165 }
166
136 - private void GenerateBAManifestMsiFeatureTables()
167 + private void WriteFeatureInfo(XmlTextWriter writer)
168 {
138 - Table wixPackageFeatureInfoTable = this.Output.EnsureTable(this.TableDefinitions["WixPackageFeatureInfo"]);
169 + var featureTuples = this.Section.Tuples.OfType<WixBundleMsiFeatureTuple>();
170
140 - foreach (WixBundleMsiFeatureRow feature in this.MsiFeatures)
171 + foreach (var featureTuple in featureTuples)
172 {
142 - Row row = wixPackageFeatureInfoTable.CreateRow(feature.SourceLineNumbers);
143 - row[0] = feature.ChainPackageId;
144 - row[1] = feature.Name;
145 - row[2] = Convert.ToString(feature.Size, CultureInfo.InvariantCulture);
146 - row[3] = feature.Parent;
147 - row[4] = feature.Title;
148 - row[5] = feature.Description;
149 - row[6] = Convert.ToString(feature.Display, CultureInfo.InvariantCulture);
150 - row[7] = Convert.ToString(feature.Level, CultureInfo.InvariantCulture);
151 - row[8] = feature.Directory;
152 - row[9] = Convert.ToString(feature.Attributes, CultureInfo.InvariantCulture);
153 - }
173 + writer.WriteStartElement("WixPackageFeatureInfo");
174 +
175 + writer.WriteAttributeString("Package", featureTuple.PackageRef);
176 + writer.WriteAttributeString("Feature", featureTuple.Name);
177 + writer.WriteAttributeString("Size", featureTuple.Size.ToString(CultureInfo.InvariantCulture));
178 + writer.WriteAttributeString("Parent", featureTuple.Parent);
179 + writer.WriteAttributeString("Title", featureTuple.Title);
180 + writer.WriteAttributeString("Description", featureTuple.Description);
181 + writer.WriteAttributeString("Display", featureTuple.Display.ToString(CultureInfo.InvariantCulture));
182 + writer.WriteAttributeString("Level", featureTuple.Level.ToString(CultureInfo.InvariantCulture));
183 + writer.WriteAttributeString("Directory", featureTuple.Directory);
184 + writer.WriteAttributeString("Attributes", featureTuple.Attributes.ToString(CultureInfo.InvariantCulture));
185
186 + writer.WriteEndElement();
187 + }
188 }
189
157 - private void GenerateBAManifestPayloadTables()
190 + private void WritePayloadInfo(XmlTextWriter writer)
191 {
159 - Table wixPayloadPropertiesTable = this.Output.EnsureTable(this.TableDefinitions["WixPayloadProperties"]);
192 + var payloadTuples = this.Section.Tuples.OfType<WixBundlePayloadTuple>();
193
161 - foreach (WixBundlePayloadRow payload in this.Payloads.Values)
194 + foreach (var payloadTuple in payloadTuples)
195 {
163 - WixPayloadPropertiesRow row = (WixPayloadPropertiesRow)wixPayloadPropertiesTable.CreateRow(payload.SourceLineNumbers);
164 - row.Id = payload.Id;
165 - row.Package = payload.Package;
166 - row.Container = payload.Container;
167 - row.Name = payload.Name;
168 - row.Size = payload.FileSize.ToString();
169 - row.DownloadUrl = payload.DownloadUrl;
170 - row.LayoutOnly = payload.LayoutOnly ? "yes" : "no";
196 + writer.WriteStartElement("WixPackageFeatureInfo");
197 +
198 + writer.WriteAttributeString("Id", payloadTuple.Id.Id);
199 + writer.WriteAttributeString("Package", payloadTuple.PackageRef);
200 + writer.WriteAttributeString("Container", payloadTuple.ContainerRef);
201 + writer.WriteAttributeString("Name", payloadTuple.Name);
202 + writer.WriteAttributeString("Size", payloadTuple.FileSize.ToString(CultureInfo.InvariantCulture));
203 + writer.WriteAttributeString("DownloadUrl", payloadTuple.DownloadUrl);
204 + writer.WriteAttributeString("LayoutOnly", payloadTuple.LayoutOnly ? "yes" : "no");
205 +
206 + writer.WriteEndElement();
207 }
208 }
209
174 - private void CreateBootstrapperApplicationManifest(string path)
210 + private void WriteCustomBootstrapperApplicationData(XmlTextWriter writer)
211 {
176 - using (XmlTextWriter writer = new XmlTextWriter(path, Encoding.Unicode))
212 + var dataTuplesGroupedByDefinitionName = this.Section.Tuples
213 + .Where(t => t.Definition.HasTag(BurnConstants.BootstrapperApplicationDataTupleDefinitionTag))
214 + .GroupBy(t => t.Definition);
215 +
216 + foreach (var group in dataTuplesGroupedByDefinitionName)
217 {
178 - writer.Formatting = Formatting.Indented;
179 - writer.WriteStartDocument();
180 - writer.WriteStartElement("BootstrapperApplicationData", "http://wixtoolset.org/schemas/v4/2010/BootstrapperApplicationData");
218 + var definition = group.Key;
219
182 - foreach (Table table in this.Output.Tables)
183 - {
184 - if (table.Definition.BootstrapperApplicationData)
185 - {
186 - // We simply assert that the table (and field) name is valid, because
187 - // this is up to the extension developer to get right. An author will
188 - // only affect the attribute value, and that will get properly escaped.
220 + // We simply assert that the table (and field) name is valid, because
221 + // this is up to the extension developer to get right. An author will
222 + // only affect the attribute value, and that will get properly escaped.
223 #if DEBUG
190 - Debug.Assert(Common.IsIdentifier(table.Name));
191 - foreach (ColumnDefinition column in table.Definition.Columns)
192 - {
193 - Debug.Assert(Common.IsIdentifier(column.Name));
194 - }
224 + Debug.Assert(Common.IsIdentifier(definition.Name));
225 + foreach (var fieldDef in definition.FieldDefinitions)
226 + {
227 + Debug.Assert(Common.IsIdentifier(fieldDef.Name));
228 + }
229 #endif // DEBUG
230
197 - foreach (Row row in table.Rows)
198 - {
199 - writer.WriteStartElement(table.Name);
200 -
201 - foreach (Field field in row.Fields)
202 - {
203 - if (null != field.Data)
204 - {
205 - writer.WriteAttributeString(field.Column.Name, field.Data.ToString());
206 - }
207 - }
231 + foreach (var row in group)
232 + {
233 + writer.WriteStartElement(definition.Name);
234
209 - writer.WriteEndElement();
235 + foreach (var field in row.Fields)
236 + {
237 + if (!field.IsNull())
238 + {
239 + writer.WriteAttributeString(field.Definition.Name, field.AsString());
240 }
241 }
212 - }
242
214 - writer.WriteEndElement();
215 - writer.WriteEndDocument();
243 + writer.WriteEndElement();
244 + }
245 }
246 }
247
219 - private WixBundlePayloadRow CreateBootstrapperApplicationManifestPayloadRow(string baManifestPath)
248 + private WixBundlePayloadTuple CreateBootstrapperApplicationManifestPayloadRow(string baManifestPath)
249 {
221 - Table payloadTable = this.Output.EnsureTable(this.TableDefinitions["WixBundlePayload"]);
222 - WixBundlePayloadRow row = (WixBundlePayloadRow)payloadTable.CreateRow(this.BundleRow.SourceLineNumbers);
223 - row.Id = Common.GenerateIdentifier("ux", "BootstrapperApplicationData.xml");
224 - row.Name = "BootstrapperApplicationData.xml";
225 - row.SourceFile = baManifestPath;
226 - row.Compressed = YesNoDefaultType.Yes;
227 - row.UnresolvedSourceFile = baManifestPath;
228 - row.Container = Compiler.BurnUXContainerId;
229 - row.EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnUXContainerEmbeddedIdFormat, this.LastUXPayloadIndex);
230 - row.Packaging = PackagingType.Embedded;
250 + var generatedId = Common.GenerateIdentifier("ux", "BootstrapperApplicationData.xml");
251 +
252 + var tuple = new WixBundlePayloadTuple(this.BundleTuple.SourceLineNumbers, new Identifier(AccessModifier.Private, generatedId))
253 + {
254 + Name = "BootstrapperApplicationData.xml",
255 + SourceFile = new IntermediateFieldPathValue { Path = baManifestPath },
256 + Compressed = true,
257 + UnresolvedSourceFile = baManifestPath,
258 + ContainerRef = BurnConstants.BurnUXContainerName,
259 + EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnUXContainerEmbeddedIdFormat, this.LastUXPayloadIndex),
260 + Packaging = PackagingType.Embedded,
261 + };
262 +
263 + var fileInfo = new FileInfo(baManifestPath);
264
232 - FileInfo fileInfo = new FileInfo(row.SourceFile);
265 + tuple.FileSize = (int)fileInfo.Length;
266
234 - row.FileSize = (int)fileInfo.Length;
267 + tuple.Hash = BundleHashAlgorithm.Hash(fileInfo);
268
236 - row.Hash = Common.GetFileHash(fileInfo.FullName);
269 + this.Section.Tuples.Add(tuple);
270
238 - return row;
271 + return tuple;
272 }
240 -#endif
273 }
274 }
src/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs new
+171
@@ -0,0 +1,171 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core.Burn.Bundles
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using System.IO;
8 + using System.Reflection;
9 + using WixToolset.Data;
10 + using WixToolset.Data.Burn;
11 + using WixToolset.Data.Tuples;
12 + using WixToolset.Extensibility.Data;
13 + using WixToolset.Extensibility.Services;
14 +
15 + internal class CreateBundleExeCommand
16 + {
17 + public CreateBundleExeCommand(IMessaging messaging, IBackendHelper backendHelper, string intermediateFolder, string outputPath, WixBundleTuple bundleTuple, WixBundleContainerTuple uxContainer, IEnumerable<WixBundleContainerTuple> containers, string burnStubPath)
18 + {
19 + this.Messaging = messaging;
20 + this.BackendHelper = backendHelper;
21 + this.IntermediateFolder = intermediateFolder;
22 + this.OutputPath = outputPath;
23 + this.BundleTuple = bundleTuple;
24 + this.UXContainer = uxContainer;
25 + this.Containers = containers;
26 + this.BurnStubPath = burnStubPath;
27 + }
28 +
29 + public IFileTransfer Transfer { get; private set; }
30 +
31 + private IMessaging Messaging { get; }
32 +
33 + private IBackendHelper BackendHelper { get; }
34 +
35 + private string IntermediateFolder { get; }
36 +
37 + private string OutputPath { get; }
38 +
39 + private WixBundleTuple BundleTuple { get; }
40 +
41 + private WixBundleContainerTuple UXContainer { get; }
42 +
43 + private IEnumerable<WixBundleContainerTuple> Containers { get; }
44 +
45 + private string BurnStubPath { get; }
46 +
47 + public void Execute()
48 + {
49 + var bundleFilename = Path.GetFileName(this.OutputPath);
50 +
51 + // Copy the burn.exe to a writable location then mark it to be moved to its final build location. Note
52 + // that today, the x64 Burn uses the x86 stub.
53 +
54 + var stubFile = this.BurnStubPath;
55 +
56 + if (String.IsNullOrEmpty(stubFile))
57 + {
58 + var stubPlatform = (Platform.X64 == this.BundleTuple.Platform) ? "x86" : this.BundleTuple.Platform.ToString();
59 +
60 + stubFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), stubPlatform, "burn.exe");
61 + }
62 +
63 + var bundleTempPath = Path.Combine(this.IntermediateFolder, bundleFilename);
64 +
65 + this.Messaging.Write(VerboseMessages.GeneratingBundle(bundleTempPath, stubFile));
66 +
67 + if ("setup.exe".Equals(bundleFilename, StringComparison.OrdinalIgnoreCase))
68 + {
69 + this.Messaging.Write(ErrorMessages.InsecureBundleFilename(bundleFilename));
70 + }
71 +
72 + this.Transfer = this.BackendHelper.CreateFileTransfer(bundleTempPath, this.OutputPath, true, this.BundleTuple.SourceLineNumbers);
73 +
74 + File.Copy(stubFile, bundleTempPath, true);
75 + File.SetAttributes(bundleTempPath, FileAttributes.Normal);
76 +
77 + this.UpdateBurnResources(bundleTempPath, this.OutputPath, this.BundleTuple);
78 +
79 + // Update the .wixburn section to point to at the UX and attached container(s) then attach the containers
80 + // if they should be attached.
81 + using (var writer = BurnWriter.Open(this.Messaging, bundleTempPath))
82 + {
83 + var burnStubFile = new FileInfo(bundleTempPath);
84 + writer.InitializeBundleSectionData(burnStubFile.Length, this.BundleTuple.BundleId);
85 +
86 + // Always attach the UX container first
87 + writer.AppendContainer(this.UXContainer.WorkingPath, BurnWriter.Container.UX);
88 +
89 + // Now append all other attached containers
90 + foreach (var container in this.Containers)
91 + {
92 + if (ContainerType.Attached == container.Type)
93 + {
94 + // The container was only created if it had payloads.
95 + if (!String.IsNullOrEmpty(container.WorkingPath) && BurnConstants.BurnUXContainerName != container.Id.Id)
96 + {
97 + writer.AppendContainer(container.WorkingPath, BurnWriter.Container.Attached);
98 + }
99 + }
100 + }
101 + }
102 + }
103 +
104 + private void UpdateBurnResources(string bundleTempPath, string outputPath, WixBundleTuple bundleInfo)
105 + {
106 + var resources = new Dtf.Resources.ResourceCollection();
107 + var version = new Dtf.Resources.VersionResource("#1", 1033);
108 +
109 + version.Load(bundleTempPath);
110 + resources.Add(version);
111 +
112 + // Ensure the bundle info provides a full four part version.
113 + var fourPartVersion = new Version(bundleInfo.Version);
114 + var major = (fourPartVersion.Major < 0) ? 0 : fourPartVersion.Major;
115 + var minor = (fourPartVersion.Minor < 0) ? 0 : fourPartVersion.Minor;
116 + var build = (fourPartVersion.Build < 0) ? 0 : fourPartVersion.Build;
117 + var revision = (fourPartVersion.Revision < 0) ? 0 : fourPartVersion.Revision;
118 +
119 + if (UInt16.MaxValue < major || UInt16.MaxValue < minor || UInt16.MaxValue < build || UInt16.MaxValue < revision)
120 + {
121 + throw new WixException(ErrorMessages.InvalidModuleOrBundleVersion(bundleInfo.SourceLineNumbers, "Bundle", bundleInfo.Version));
122 + }
123 +
124 + fourPartVersion = new Version(major, minor, build, revision);
125 + version.FileVersion = fourPartVersion;
126 + version.ProductVersion = fourPartVersion;
127 +
128 + var strings = version[1033];
129 + strings["LegalCopyright"] = bundleInfo.Copyright;
130 + strings["OriginalFilename"] = Path.GetFileName(outputPath);
131 + strings["FileVersion"] = bundleInfo.Version; // string versions do not have to be four parts.
132 + strings["ProductVersion"] = bundleInfo.Version; // string versions do not have to be four parts.
133 +
134 + if (!String.IsNullOrEmpty(bundleInfo.Name))
135 + {
136 + strings["ProductName"] = bundleInfo.Name;
137 + strings["FileDescription"] = bundleInfo.Name;
138 + }
139 +
140 + if (!String.IsNullOrEmpty(bundleInfo.Manufacturer))
141 + {
142 + strings["CompanyName"] = bundleInfo.Manufacturer;
143 + }
144 + else
145 + {
146 + strings["CompanyName"] = String.Empty;
147 + }
148 +
149 + if (!String.IsNullOrEmpty(bundleInfo.IconSourceFile))
150 + {
151 + var iconGroup = new Dtf.Resources.GroupIconResource("#1", 1033);
152 + iconGroup.ReadFromFile(bundleInfo.IconSourceFile);
153 + resources.Add(iconGroup);
154 +
155 + foreach (var icon in iconGroup.Icons)
156 + {
157 + resources.Add(icon);
158 + }
159 + }
160 +
161 + if (!String.IsNullOrEmpty(bundleInfo.SplashScreenSourceFile))
162 + {
163 + var bitmap = new Dtf.Resources.BitmapResource("#1", 1033);
164 + bitmap.ReadFromFile(bundleInfo.SplashScreenSourceFile);
165 + resources.Add(bitmap);
166 + }
167 +
168 + resources.Save(bundleTempPath);
169 + }
170 + }
171 +}
src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs
+227 -172
@@ -6,76 +6,103 @@ namespace WixToolset.Core.Burn.Bundles
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.Globalization;
9 + using System.IO;
10 using System.Linq;
11 using System.Text;
12 using System.Xml;
13 using WixToolset.Data;
14 + using WixToolset.Data.Burn;
15 + using WixToolset.Data.Tuples;
16 using WixToolset.Extensibility;
17 + using WixToolset.Extensibility.Services;
18
19 internal class CreateBurnManifestCommand
20 {
17 -#if TODO
18 - public IEnumerable<IBurnBackendExtension> BackendExtensions { private get; set; }
21 + public CreateBurnManifestCommand(IMessaging messaging, IEnumerable<IBurnBackendExtension> backendExtensions, string executableName, IntermediateSection section, WixBundleTuple bundleTuple, IEnumerable<WixBundleContainerTuple> containers, WixChainTuple chainTuple, IEnumerable<PackageFacade> orderedPackages, IEnumerable<WixBundleRollbackBoundaryTuple> boundaries, IEnumerable<WixBundlePayloadTuple> uxPayloads, Dictionary<string, WixBundlePayloadTuple> allPayloadsById, IEnumerable<SearchFacade> orderedSearches, IEnumerable<WixBundleCatalogTuple> catalogs, string intermediateFolder)
22 + {
23 + this.Messaging = messaging;
24 + this.BackendExtensions = backendExtensions;
25 + this.ExecutableName = executableName;
26 + this.Section = section;
27 + this.BundleTuple = bundleTuple;
28 + this.Chain = chainTuple;
29 + this.Containers = containers;
30 + this.OrderedPackages = orderedPackages;
31 + this.RollbackBoundaries = boundaries;
32 + this.UXContainerPayloads = uxPayloads;
33 + this.Payloads = allPayloadsById;
34 + this.OrderedSearches = orderedSearches;
35 + this.Catalogs = catalogs;
36 + this.IntermediateFolder = intermediateFolder;
37 + }
38 +
39 + public string OutputPath { get; private set; }
40 +
41 + private IMessaging Messaging { get; }
42
20 - public Output Output { private get; set; }
43 + private IEnumerable<IBurnBackendExtension> BackendExtensions { get; }
44
22 - public string ExecutableName { private get; set; }
45 + private string ExecutableName { get; }
46
24 - public WixBundleRow BundleInfo { private get; set; }
47 + private IntermediateSection Section { get; }
48
26 - public WixChainRow Chain { private get; set; }
49 + private WixBundleTuple BundleTuple { get; }
50
28 - public string OutputPath { private get; set; }
51 + private WixChainTuple Chain { get; }
52
30 - public IEnumerable<WixBundleRollbackBoundaryRow> RollbackBoundaries { private get; set; }
53 + private IEnumerable<WixBundleRollbackBoundaryTuple> RollbackBoundaries { get; }
54
32 - public IEnumerable<PackageFacade> OrderedPackages { private get; set; }
55 + private IEnumerable<PackageFacade> OrderedPackages { get; }
56
34 - public IEnumerable<WixSearchInfo> OrderedSearches { private get; set; }
57 + private IEnumerable<SearchFacade> OrderedSearches { get; }
58
36 - public Dictionary<string, WixBundlePayloadRow> Payloads { private get; set; }
59 + private Dictionary<string, WixBundlePayloadTuple> Payloads { get; }
60
38 - public Dictionary<string, WixBundleContainerRow> Containers { private get; set; }
61 + private IEnumerable<WixBundleContainerTuple> Containers { get; }
62
40 - public IEnumerable<WixBundlePayloadRow> UXContainerPayloads { private get; set; }
63 + private IEnumerable<WixBundlePayloadTuple> UXContainerPayloads { get; }
64
42 - public IEnumerable<WixBundleCatalogRow> Catalogs { private get; set; }
65 + private IEnumerable<WixBundleCatalogTuple> Catalogs { get; }
66 +
67 + private string IntermediateFolder { get; }
68
69 public void Execute()
70 {
46 - using (XmlTextWriter writer = new XmlTextWriter(this.OutputPath, Encoding.UTF8))
71 + this.OutputPath = Path.Combine(this.IntermediateFolder, "bundle-manifest.xml");
72 +
73 + using (var writer = new XmlTextWriter(this.OutputPath, Encoding.UTF8))
74 {
75 writer.WriteStartDocument();
76
77 writer.WriteStartElement("BurnManifest", BurnCommon.BurnNamespace);
78
79 // Write the condition, if there is one
53 - if (null != this.BundleInfo.Condition)
80 + if (null != this.BundleTuple.Condition)
81 {
55 - writer.WriteElementString("Condition", this.BundleInfo.Condition);
82 + writer.WriteElementString("Condition", this.BundleTuple.Condition);
83 }
84
85 // Write the log element if default logging wasn't disabled.
59 - if (!String.IsNullOrEmpty(this.BundleInfo.LogPrefix))
86 + if (!String.IsNullOrEmpty(this.BundleTuple.LogPrefix))
87 {
88 writer.WriteStartElement("Log");
62 - if (!String.IsNullOrEmpty(this.BundleInfo.LogPathVariable))
89 + if (!String.IsNullOrEmpty(this.BundleTuple.LogPathVariable))
90 {
64 - writer.WriteAttributeString("PathVariable", this.BundleInfo.LogPathVariable);
91 + writer.WriteAttributeString("PathVariable", this.BundleTuple.LogPathVariable);
92 }
66 - writer.WriteAttributeString("Prefix", this.BundleInfo.LogPrefix);
67 - writer.WriteAttributeString("Extension", this.BundleInfo.LogExtension);
93 + writer.WriteAttributeString("Prefix", this.BundleTuple.LogPrefix);
94 + writer.WriteAttributeString("Extension", this.BundleTuple.LogExtension);
95 writer.WriteEndElement();
96 }
97
98
99 // Get update if specified.
73 - WixBundleUpdateRow updateRow = this.Output.Tables["WixBundleUpdate"].RowsAs<WixBundleUpdateRow>().FirstOrDefault();
100 + var updateTuple = this.Section.Tuples.OfType<WixBundleUpdateTuple>().FirstOrDefault();
101
75 - if (null != updateRow)
102 + if (null != updateTuple)
103 {
104 writer.WriteStartElement("Update");
78 - writer.WriteAttributeString("Location", updateRow.Location);
105 + writer.WriteAttributeString("Location", updateTuple.Location);
106 writer.WriteEndElement(); // </Update>
107 }
108
@@ -83,23 +110,27 @@ namespace WixToolset.Core.Burn.Bundles
110
111 // For the related bundles with duplicated identifiers the second instance is ignored (i.e. the Duplicates
112 // enumeration in the index row list is not used).
86 - RowIndexedList<WixRelatedBundleRow> relatedBundles = new RowIndexedList<WixRelatedBundleRow>(this.Output.Tables["WixRelatedBundle"]);
113 + var relatedBundles = this.Section.Tuples.OfType<WixRelatedBundleTuple>();
114 + var distinctRelatedBundles = new HashSet<string>();
115
88 - foreach (WixRelatedBundleRow relatedBundle in relatedBundles)
116 + foreach (var relatedBundle in relatedBundles)
117 {
90 - writer.WriteStartElement("RelatedBundle");
91 - writer.WriteAttributeString("Id", relatedBundle.Id);
92 - writer.WriteAttributeString("Action", Convert.ToString(relatedBundle.Action, CultureInfo.InvariantCulture));
93 - writer.WriteEndElement();
118 + if (distinctRelatedBundles.Add(relatedBundle.BundleId))
119 + {
120 + writer.WriteStartElement("RelatedBundle");
121 + writer.WriteAttributeString("Id", relatedBundle.BundleId);
122 + writer.WriteAttributeString("Action", relatedBundle.Action.ToString());
123 + writer.WriteEndElement();
124 + }
125 }
126
127 // Write the variables
97 - IEnumerable<WixBundleVariableRow> variables = this.Output.Tables["WixBundleVariable"].RowsAs<WixBundleVariableRow>();
128 + var variables = this.Section.Tuples.OfType<WixBundleVariableTuple>();
129
99 - foreach (WixBundleVariableRow variable in variables)
130 + foreach (var variable in variables)
131 {
132 writer.WriteStartElement("Variable");
102 - writer.WriteAttributeString("Id", variable.Id);
133 + writer.WriteAttributeString("Id", variable.Id.Id);
134 if (null != variable.Type)
135 {
136 writer.WriteAttributeString("Value", variable.Value);
@@ -111,20 +142,20 @@ namespace WixToolset.Core.Burn.Bundles
142 }
143
144 // Write the searches
114 - foreach (WixSearchInfo searchinfo in this.OrderedSearches)
145 + foreach (var searchinfo in this.OrderedSearches)
146 {
147 searchinfo.WriteXml(writer);
148 }
149
150 // write the UX element
151 writer.WriteStartElement("UX");
121 - if (!String.IsNullOrEmpty(this.BundleInfo.SplashScreenBitmapPath))
152 + if (!String.IsNullOrEmpty(this.BundleTuple.SplashScreenSourceFile))
153 {
154 writer.WriteAttributeString("SplashScreen", "yes");
155 }
156
157 // write the UX allPayloads...
127 - foreach (WixBundlePayloadRow payload in this.UXContainerPayloads)
158 + foreach (var payload in this.UXContainerPayloads)
159 {
160 writer.WriteStartElement("Payload");
161 this.WriteBurnManifestPayloadAttributes(writer, payload, true, this.Payloads);
@@ -136,18 +167,18 @@ namespace WixToolset.Core.Burn.Bundles
167 // write the catalog elements
168 if (this.Catalogs.Any())
169 {
139 - foreach (WixBundleCatalogRow catalog in this.Catalogs)
170 + foreach (var catalog in this.Catalogs)
171 {
172 writer.WriteStartElement("Catalog");
142 - writer.WriteAttributeString("Id", catalog.Id);
143 - writer.WriteAttributeString("Payload", catalog.Payload);
173 + writer.WriteAttributeString("Id", catalog.Id.Id);
174 + writer.WriteAttributeString("Payload", catalog.PayloadRef);
175 writer.WriteEndElement();
176 }
177 }
178
148 - foreach (WixBundleContainerRow container in this.Containers.Values)
179 + foreach (var container in this.Containers)
180 {
150 - if (!String.IsNullOrEmpty(container.WorkingPath) && Compiler.BurnUXContainerId != container.Id)
181 + if (!String.IsNullOrEmpty(container.WorkingPath) && BurnConstants.BurnUXContainerName != container.Id.Id)
182 {
183 writer.WriteStartElement("Container");
184 this.WriteBurnManifestContainerAttributes(writer, this.ExecutableName, container);
@@ -155,9 +186,9 @@ namespace WixToolset.Core.Burn.Bundles
186 }
187 }
188
158 - foreach (WixBundlePayloadRow payload in this.Payloads.Values)
189 + foreach (var payload in this.Payloads.Values)
190 {
160 - if (PackagingType.Embedded == payload.Packaging && Compiler.BurnUXContainerId != payload.Container)
191 + if (PackagingType.Embedded == payload.Packaging && BurnConstants.BurnUXContainerName != payload.ContainerRef)
192 {
193 writer.WriteStartElement("Payload");
194 this.WriteBurnManifestPayloadAttributes(writer, payload, true, this.Payloads);
@@ -171,77 +202,78 @@ namespace WixToolset.Core.Burn.Bundles
202 }
203 }
204
174 - foreach (WixBundleRollbackBoundaryRow rollbackBoundary in this.RollbackBoundaries)
205 + foreach (var rollbackBoundary in this.RollbackBoundaries)
206 {
207 writer.WriteStartElement("RollbackBoundary");
177 - writer.WriteAttributeString("Id", rollbackBoundary.ChainPackageId);
178 - writer.WriteAttributeString("Vital", YesNoType.Yes == rollbackBoundary.Vital ? "yes" : "no");
179 - writer.WriteAttributeString("Transaction", YesNoType.Yes == rollbackBoundary.Transaction ? "yes" : "no");
208 + writer.WriteAttributeString("Id", rollbackBoundary.Id.Id);
209 + writer.WriteAttributeString("Vital", rollbackBoundary.Vital == false ? "no" : "yes");
210 + writer.WriteAttributeString("Transaction", rollbackBoundary.Transaction == true ? "yes" : "no");
211 writer.WriteEndElement();
212 }
213
214 // Write the registration information...
215 writer.WriteStartElement("Registration");
216
186 - writer.WriteAttributeString("Id", this.BundleInfo.BundleId.ToString("B"));
217 + writer.WriteAttributeString("Id", this.BundleTuple.BundleId);
218 writer.WriteAttributeString("ExecutableName", this.ExecutableName);
188 - writer.WriteAttributeString("PerMachine", this.BundleInfo.PerMachine ? "yes" : "no");
189 - writer.WriteAttributeString("Tag", this.BundleInfo.Tag);
190 - writer.WriteAttributeString("Version", this.BundleInfo.Version);
191 - writer.WriteAttributeString("ProviderKey", this.BundleInfo.ProviderKey);
219 + writer.WriteAttributeString("PerMachine", this.BundleTuple.PerMachine ? "yes" : "no");
220 + writer.WriteAttributeString("Tag", this.BundleTuple.Tag);
221 + writer.WriteAttributeString("Version", this.BundleTuple.Version);
222 + writer.WriteAttributeString("ProviderKey", this.BundleTuple.ProviderKey);
223
224 writer.WriteStartElement("Arp");
194 - writer.WriteAttributeString("Register", (0 < this.BundleInfo.DisableModify && this.BundleInfo.DisableRemove) ? "no" : "yes"); // do not register if disabled modify and remove.
195 - writer.WriteAttributeString("DisplayName", this.BundleInfo.Name);
196 - writer.WriteAttributeString("DisplayVersion", this.BundleInfo.Version);
225 + writer.WriteAttributeString("Register", (this.BundleTuple.DisableModify || this.BundleTuple.SingleChangeUninstallButton) && this.BundleTuple.DisableRemove ? "no" : "yes"); // do not register if disabled modify and remove.
226 + writer.WriteAttributeString("DisplayName", this.BundleTuple.Name);
227 + writer.WriteAttributeString("DisplayVersion", this.BundleTuple.Version);
228
198 - if (!String.IsNullOrEmpty(this.BundleInfo.Publisher))
229 + if (!String.IsNullOrEmpty(this.BundleTuple.Manufacturer))
230 {
200 - writer.WriteAttributeString("Publisher", this.BundleInfo.Publisher);
231 + writer.WriteAttributeString("Publisher", this.BundleTuple.Manufacturer);
232 }
233
203 - if (!String.IsNullOrEmpty(this.BundleInfo.HelpLink))
234 + if (!String.IsNullOrEmpty(this.BundleTuple.HelpUrl))
235 {
205 - writer.WriteAttributeString("HelpLink", this.BundleInfo.HelpLink);
236 + writer.WriteAttributeString("HelpLink", this.BundleTuple.HelpUrl);
237 }
238
208 - if (!String.IsNullOrEmpty(this.BundleInfo.HelpTelephone))
239 + if (!String.IsNullOrEmpty(this.BundleTuple.HelpTelephone))
240 {
210 - writer.WriteAttributeString("HelpTelephone", this.BundleInfo.HelpTelephone);
241 + writer.WriteAttributeString("HelpTelephone", this.BundleTuple.HelpTelephone);
242 }
243
213 - if (!String.IsNullOrEmpty(this.BundleInfo.AboutUrl))
244 + if (!String.IsNullOrEmpty(this.BundleTuple.AboutUrl))
245 {
215 - writer.WriteAttributeString("AboutUrl", this.BundleInfo.AboutUrl);
246 + writer.WriteAttributeString("AboutUrl", this.BundleTuple.AboutUrl);
247 }
248
218 - if (!String.IsNullOrEmpty(this.BundleInfo.UpdateUrl))
249 + if (!String.IsNullOrEmpty(this.BundleTuple.UpdateUrl))
250 {
220 - writer.WriteAttributeString("UpdateUrl", this.BundleInfo.UpdateUrl);
251 + writer.WriteAttributeString("UpdateUrl", this.BundleTuple.UpdateUrl);
252 }
253
223 - if (!String.IsNullOrEmpty(this.BundleInfo.ParentName))
254 + if (!String.IsNullOrEmpty(this.BundleTuple.ParentName))
255 {
225 - writer.WriteAttributeString("ParentDisplayName", this.BundleInfo.ParentName);
256 + writer.WriteAttributeString("ParentDisplayName", this.BundleTuple.ParentName);
257 }
258
228 - if (1 == this.BundleInfo.DisableModify)
259 + if (this.BundleTuple.DisableModify)
260 {
261 writer.WriteAttributeString("DisableModify", "yes");
262 }
232 - else if (2 == this.BundleInfo.DisableModify)
263 +
264 + if (this.BundleTuple.DisableRemove)
265 {
234 - writer.WriteAttributeString("DisableModify", "button");
266 + writer.WriteAttributeString("DisableRemove", "yes");
267 }
268
237 - if (this.BundleInfo.DisableRemove)
269 + if (this.BundleTuple.SingleChangeUninstallButton)
270 {
239 - writer.WriteAttributeString("DisableRemove", "yes");
271 + writer.WriteAttributeString("DisableModify", "button");
272 }
273 writer.WriteEndElement(); // </Arp>
274
275 // Get update registration if specified.
244 - WixUpdateRegistrationRow updateRegistrationInfo = this.Output.Tables["WixUpdateRegistration"].RowsAs<WixUpdateRegistrationRow>().FirstOrDefault();
276 + var updateRegistrationInfo = this.Section.Tuples.OfType<WixUpdateRegistrationTuple>().FirstOrDefault();
277
278 if (null != updateRegistrationInfo)
279 {
@@ -263,9 +295,9 @@ namespace WixToolset.Core.Burn.Bundles
295 writer.WriteEndElement(); // </Update>
296 }
297
266 - IEnumerable<Row> bundleTags = this.Output.Tables["WixBundleTag"].RowsAs<Row>();
267 -
268 - foreach (Row row in bundleTags)
298 +#if TODO // Handle SWID Tags
299 + var bundleTags = this.Output.Tables["WixBundleTag"].RowsAs<Row>();
300 + foreach (var row in bundleTags)
301 {
302 writer.WriteStartElement("SoftwareTag");
303 writer.WriteAttributeString("Filename", (string)row[0]);
@@ -273,6 +305,7 @@ namespace WixToolset.Core.Burn.Bundles
305 writer.WriteCData((string)row[4]);
306 writer.WriteEndElement();
307 }
308 +#endif
309
310 writer.WriteEndElement(); // </Register>
311
@@ -294,25 +327,28 @@ namespace WixToolset.Core.Burn.Bundles
327 }
328
329 // Index a few tables by package.
297 - ILookup<string, WixBundlePatchTargetCodeRow> targetCodesByPatch = this.Output.Tables["WixBundlePatchTargetCode"].RowsAs<WixBundlePatchTargetCodeRow>().ToLookup(r => r.MspPackageId);
298 - ILookup<string, WixBundleMsiFeatureRow> msiFeaturesByPackage = this.Output.Tables["WixBundleMsiFeature"].RowsAs<WixBundleMsiFeatureRow>().ToLookup(r => r.ChainPackageId);
299 - ILookup<string, WixBundleMsiPropertyRow> msiPropertiesByPackage = this.Output.Tables["WixBundleMsiProperty"].RowsAs<WixBundleMsiPropertyRow>().ToLookup(r => r.ChainPackageId);
300 - ILookup<string, WixBundlePayloadRow> payloadsByPackage = this.Payloads.Values.ToLookup(p => p.Package);
301 - ILookup<string, WixBundleRelatedPackageRow> relatedPackagesByPackage = this.Output.Tables["WixBundleRelatedPackage"].RowsAs<WixBundleRelatedPackageRow>().ToLookup(r => r.ChainPackageId);
302 - ILookup<string, WixBundleSlipstreamMspRow> slipstreamMspsByPackage = this.Output.Tables["WixBundleSlipstreamMsp"].RowsAs<WixBundleSlipstreamMspRow>().ToLookup(r => r.ChainPackageId);
303 - ILookup<string, WixBundlePackageExitCodeRow> exitCodesByPackage = this.Output.Tables["WixBundlePackageExitCode"].RowsAs<WixBundlePackageExitCodeRow>().ToLookup(r => r.ChainPackageId);
304 - ILookup<string, WixBundlePackageCommandLineRow> commandLinesByPackage = this.Output.Tables["WixBundlePackageCommandLine"].RowsAs<WixBundlePackageCommandLineRow>().ToLookup(r => r.ChainPackageId);
330 + var targetCodesByPatch = this.Section.Tuples.OfType<WixBundlePatchTargetCodeTuple>().ToLookup(r => r.PackageRef);
331 + var msiFeaturesByPackage = this.Section.Tuples.OfType<WixBundleMsiFeatureTuple>().ToLookup(r => r.PackageRef);
332 + var msiPropertiesByPackage = this.Section.Tuples.OfType<WixBundleMsiPropertyTuple>().ToLookup(r => r.PackageRef);
333 + var payloadsByPackage = this.Payloads.Values.ToLookup(p => p.PackageRef);
334 + var relatedPackagesByPackage = this.Section.Tuples.OfType<WixBundleRelatedPackageTuple>().ToLookup(r => r.PackageRef);
335 + var slipstreamMspsByPackage = this.Section.Tuples.OfType<WixBundleSlipstreamMspTuple>().ToLookup(r => r.MspPackageRef);
336 + var exitCodesByPackage = this.Section.Tuples.OfType<WixBundlePackageExitCodeTuple>().ToLookup(r => r.ChainPackageId);
337 + var commandLinesByPackage = this.Section.Tuples.OfType<WixBundlePackageCommandLineTuple>().ToLookup(r => r.WixBundlePackageRef);
338 +
339 + var dependenciesByPackage = this.Section.Tuples.OfType<ProvidesDependencyTuple>().ToLookup(p => p.PackageRef);
340 +
341
342 // Build up the list of target codes from all the MSPs in the chain.
307 - List<WixBundlePatchTargetCodeRow> targetCodes = new List<WixBundlePatchTargetCodeRow>();
343 + var targetCodes = new List<WixBundlePatchTargetCodeTuple>();
344
309 - foreach (PackageFacade package in this.OrderedPackages)
345 + foreach (var package in this.OrderedPackages)
346 {
311 - writer.WriteStartElement(String.Format(CultureInfo.InvariantCulture, "{0}Package", package.Package.Type));
347 + writer.WriteStartElement(String.Format(CultureInfo.InvariantCulture, "{0}Package", package.PackageTuple.Type));
348
313 - writer.WriteAttributeString("Id", package.Package.WixChainItemId);
349 + writer.WriteAttributeString("Id", package.PackageId);
350
315 - switch (package.Package.Cache)
351 + switch (package.PackageTuple.Cache)
352 {
353 case YesNoAlwaysType.No:
354 writer.WriteAttributeString("Cache", "no");
@@ -325,74 +361,74 @@ namespace WixToolset.Core.Burn.Bundles
361 break;
362 }
363
328 - writer.WriteAttributeString("CacheId", package.Package.CacheId);
329 - writer.WriteAttributeString("InstallSize", Convert.ToString(package.Package.InstallSize));
330 - writer.WriteAttributeString("Size", Convert.ToString(package.Package.Size));
331 - writer.WriteAttributeString("PerMachine", YesNoDefaultType.Yes == package.Package.PerMachine ? "yes" : "no");
332 - writer.WriteAttributeString("Permanent", package.Package.Permanent ? "yes" : "no");
333 - writer.WriteAttributeString("Vital", (YesNoType.Yes == package.Package.Vital) ? "yes" : "no");
364 + writer.WriteAttributeString("CacheId", package.PackageTuple.CacheId);
365 + writer.WriteAttributeString("InstallSize", Convert.ToString(package.PackageTuple.InstallSize));
366 + writer.WriteAttributeString("Size", Convert.ToString(package.PackageTuple.Size));
367 + writer.WriteAttributeString("PerMachine", YesNoDefaultType.Yes == package.PackageTuple.PerMachine ? "yes" : "no");
368 + writer.WriteAttributeString("Permanent", package.PackageTuple.Permanent ? "yes" : "no");
369 + writer.WriteAttributeString("Vital", package.PackageTuple.Vital == false ? "no" : "yes");
370
335 - if (null != package.Package.RollbackBoundary)
371 + if (null != package.PackageTuple.RollbackBoundaryRef)
372 {
337 - writer.WriteAttributeString("RollbackBoundaryForward", package.Package.RollbackBoundary);
373 + writer.WriteAttributeString("RollbackBoundaryForward", package.PackageTuple.RollbackBoundaryRef);
374 }
375
340 - if (!String.IsNullOrEmpty(package.Package.RollbackBoundaryBackward))
376 + if (!String.IsNullOrEmpty(package.PackageTuple.RollbackBoundaryBackwardRef))
377 {
342 - writer.WriteAttributeString("RollbackBoundaryBackward", package.Package.RollbackBoundaryBackward);
378 + writer.WriteAttributeString("RollbackBoundaryBackward", package.PackageTuple.RollbackBoundaryBackwardRef);
379 }
380
345 - if (!String.IsNullOrEmpty(package.Package.LogPathVariable))
381 + if (!String.IsNullOrEmpty(package.PackageTuple.LogPathVariable))
382 {
347 - writer.WriteAttributeString("LogPathVariable", package.Package.LogPathVariable);
383 + writer.WriteAttributeString("LogPathVariable", package.PackageTuple.LogPathVariable);
384 }
385
350 - if (!String.IsNullOrEmpty(package.Package.RollbackLogPathVariable))
386 + if (!String.IsNullOrEmpty(package.PackageTuple.RollbackLogPathVariable))
387 {
352 - writer.WriteAttributeString("RollbackLogPathVariable", package.Package.RollbackLogPathVariable);
388 + writer.WriteAttributeString("RollbackLogPathVariable", package.PackageTuple.RollbackLogPathVariable);
389 }
390
355 - if (!String.IsNullOrEmpty(package.Package.InstallCondition))
391 + if (!String.IsNullOrEmpty(package.PackageTuple.InstallCondition))
392 {
357 - writer.WriteAttributeString("InstallCondition", package.Package.InstallCondition);
393 + writer.WriteAttributeString("InstallCondition", package.PackageTuple.InstallCondition);
394 }
395
360 - if (WixBundlePackageType.Exe == package.Package.Type)
396 + if (package.SpecificPackageTuple is WixBundleExePackageTuple exePackage) // EXE
397 {
362 - writer.WriteAttributeString("DetectCondition", package.ExePackage.DetectCondition);
363 - writer.WriteAttributeString("InstallArguments", package.ExePackage.InstallCommand);
364 - writer.WriteAttributeString("UninstallArguments", package.ExePackage.UninstallCommand);
365 - writer.WriteAttributeString("RepairArguments", package.ExePackage.RepairCommand);
366 - writer.WriteAttributeString("Repairable", package.ExePackage.Repairable ? "yes" : "no");
367 - if (!String.IsNullOrEmpty(package.ExePackage.ExeProtocol))
398 + writer.WriteAttributeString("DetectCondition", exePackage.DetectCondition);
399 + writer.WriteAttributeString("InstallArguments", exePackage.InstallCommand);
400 + writer.WriteAttributeString("UninstallArguments", exePackage.UninstallCommand);
401 + writer.WriteAttributeString("RepairArguments", exePackage.RepairCommand);
402 + writer.WriteAttributeString("Repairable", exePackage.Repairable ? "yes" : "no");
403 + if (!String.IsNullOrEmpty(exePackage.ExeProtocol))
404 {
369 - writer.WriteAttributeString("Protocol", package.ExePackage.ExeProtocol);
405 + writer.WriteAttributeString("Protocol", exePackage.ExeProtocol);
406 }
407 }
372 - else if (WixBundlePackageType.Msi == package.Package.Type)
408 + else if (package.SpecificPackageTuple is WixBundleMsiPackageTuple msiPackage) // MSI
409 {
374 - writer.WriteAttributeString("ProductCode", package.MsiPackage.ProductCode);
375 - writer.WriteAttributeString("Language", package.MsiPackage.ProductLanguage.ToString(CultureInfo.InvariantCulture));
376 - writer.WriteAttributeString("Version", package.MsiPackage.ProductVersion);
377 - writer.WriteAttributeString("DisplayInternalUI", package.MsiPackage.DisplayInternalUI ? "yes" : "no");
378 - if (!String.IsNullOrEmpty(package.MsiPackage.UpgradeCode))
410 + writer.WriteAttributeString("ProductCode", msiPackage.ProductCode);
411 + writer.WriteAttributeString("Language", msiPackage.ProductLanguage.ToString(CultureInfo.InvariantCulture));
412 + writer.WriteAttributeString("Version", msiPackage.ProductVersion);
413 + writer.WriteAttributeString("DisplayInternalUI", msiPackage.DisplayInternalUI ? "yes" : "no");
414 + if (!String.IsNullOrEmpty(msiPackage.UpgradeCode))
415 {
380 - writer.WriteAttributeString("UpgradeCode", package.MsiPackage.UpgradeCode);
416 + writer.WriteAttributeString("UpgradeCode", msiPackage.UpgradeCode);
417 }
418 }
383 - else if (WixBundlePackageType.Msp == package.Package.Type)
419 + else if (package.SpecificPackageTuple is WixBundleMspPackageTuple mspPackage) // MSP
420 {
385 - writer.WriteAttributeString("PatchCode", package.MspPackage.PatchCode);
386 - writer.WriteAttributeString("PatchXml", package.MspPackage.PatchXml);
387 - writer.WriteAttributeString("DisplayInternalUI", package.MspPackage.DisplayInternalUI ? "yes" : "no");
421 + writer.WriteAttributeString("PatchCode", mspPackage.PatchCode);
422 + writer.WriteAttributeString("PatchXml", mspPackage.PatchXml);
423 + writer.WriteAttributeString("DisplayInternalUI", mspPackage.DisplayInternalUI ? "yes" : "no");
424
425 // If there is still a chance that all of our patches will target a narrow set of
426 // product codes, add the patch list to the overall list.
427 if (null != targetCodes)
428 {
393 - if (!package.MspPackage.TargetUnspecified)
429 + if (!mspPackage.TargetUnspecified)
430 {
395 - IEnumerable<WixBundlePatchTargetCodeRow> patchTargetCodes = targetCodesByPatch[package.MspPackage.ChainPackageId];
431 + var patchTargetCodes = targetCodesByPatch[mspPackage.Id.Id];
432
433 targetCodes.AddRange(patchTargetCodes);
434 }
@@ -402,24 +438,24 @@ namespace WixToolset.Core.Burn.Bundles
438 }
439 }
440 }
405 - else if (WixBundlePackageType.Msu == package.Package.Type)
441 + else if (package.SpecificPackageTuple is WixBundleMsuPackageTuple msuPackage) // MSU
442 {
407 - writer.WriteAttributeString("DetectCondition", package.MsuPackage.DetectCondition);
408 - writer.WriteAttributeString("KB", package.MsuPackage.MsuKB);
443 + writer.WriteAttributeString("DetectCondition", msuPackage.DetectCondition);
444 + writer.WriteAttributeString("KB", msuPackage.MsuKB);
445 }
446
411 - IEnumerable<WixBundleMsiFeatureRow> packageMsiFeatures = msiFeaturesByPackage[package.Package.WixChainItemId];
447 + var packageMsiFeatures = msiFeaturesByPackage[package.PackageId];
448
413 - foreach (WixBundleMsiFeatureRow feature in packageMsiFeatures)
449 + foreach (var feature in packageMsiFeatures)
450 {
451 writer.WriteStartElement("MsiFeature");
452 writer.WriteAttributeString("Id", feature.Name);
453 writer.WriteEndElement();
454 }
455
420 - IEnumerable<WixBundleMsiPropertyRow> packageMsiProperties = msiPropertiesByPackage[package.Package.WixChainItemId];
456 + var packageMsiProperties = msiPropertiesByPackage[package.PackageId];
457
422 - foreach (WixBundleMsiPropertyRow msiProperty in packageMsiProperties)
458 + foreach (var msiProperty in packageMsiProperties)
459 {
460 writer.WriteStartElement("MsiProperty");
461 writer.WriteAttributeString("Id", msiProperty.Name);
@@ -431,18 +467,18 @@ namespace WixToolset.Core.Burn.Bundles
467 writer.WriteEndElement();
468 }
469
434 - IEnumerable<WixBundleSlipstreamMspRow> packageSlipstreamMsps = slipstreamMspsByPackage[package.Package.WixChainItemId];
470 + var packageSlipstreamMsps = slipstreamMspsByPackage[package.PackageId];
471
436 - foreach (WixBundleSlipstreamMspRow slipstreamMsp in packageSlipstreamMsps)
472 + foreach (var slipstreamMsp in packageSlipstreamMsps)
473 {
474 writer.WriteStartElement("SlipstreamMsp");
439 - writer.WriteAttributeString("Id", slipstreamMsp.MspPackageId);
475 + writer.WriteAttributeString("Id", slipstreamMsp.MspPackageRef);
476 writer.WriteEndElement();
477 }
478
443 - IEnumerable<WixBundlePackageExitCodeRow> packageExitCodes = exitCodesByPackage[package.Package.WixChainItemId];
479 + var packageExitCodes = exitCodesByPackage[package.PackageId];
480
445 - foreach (WixBundlePackageExitCodeRow exitCode in packageExitCodes)
481 + foreach (var exitCode in packageExitCodes)
482 {
483 writer.WriteStartElement("ExitCode");
484
@@ -459,9 +495,9 @@ namespace WixToolset.Core.Burn.Bundles
495 writer.WriteEndElement();
496 }
497
462 - IEnumerable<WixBundlePackageCommandLineRow> packageCommandLines = commandLinesByPackage[package.Package.WixChainItemId];
498 + var packageCommandLines = commandLinesByPackage[package.PackageId];
499
464 - foreach (WixBundlePackageCommandLineRow commandLine in packageCommandLines)
500 + foreach (var commandLine in packageCommandLines)
501 {
502 writer.WriteStartElement("CommandLine");
503 writer.WriteAttributeString("InstallArgument", commandLine.InstallArgument);
@@ -472,18 +508,38 @@ namespace WixToolset.Core.Burn.Bundles
508 }
509
510 // Output the dependency information.
475 - foreach (ProvidesDependency dependency in package.Provides)
511 + var dependencies = dependenciesByPackage[package.PackageId];
512 +
513 + foreach (var dependency in dependencies)
514 {
477 - // TODO: Add to wixpdb as an imported table, or link package wixpdbs to bundle wixpdbs.
478 - dependency.WriteXml(writer);
515 + writer.WriteStartElement("Provides");
516 + writer.WriteAttributeString("Key", dependency.Key);
517 +
518 + if (!String.IsNullOrEmpty(dependency.Version))
519 + {
520 + writer.WriteAttributeString("Version", dependency.Version);
521 + }
522 +
523 + if (!String.IsNullOrEmpty(dependency.DisplayName))
524 + {
525 + writer.WriteAttributeString("DisplayName", dependency.DisplayName);
526 + }
527 +
528 + if (dependency.Imported)
529 + {
530 + // The package dependency was explicitly authored into the manifest.
531 + writer.WriteAttributeString("Imported", "yes");
532 + }
533 +
534 + writer.WriteEndElement();
535 }
536
481 - IEnumerable<WixBundleRelatedPackageRow> packageRelatedPackages = relatedPackagesByPackage[package.Package.WixChainItemId];
537 + var packageRelatedPackages = relatedPackagesByPackage[package.PackageId];
538
483 - foreach (WixBundleRelatedPackageRow related in packageRelatedPackages)
539 + foreach (var related in packageRelatedPackages)
540 {
541 writer.WriteStartElement("RelatedPackage");
486 - writer.WriteAttributeString("Id", related.Id);
542 + writer.WriteAttributeString("Id", related.RelatedId);
543 if (!String.IsNullOrEmpty(related.MinVersion))
544 {
545 writer.WriteAttributeString("MinVersion", related.MinVersion);
@@ -496,7 +552,7 @@ namespace WixToolset.Core.Burn.Bundles
552 }
553 writer.WriteAttributeString("OnlyDetect", related.OnlyDetect ? "yes" : "no");
554
499 - string[] relatedLanguages = related.Languages.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
555 + var relatedLanguages = related.Languages.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
556
557 if (0 < relatedLanguages.Length)
558 {
@@ -513,17 +569,17 @@ namespace WixToolset.Core.Burn.Bundles
569
570 // Write any contained Payloads with the PackagePayload being first
571 writer.WriteStartElement("PayloadRef");
516 - writer.WriteAttributeString("Id", package.Package.PackagePayload);
572 + writer.WriteAttributeString("Id", package.PackageTuple.PayloadRef);
573 writer.WriteEndElement();
574
519 - IEnumerable<WixBundlePayloadRow> packagePayloads = payloadsByPackage[package.Package.WixChainItemId];
575 + var packagePayloads = payloadsByPackage[package.PackageId];
576
521 - foreach (WixBundlePayloadRow payload in packagePayloads)
577 + foreach (var payload in packagePayloads)
578 {
523 - if (payload.Id != package.Package.PackagePayload)
579 + if (payload.Id.Id != package.PackageTuple.PayloadRef)
580 {
581 writer.WriteStartElement("PayloadRef");
526 - writer.WriteAttributeString("Id", payload.Id);
582 + writer.WriteAttributeString("Id", payload.Id.Id);
583 writer.WriteEndElement();
584 }
585 }
@@ -534,7 +590,7 @@ namespace WixToolset.Core.Burn.Bundles
590
591 if (null != targetCodes)
592 {
537 - foreach (WixBundlePatchTargetCodeRow targetCode in targetCodes)
593 + foreach (var targetCode in targetCodes)
594 {
595 writer.WriteStartElement("PatchTargetCode");
596 writer.WriteAttributeString("TargetCode", targetCode.TargetCode);
@@ -544,12 +600,12 @@ namespace WixToolset.Core.Burn.Bundles
600 }
601
602 // Write the ApprovedExeForElevation elements.
547 - IEnumerable<WixApprovedExeForElevationRow> approvedExesForElevation = this.Output.Tables["WixApprovedExeForElevation"].RowsAs<WixApprovedExeForElevationRow>();
603 + var approvedExesForElevation = this.Section.Tuples.OfType<WixApprovedExeForElevationTuple>();
604
549 - foreach (WixApprovedExeForElevationRow approvedExeForElevation in approvedExesForElevation)
605 + foreach (var approvedExeForElevation in approvedExesForElevation)
606 {
607 writer.WriteStartElement("ApprovedExeForElevation");
552 - writer.WriteAttributeString("Id", approvedExeForElevation.Id);
608 + writer.WriteAttributeString("Id", approvedExeForElevation.Id.Id);
609 writer.WriteAttributeString("Key", approvedExeForElevation.Key);
610
611 if (!String.IsNullOrEmpty(approvedExeForElevation.ValueName))
@@ -569,15 +625,15 @@ namespace WixToolset.Core.Burn.Bundles
625 }
626 }
627
572 - private void WriteBurnManifestContainerAttributes(XmlTextWriter writer, string executableName, WixBundleContainerRow container)
628 + private void WriteBurnManifestContainerAttributes(XmlTextWriter writer, string executableName, WixBundleContainerTuple container)
629 {
574 - writer.WriteAttributeString("Id", container.Id);
630 + writer.WriteAttributeString("Id", container.Id.Id);
631 writer.WriteAttributeString("FileSize", container.Size.ToString(CultureInfo.InvariantCulture));
632 writer.WriteAttributeString("Hash", container.Hash);
633
634 if (ContainerType.Detached == container.Type)
635 {
580 - string resolvedUrl = this.ResolveUrl(container.DownloadUrl, null, null, container.Id, container.Name);
636 + string resolvedUrl = this.ResolveUrl(container.DownloadUrl, null, null, container.Id.Id, container.Name);
637 if (!String.IsNullOrEmpty(resolvedUrl))
638 {
639 writer.WriteAttributeString("DownloadUrl", resolvedUrl);
@@ -593,7 +649,7 @@ namespace WixToolset.Core.Burn.Bundles
649 {
650 if (!String.IsNullOrEmpty(container.DownloadUrl))
651 {
596 - Messaging.Instance.OnMessage(WixWarnings.DownloadUrlNotSupportedForAttachedContainers(container.SourceLineNumbers, container.Id));
652 + this.Messaging.Write(WarningMessages.DownloadUrlNotSupportedForAttachedContainers(container.SourceLineNumbers, container.Id.Id));
653 }
654
655 writer.WriteAttributeString("FilePath", executableName); // attached containers use the name of the bundle since they are attached to the executable.
@@ -603,11 +659,11 @@ namespace WixToolset.Core.Burn.Bundles
659 }
660 }
661
606 - private void WriteBurnManifestPayloadAttributes(XmlTextWriter writer, WixBundlePayloadRow payload, bool embeddedOnly, Dictionary<string, WixBundlePayloadRow> allPayloads)
662 + private void WriteBurnManifestPayloadAttributes(XmlTextWriter writer, WixBundlePayloadTuple payload, bool embeddedOnly, Dictionary<string, WixBundlePayloadTuple> allPayloads)
663 {
664 Debug.Assert(!embeddedOnly || PackagingType.Embedded == payload.Packaging);
665
610 - writer.WriteAttributeString("Id", payload.Id);
666 + writer.WriteAttributeString("Id", payload.Id.Id);
667 writer.WriteAttributeString("FilePath", payload.Name);
668 writer.WriteAttributeString("FileSize", payload.FileSize.ToString(CultureInfo.InvariantCulture));
669 writer.WriteAttributeString("Hash", payload.Hash);
@@ -632,22 +688,22 @@ namespace WixToolset.Core.Burn.Bundles
688 case PackagingType.Embedded: // this means it's in a container.
689 if (!String.IsNullOrEmpty(payload.DownloadUrl))
690 {
635 - Messaging.Instance.OnMessage(WixWarnings.DownloadUrlNotSupportedForEmbeddedPayloads(payload.SourceLineNumbers, payload.Id));
691 + this.Messaging.Write(WarningMessages.DownloadUrlNotSupportedForEmbeddedPayloads(payload.SourceLineNumbers, payload.Id.Id));
692 }
693
694 writer.WriteAttributeString("Packaging", "embedded");
695 writer.WriteAttributeString("SourcePath", payload.EmbeddedId);
696
641 - if (Compiler.BurnUXContainerId != payload.Container)
697 + if (BurnConstants.BurnUXContainerName != payload.ContainerRef)
698 {
643 - writer.WriteAttributeString("Container", payload.Container);
699 + writer.WriteAttributeString("Container", payload.ContainerRef);
700 }
701 break;
702
703 case PackagingType.External:
648 - string packageId = payload.ParentPackagePayload;
649 - string parentUrl = payload.ParentPackagePayload == null ? null : allPayloads[payload.ParentPackagePayload].DownloadUrl;
650 - string resolvedUrl = this.ResolveUrl(payload.DownloadUrl, parentUrl, packageId, payload.Id, payload.Name);
704 + var packageId = payload.ParentPackagePayloadRef;
705 + var parentUrl = payload.ParentPackagePayloadRef == null ? null : allPayloads[payload.ParentPackagePayloadRef].DownloadUrl;
706 + var resolvedUrl = this.ResolveUrl(payload.DownloadUrl, parentUrl, packageId, payload.Id.Id, payload.Name);
707 if (!String.IsNullOrEmpty(resolvedUrl))
708 {
709 writer.WriteAttributeString("DownloadUrl", resolvedUrl);
@@ -662,9 +718,9 @@ namespace WixToolset.Core.Burn.Bundles
718 break;
719 }
720
665 - if (!String.IsNullOrEmpty(payload.Catalog))
721 + if (!String.IsNullOrEmpty(payload.CatalogRef))
722 {
667 - writer.WriteAttributeString("Catalog", payload.Catalog);
723 + writer.WriteAttributeString("Catalog", payload.CatalogRef);
724 }
725 }
726
@@ -682,6 +738,5 @@ namespace WixToolset.Core.Burn.Bundles
738
739 return resolved;
740 }
685 -#endif
741 }
742 }
src/WixToolset.Core.Burn/Bundles/CreateContainerCommand.cs
+34 -25
@@ -4,24 +4,39 @@ namespace WixToolset.Core.Burn.Bundles
4 {
5 using System;
6 using System.Collections.Generic;
7 - using System.Diagnostics;
7 using System.IO;
8 using System.Linq;
9 + using WixToolset.Core.Native;
10 using WixToolset.Data;
11 + using WixToolset.Data.Tuples;
12
13 /// <summary>
14 /// Creates cabinet files.
15 /// </summary>
16 internal class CreateContainerCommand
17 {
17 -#if TODO
18 - public CompressionLevel DefaultCompressionLevel { private get; set; }
18 + public CreateContainerCommand(IEnumerable<WixBundlePayloadTuple> payloads, string outputPath, CompressionLevel? compressionLevel)
19 + {
20 + this.Payloads = payloads;
21 + this.OutputPath = outputPath;
22 + this.CompressionLevel = compressionLevel;
23 + }
24 +
25 + public CreateContainerCommand(string manifestPath, IEnumerable<WixBundlePayloadTuple> payloads, string outputPath, CompressionLevel? compressionLevel)
26 + {
27 + this.ManifestFile = manifestPath;
28 + this.Payloads = payloads;
29 + this.OutputPath = outputPath;
30 + this.CompressionLevel = compressionLevel;
31 + }
32 +
33 + private CompressionLevel? CompressionLevel { get; }
34
20 - public IEnumerable<WixBundlePayloadRow> Payloads { private get; set; }
35 + private string ManifestFile { get; }
36
22 - public string ManifestFile { private get; set; }
37 + private string OutputPath { get; }
38
24 - public string OutputPath { private get; set; }
39 + private IEnumerable<WixBundlePayloadTuple> Payloads { get; }
40
41 public string Hash { get; private set; }
42
@@ -29,40 +44,34 @@ namespace WixToolset.Core.Burn.Bundles
44
45 public void Execute()
46 {
32 - int payloadCount = this.Payloads.Count(); // The number of embedded payloads
47 + var payloadCount = this.Payloads.Count(); // The number of embedded payloads
48
49 if (!String.IsNullOrEmpty(this.ManifestFile))
50 {
51 ++payloadCount;
52 }
53
39 - using (var cab = new WixCreateCab(Path.GetFileName(this.OutputPath), Path.GetDirectoryName(this.OutputPath), payloadCount, 0, 0, this.DefaultCompressionLevel))
40 - {
41 - // If a manifest was provided always add it as "payload 0" to the container.
42 - if (!String.IsNullOrEmpty(this.ManifestFile))
43 - {
44 - cab.AddFile(this.ManifestFile, "0");
45 - }
54 + var cabinetPath = Path.GetFullPath(this.OutputPath);
55
47 - foreach (WixBundlePayloadRow payload in this.Payloads)
48 - {
49 - Debug.Assert(PackagingType.Embedded == payload.Packaging);
56 + var files = new List<CabinetCompressFile>();
57
51 - Messaging.Instance.OnMessage(WixVerboses.LoadingPayload(payload.FullFileName));
58 + // If a manifest was provided always add it as "payload 0" to the container.
59 + if (!String.IsNullOrEmpty(this.ManifestFile))
60 + {
61 + files.Add(new CabinetCompressFile(this.ManifestFile, "0"));
62 + }
63
53 - cab.AddFile(payload.FullFileName, payload.EmbeddedId);
54 - }
64 + files.AddRange(this.Payloads.Select(p => new CabinetCompressFile(p.SourceFile.Path, p.EmbeddedId)));
65
56 - cab.Complete();
57 - }
66 + var cab = new Cabinet(cabinetPath);
67 + cab.Compress(files, this.CompressionLevel ?? Data.CompressionLevel.Mszip);
68
69 // Now that the container is created, set the outputs of the command.
60 - FileInfo fileInfo = new FileInfo(this.OutputPath);
70 + var fileInfo = new FileInfo(cabinetPath);
71
62 - this.Hash = Common.GetFileHash(fileInfo.FullName);
72 + this.Hash = BundleHashAlgorithm.Hash(fileInfo);
73
74 this.Size = fileInfo.Length;
75 }
66 -#endif
76 }
77 }
src/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs new
+134
@@ -0,0 +1,134 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core.Burn.Bundles
4 +{
5 + using System.Collections.Generic;
6 + using System.Diagnostics;
7 + using System.IO;
8 + using System.Linq;
9 + using WixToolset.Data;
10 + using WixToolset.Data.Burn;
11 + using WixToolset.Data.Tuples;
12 + using WixToolset.Extensibility.Data;
13 + using WixToolset.Extensibility.Services;
14 +
15 + internal class CreateNonUXContainers
16 + {
17 + public CreateNonUXContainers(IBackendHelper backendHelper, IntermediateSection section, WixBootstrapperApplicationTuple bootstrapperApplicationTuple, Dictionary<string, WixBundlePayloadTuple> payloadTuples, string intermediateFolder, string layoutFolder, CompressionLevel? defaultCompressionLevel)
18 + {
19 + this.BackendHelper = backendHelper;
20 + this.Section = section;
21 + this.BootstrapperApplicationTuple = bootstrapperApplicationTuple;
22 + this.PayloadTuples = payloadTuples;
23 + this.IntermediateFolder = intermediateFolder;
24 + this.LayoutFolder = layoutFolder;
25 + this.DefaultCompressionLevel = defaultCompressionLevel;
26 + }
27 +
28 + public IEnumerable<IFileTransfer> FileTransfers { get; private set; }
29 +
30 + public WixBundleContainerTuple UXContainer { get; set; }
31 +
32 + public IEnumerable<WixBundlePayloadTuple> UXContainerPayloads { get; private set; }
33 +
34 + public IEnumerable<WixBundleContainerTuple> Containers { get; private set; }
35 +
36 + private IBackendHelper BackendHelper { get; }
37 +
38 + private IntermediateSection Section { get; }
39 +
40 + private WixBootstrapperApplicationTuple BootstrapperApplicationTuple { get; }
41 +
42 + private Dictionary<string, WixBundlePayloadTuple> PayloadTuples { get; }
43 +
44 + private string IntermediateFolder { get; }
45 +
46 + private string LayoutFolder { get; }
47 +
48 + private CompressionLevel? DefaultCompressionLevel { get; }
49 +
50 + public void Execute()
51 + {
52 + var fileTransfers = new List<IFileTransfer>();
53 +
54 + var uxPayloadTuples = new List<WixBundlePayloadTuple>();
55 +
56 + var attachedContainerIndex = 1; // count starts at one because UX container is "0".
57 +
58 + var containerTuples = this.Section.Tuples.OfType<WixBundleContainerTuple>().ToList();
59 +
60 + var payloadsByContainer = this.PayloadTuples.Values.ToLookup(p => p.ContainerRef);
61 +
62 + foreach (var container in containerTuples)
63 + {
64 + var containerId = container.Id.Id;
65 +
66 + var containerPayloads = payloadsByContainer[containerId];
67 +
68 + if (!containerPayloads.Any())
69 + {
70 + if (containerId != BurnConstants.BurnDefaultAttachedContainerName)
71 + {
72 + // TODO: display warning that we're ignoring container that ended up with no paylods in it.
73 + }
74 + }
75 + else if (BurnConstants.BurnUXContainerName == containerId)
76 + {
77 + this.UXContainer = container;
78 +
79 + container.WorkingPath = Path.Combine(this.IntermediateFolder, container.Name);
80 + container.AttachedContainerIndex = 0;
81 +
82 + // Gather the list of UX payloads but ensure the BootstrapperApplication Payload is the first
83 + // in the list since that is the Payload that Burn attempts to load.
84 + var baPayloadId = this.BootstrapperApplicationTuple.Id.Id;
85 +
86 + foreach (var uxPayload in containerPayloads)
87 + {
88 + if (uxPayload.Id.Id == baPayloadId)
89 + {
90 + uxPayloadTuples.Insert(0, uxPayload);
91 + }
92 + else
93 + {
94 + uxPayloadTuples.Add(uxPayload);
95 + }
96 + }
97 + }
98 + else
99 + {
100 + container.WorkingPath = Path.Combine(this.IntermediateFolder, container.Name);
101 +
102 + // Add detached containers to the list of file transfers.
103 + if (ContainerType.Detached == container.Type)
104 + {
105 + var transfer = this.BackendHelper.CreateFileTransfer(container.WorkingPath, Path.Combine(this.LayoutFolder, container.Name), true, container.SourceLineNumbers);
106 + fileTransfers.Add(transfer);
107 + }
108 + else // update the attached container index.
109 + {
110 + Debug.Assert(ContainerType.Attached == container.Type);
111 +
112 + container.AttachedContainerIndex = attachedContainerIndex;
113 + ++attachedContainerIndex;
114 + }
115 +
116 + this.CreateContainer(container, containerPayloads, null);
117 + }
118 + }
119 +
120 + this.Containers = containerTuples;
121 + this.UXContainerPayloads = uxPayloadTuples;
122 + this.FileTransfers = fileTransfers;
123 + }
124 +
125 + private void CreateContainer(WixBundleContainerTuple container, IEnumerable<WixBundlePayloadTuple> containerPayloads, string manifestFile)
126 + {
127 + var command = new CreateContainerCommand(containerPayloads, container.WorkingPath, this.DefaultCompressionLevel);
128 + command.Execute();
129 +
130 + container.Hash = command.Hash;
131 + container.Size = command.Size;
132 + }
133 + }
134 +}
src/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs
+43 -35
@@ -3,61 +3,69 @@
3 namespace WixToolset.Core.Burn.Bundles
4 {
5 using System.Collections.Generic;
6 + using System.Linq;
7 using WixToolset.Data;
8 + using WixToolset.Data.Tuples;
9
10 internal class GetPackageFacadesCommand
11 {
10 -#if TODO
11 - public Table PackageTable { private get; set; }
12 -
13 - public Table ExePackageTable { private get; set; }
14 -
15 - public Table MsiPackageTable { private get; set; }
12 + public GetPackageFacadesCommand(IEnumerable<WixBundlePackageTuple> chainPackageTuples, IntermediateSection section)
13 + {
14 + this.ChainPackageTuples = chainPackageTuples;
15 + this.Section = section;
16 + }
17
17 - public Table MspPackageTable { private get; set; }
18 + private IEnumerable<WixBundlePackageTuple> ChainPackageTuples { get; }
19
19 - public Table MsuPackageTable { private get; set; }
20 + private IntermediateSection Section { get; }
21
22 public IDictionary<string, PackageFacade> PackageFacades { get; private set; }
23
24 public void Execute()
25 {
25 - RowDictionary<WixBundleExePackageRow> exePackages = new RowDictionary<WixBundleExePackageRow>(this.ExePackageTable);
26 - RowDictionary<WixBundleMsiPackageRow> msiPackages = new RowDictionary<WixBundleMsiPackageRow>(this.MsiPackageTable);
27 - RowDictionary<WixBundleMspPackageRow> mspPackages = new RowDictionary<WixBundleMspPackageRow>(this.MspPackageTable);
28 - RowDictionary<WixBundleMsuPackageRow> msuPackages = new RowDictionary<WixBundleMsuPackageRow>(this.MsuPackageTable);
26 + var exePackages = this.Section.Tuples.OfType<WixBundleExePackageTuple>().ToDictionary(t => t.Id.Id);
27 + var msiPackages = this.Section.Tuples.OfType<WixBundleMsiPackageTuple>().ToDictionary(t => t.Id.Id);
28 + var mspPackages = this.Section.Tuples.OfType<WixBundleMspPackageTuple>().ToDictionary(t => t.Id.Id);
29 + var msuPackages = this.Section.Tuples.OfType<WixBundleMsuPackageTuple>().ToDictionary(t => t.Id.Id);
30
30 - Dictionary<string, PackageFacade> facades = new Dictionary<string, PackageFacade>(this.PackageTable.Rows.Count);
31 + var facades = new Dictionary<string, PackageFacade>();
32
32 - foreach (WixBundlePackageRow package in this.PackageTable.Rows)
33 + foreach (var package in this.ChainPackageTuples)
34 {
34 - string id = package.WixChainItemId;
35 - PackageFacade facade = null;
36 -
35 + var id = package.Id.Id;
36 switch (package.Type)
37 {
39 - case WixBundlePackageType.Exe:
40 - facade = new PackageFacade(package, exePackages.Get(id));
41 - break;
42 -
43 - case WixBundlePackageType.Msi:
44 - facade = new PackageFacade(package, msiPackages.Get(id));
45 - break;
46 -
47 - case WixBundlePackageType.Msp:
48 - facade = new PackageFacade(package, mspPackages.Get(id));
49 - break;
50 -
51 - case WixBundlePackageType.Msu:
52 - facade = new PackageFacade(package, msuPackages.Get(id));
53 - break;
38 + case WixBundlePackageType.Exe:
39 + if (exePackages.TryGetValue(id, out var exePackage))
40 + {
41 + facades.Add(id, new PackageFacade(package, exePackage));
42 + }
43 + break;
44 +
45 + case WixBundlePackageType.Msi:
46 + if (msiPackages.TryGetValue(id, out var msiPackage))
47 + {
48 + facades.Add(id, new PackageFacade(package, msiPackage));
49 + }
50 + break;
51 +
52 + case WixBundlePackageType.Msp:
53 + if (mspPackages.TryGetValue(id, out var mspPackage))
54 + {
55 + facades.Add(id, new PackageFacade(package, mspPackage));
56 + }
57 + break;
58 +
59 + case WixBundlePackageType.Msu:
60 + if (msuPackages.TryGetValue(id, out var msuPackage))
61 + {
62 + facades.Add(id, new PackageFacade(package, msuPackage));
63 + }
64 + break;
65 }
55 -
56 - facades.Add(id, facade);
66 }
67
68 this.PackageFacades = facades;
69 }
61 -#endif
70 }
71 }
src/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs
+38 -28
@@ -5,24 +5,35 @@ namespace WixToolset.Core.Burn.Bundles
5 using System;
6 using System.Collections.Generic;
7 using WixToolset.Data;
8 + using WixToolset.Data.Tuples;
9 + using WixToolset.Extensibility.Services;
10
11 internal class OrderPackagesAndRollbackBoundariesCommand
12 {
11 -#if TODO
12 - public Table WixGroupTable { private get; set; }
13 + public OrderPackagesAndRollbackBoundariesCommand(IMessaging messaging, IEnumerable<WixGroupTuple> groupTuples, Dictionary<string, WixBundleRollbackBoundaryTuple> boundaryTuples, IDictionary<string, PackageFacade> packageFacades)
14 + {
15 + this.Messaging = messaging;
16 + this.GroupTuples = groupTuples;
17 + this.Boundaries = boundaryTuples;
18 + this.PackageFacades = packageFacades;
19 + }
20 +
21 + private IMessaging Messaging { get; }
22 +
23 + public IEnumerable<WixGroupTuple> GroupTuples { get; }
24
14 - public RowDictionary<WixBundleRollbackBoundaryRow> Boundaries { private get; set; }
25 + public Dictionary<string, WixBundleRollbackBoundaryTuple> Boundaries { get; }
26
16 - public IDictionary<string, PackageFacade> PackageFacades { private get; set; }
27 + public IDictionary<string, PackageFacade> PackageFacades { get; }
28
29 public IEnumerable<PackageFacade> OrderedPackageFacades { get; private set; }
30
20 - public IEnumerable<WixBundleRollbackBoundaryRow> UsedRollbackBoundaries { get; private set; }
31 + public IEnumerable<WixBundleRollbackBoundaryTuple> UsedRollbackBoundaries { get; private set; }
32
33 public void Execute()
34 {
24 - List<PackageFacade> orderedFacades = new List<PackageFacade>();
25 - List<WixBundleRollbackBoundaryRow> usedBoundaries = new List<WixBundleRollbackBoundaryRow>();
35 + var orderedFacades = new List<PackageFacade>();
36 + var usedBoundaries = new List<WixBundleRollbackBoundaryTuple>();
37
38 // Process the chain of packages to add them in the correct order
39 // and assign the forward rollback boundaries as appropriate. Remember
@@ -33,44 +44,44 @@ namespace WixToolset.Core.Burn.Bundles
44 // We handle uninstall (aka: backwards) rollback boundaries after
45 // we get these install/repair (aka: forward) rollback boundaries
46 // defined.
36 - WixBundleRollbackBoundaryRow previousRollbackBoundary = null;
37 - WixBundleRollbackBoundaryRow lastRollbackBoundary = null;
38 - bool boundaryHadX86Package = false;
47 + WixBundleRollbackBoundaryTuple previousRollbackBoundary = null;
48 + WixBundleRollbackBoundaryTuple lastRollbackBoundary = null;
49 + var boundaryHadX86Package = false;
50
40 - foreach (WixGroupRow row in this.WixGroupTable.Rows)
51 + foreach (var groupTuple in this.GroupTuples)
52 {
42 - if (ComplexReferenceChildType.Package == row.ChildType && ComplexReferenceParentType.PackageGroup == row.ParentType && "WixChain" == row.ParentId)
53 + if (ComplexReferenceChildType.Package == groupTuple.ChildType && ComplexReferenceParentType.PackageGroup == groupTuple.ParentType && "WixChain" == groupTuple.ParentId)
54 {
44 - PackageFacade facade = null;
45 - if (PackageFacades.TryGetValue(row.ChildId, out facade))
55 + if (this.PackageFacades.TryGetValue(groupTuple.ChildId, out var facade))
56 {
57 if (null != previousRollbackBoundary)
58 {
59 usedBoundaries.Add(previousRollbackBoundary);
50 - facade.Package.RollbackBoundary = previousRollbackBoundary.ChainPackageId;
60 + facade.PackageTuple.RollbackBoundaryRef = previousRollbackBoundary.Id.Id;
61 previousRollbackBoundary = null;
62
53 - boundaryHadX86Package = (facade.Package.x64 == YesNoType.Yes);
63 + boundaryHadX86Package = facade.PackageTuple.Win64;
64 }
65
66 // Error if MSI transaction has x86 package preceding x64 packages
57 - if ((lastRollbackBoundary != null) && (lastRollbackBoundary.Transaction == YesNoType.Yes)
67 + if ((lastRollbackBoundary != null)
68 + && lastRollbackBoundary.Transaction == true
69 && boundaryHadX86Package
59 - && (facade.Package.x64 == YesNoType.Yes))
70 + && facade.PackageTuple.Win64)
71 {
61 - Messaging.Instance.OnMessage(WixErrors.MsiTransactionX86BeforeX64(lastRollbackBoundary.SourceLineNumbers));
72 + this.Messaging.Write(ErrorMessages.MsiTransactionX86BeforeX64(lastRollbackBoundary.SourceLineNumbers));
73 }
63 - boundaryHadX86Package = boundaryHadX86Package || (facade.Package.x64 == YesNoType.No);
74 + boundaryHadX86Package |= facade.PackageTuple.Win64;
75
76 orderedFacades.Add(facade);
77 }
78 else // must be a rollback boundary.
79 {
80 // Discard the next rollback boundary if we have a previously defined boundary.
70 - WixBundleRollbackBoundaryRow nextRollbackBoundary = Boundaries.Get(row.ChildId);
81 + var nextRollbackBoundary = this.Boundaries[groupTuple.ChildId];
82 if (null != previousRollbackBoundary)
83 {
73 - Messaging.Instance.OnMessage(WixWarnings.DiscardedRollbackBoundary(nextRollbackBoundary.SourceLineNumbers, nextRollbackBoundary.ChainPackageId));
84 + this.Messaging.Write(WarningMessages.DiscardedRollbackBoundary(nextRollbackBoundary.SourceLineNumbers, nextRollbackBoundary.Id.Id));
85 }
86 else
87 {
@@ -83,7 +94,7 @@ namespace WixToolset.Core.Burn.Bundles
94
95 if (null != previousRollbackBoundary)
96 {
86 - Messaging.Instance.OnMessage(WixWarnings.DiscardedRollbackBoundary(previousRollbackBoundary.SourceLineNumbers, previousRollbackBoundary.ChainPackageId));
97 + this.Messaging.Write(WarningMessages.DiscardedRollbackBoundary(previousRollbackBoundary.SourceLineNumbers, previousRollbackBoundary.Id.Id));
98 }
99
100 // With the forward rollback boundaries assigned, we can now go
@@ -120,14 +131,14 @@ namespace WixToolset.Core.Burn.Bundles
131
132 foreach (PackageFacade package in orderedFacades)
133 {
123 - if (null != package.Package.RollbackBoundary)
134 + if (null != package.PackageTuple.RollbackBoundaryRef)
135 {
136 if (null != previousFacade)
137 {
127 - previousFacade.Package.RollbackBoundaryBackward = previousRollbackBoundaryId;
138 + previousFacade.PackageTuple.RollbackBoundaryBackwardRef = previousRollbackBoundaryId;
139 }
140
130 - previousRollbackBoundaryId = package.Package.RollbackBoundary;
141 + previousRollbackBoundaryId = package.PackageTuple.RollbackBoundaryRef;
142 }
143
144 previousFacade = package;
@@ -135,12 +146,11 @@ namespace WixToolset.Core.Burn.Bundles
146
147 if (!String.IsNullOrEmpty(previousRollbackBoundaryId) && null != previousFacade)
148 {
138 - previousFacade.Package.RollbackBoundaryBackward = previousRollbackBoundaryId;
149 + previousFacade.PackageTuple.RollbackBoundaryBackwardRef = previousRollbackBoundaryId;
150 }
151
152 this.OrderedPackageFacades = orderedFacades;
153 this.UsedRollbackBoundaries = usedBoundaries;
154 }
144 -#endif
155 }
156 }
src/WixToolset.Core.Burn/Bundles/PackageFacade.cs
+11 -44
@@ -2,57 +2,24 @@
2
3 namespace WixToolset.Core.Burn.Bundles
4 {
5 + using System.Diagnostics;
6 + using WixToolset.Data;
7 + using WixToolset.Data.Tuples;
8 +
9 internal class PackageFacade
10 {
7 -#if TODO
8 - private PackageFacade(WixBundlePackageRow package)
9 - {
10 - this.Package = package;
11 - this.Provides = new ProvidesDependencyCollection();
12 - }
13 -
14 - public PackageFacade(WixBundlePackageRow package, WixBundleExePackageRow exePackage)
15 - : this(package)
11 + public PackageFacade(WixBundlePackageTuple packageTuple, IntermediateTuple specificPackageTuple)
12 {
17 - this.ExePackage = exePackage;
18 - }
13 + Debug.Assert(packageTuple.Id.Id == specificPackageTuple.Id.Id);
14
20 - public PackageFacade(WixBundlePackageRow package, WixBundleMsiPackageRow msiPackage)
21 - : this(package)
22 - {
23 - this.MsiPackage = msiPackage;
15 + this.PackageTuple = packageTuple;
16 + this.SpecificPackageTuple = specificPackageTuple;
17 }
18
26 - public PackageFacade(WixBundlePackageRow package, WixBundleMspPackageRow mspPackage)
27 - : this(package)
28 - {
29 - this.MspPackage = mspPackage;
30 - }
31 -
32 - public PackageFacade(WixBundlePackageRow package, WixBundleMsuPackageRow msuPackage)
33 - : this(package)
34 - {
35 - this.MsuPackage = msuPackage;
36 - }
37 -
38 - public WixBundlePackageRow Package { get; private set; }
39 -
40 - public WixBundleExePackageRow ExePackage { get; private set; }
41 -
42 - public WixBundleMsiPackageRow MsiPackage { get; private set; }
43 -
44 - public WixBundleMspPackageRow MspPackage { get; private set; }
19 + public string PackageId => this.PackageTuple.Id.Id;
20
46 - public WixBundleMsuPackageRow MsuPackage { get; private set; }
21 + public WixBundlePackageTuple PackageTuple { get; }
22
48 - /// <summary>
49 - /// The provides dependencies authored and imported for this package.
50 - /// </summary>
51 - /// <remarks>
52 - /// TODO: Eventually this collection should turn into Rows so they are tracked in the PDB but
53 - /// the relationship with the extension makes it much trickier to pull off.
54 - /// </remarks>
55 - public ProvidesDependencyCollection Provides { get; private set; }
56 -#endif
23 + public IntermediateTuple SpecificPackageTuple { get; }
24 }
25 }
src/WixToolset.Core.Burn/Bundles/ProcessExePackageCommand.cs
+14 -9
@@ -3,32 +3,37 @@
3 namespace WixToolset.Core.Burn.Bundles
4 {
5 using System;
6 - using WixToolset.Data;
6 + using System.Collections.Generic;
7 + using WixToolset.Data.Tuples;
8
9 /// <summary>
10 /// Initializes package state from the Exe contents.
11 /// </summary>
12 internal class ProcessExePackageCommand
13 {
13 -#if TODO
14 - public RowDictionary<WixBundlePayloadRow> AuthoredPayloads { private get; set; }
14 + public ProcessExePackageCommand(PackageFacade facade, Dictionary<string, WixBundlePayloadTuple> payloadTuples)
15 + {
16 + this.AuthoredPayloads = payloadTuples;
17 + this.Facade = facade;
18 + }
19 +
20 + public Dictionary<string, WixBundlePayloadTuple> AuthoredPayloads { get; }
21
16 - public PackageFacade Facade { private get; set; }
22 + public PackageFacade Facade { get; }
23
24 /// <summary>
25 /// Processes the Exe packages to add properties and payloads from the Exe packages.
26 /// </summary>
27 public void Execute()
28 {
23 - WixBundlePayloadRow packagePayload = this.AuthoredPayloads.Get(this.Facade.Package.PackagePayload);
29 + var packagePayload = this.AuthoredPayloads[this.Facade.PackageTuple.PayloadRef];
30
25 - if (String.IsNullOrEmpty(this.Facade.Package.CacheId))
31 + if (String.IsNullOrEmpty(this.Facade.PackageTuple.CacheId))
32 {
27 - this.Facade.Package.CacheId = packagePayload.Hash;
33 + this.Facade.PackageTuple.CacheId = packagePayload.Hash;
34 }
35
30 - this.Facade.Package.Version = packagePayload.Version;
36 + this.Facade.PackageTuple.Version = packagePayload.Version;
37 }
32 -#endif
38 }
39 }
src/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs
+238 -187
@@ -3,7 +3,6 @@
3 namespace WixToolset.Core.Burn.Bundles
4 {
5 using System;
6 - using System.Collections;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.Globalization;
@@ -11,47 +10,62 @@ namespace WixToolset.Core.Burn.Bundles
10 using System.Linq;
11 using WixToolset.Data;
12 using WixToolset.Extensibility;
14 - using WixToolset.Core.Native;
13 using Dtf = WixToolset.Dtf.WindowsInstaller;
16 - using WixToolset.Data.Bind;
14 + using WixToolset.Extensibility.Services;
15 + using WixToolset.Data.Tuples;
16 + using WixToolset.Data.WindowsInstaller;
17 + using WixToolset.Extensibility.Data;
18
19 /// <summary>
20 /// Initializes package state from the MSI contents.
21 /// </summary>
22 internal class ProcessMsiPackageCommand
23 {
23 -#if TODO
24 private const string PropertySqlFormat = "SELECT `Value` FROM `Property` WHERE `Property` = '{0}'";
25
26 - public RowDictionary<WixBundlePayloadRow> AuthoredPayloads { private get; set; }
26 + public ProcessMsiPackageCommand(IServiceProvider serviceProvider, IEnumerable<IBurnBackendExtension> backendExtensions, IntermediateSection section, PackageFacade facade, Dictionary<string, WixBundlePayloadTuple> payloadTuples)
27 + {
28 + this.Messaging = serviceProvider.GetService<IMessaging>();
29 + this.BackendHelper = serviceProvider.GetService<IBackendHelper>();
30 + this.PathResolver = serviceProvider.GetService<IPathResolver>();
31 +
32 + this.BackendExtensions = backendExtensions;
33 +
34 + this.AuthoredPayloads = payloadTuples;
35 + this.Section = section;
36 + this.Facade = facade;
37 + }
38 +
39 + private IMessaging Messaging { get; }
40
28 - public PackageFacade Facade { private get; set; }
41 + private IBackendHelper BackendHelper { get; }
42
30 - public IEnumerable<IBurnBackendExtension> BackendExtensions { private get; set; }
43 + private IPathResolver PathResolver { get; }
44
32 - public Table MsiFeatureTable { private get; set; }
45 + private IEnumerable<IBurnBackendExtension> BackendExtensions { get; }
46
34 - public Table MsiPropertyTable { private get; set; }
47 + private Dictionary<string, WixBundlePayloadTuple> AuthoredPayloads { get; }
48
36 - public Table PayloadTable { private get; set; }
49 + private PackageFacade Facade { get; }
50
38 - public Table RelatedPackageTable { private get; set; }
51 + private IntermediateSection Section { get; }
52
53 /// <summary>
54 /// Processes the MSI packages to add properties and payloads from the MSI packages.
55 /// </summary>
56 public void Execute()
57 {
45 - WixBundlePayloadRow packagePayload = this.AuthoredPayloads.Get(this.Facade.Package.PackagePayload);
58 + var packagePayload = this.AuthoredPayloads[this.Facade.PackageTuple.PayloadRef];
59
47 - string sourcePath = packagePayload.FullFileName;
48 - bool longNamesInImage = false;
49 - bool compressed = false;
50 - bool x64 = false;
60 + var msiPackage = (WixBundleMsiPackageTuple)this.Facade.SpecificPackageTuple;
61 +
62 + var sourcePath = packagePayload.SourceFile.Path;
63 + var longNamesInImage = false;
64 + var compressed = false;
65 try
66 {
67 // Read data out of the msi database...
54 - using (Dtf.SummaryInfo sumInfo = new Dtf.SummaryInfo(sourcePath, false))
68 + using (var sumInfo = new Dtf.SummaryInfo(sourcePath, false))
69 {
70 // 1 is the Word Count summary information stream bit that means
71 // the MSI uses short file names when set. We care about long file
@@ -62,83 +76,84 @@ namespace WixToolset.Core.Burn.Bundles
76 // files are compressed in the MSI by default when the bit is set.
77 compressed = 2 == (sumInfo.WordCount & 2);
78
65 - x64 = (sumInfo.Template.Contains("x64") || sumInfo.Template.Contains("Intel64"));
66 -
79 // 8 is the Word Count summary information stream bit that means
80 // "Elevated privileges are not required to install this package."
81 // in MSI 4.5 and below, if this bit is 0, elevation is required.
70 - this.Facade.Package.PerMachine = (0 == (sumInfo.WordCount & 8)) ? YesNoDefaultType.Yes : YesNoDefaultType.No;
71 - this.Facade.Package.x64 = x64 ? YesNoType.Yes : YesNoType.No;
82 + var perMachine = (0 == (sumInfo.WordCount & 8));
83 + var x64 = (sumInfo.Template.Contains("x64") || sumInfo.Template.Contains("Intel64"));
84 +
85 + this.Facade.PackageTuple.PerMachine = perMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No;
86 + this.Facade.PackageTuple.Win64 = x64;
87 }
88
74 - using (Dtf.Database db = new Dtf.Database(sourcePath))
89 + using (var db = new Dtf.Database(sourcePath))
90 {
76 - this.Facade.MsiPackage.ProductCode = ProcessMsiPackageCommand.GetProperty(db, "ProductCode");
77 - this.Facade.MsiPackage.UpgradeCode = ProcessMsiPackageCommand.GetProperty(db, "UpgradeCode");
78 - this.Facade.MsiPackage.Manufacturer = ProcessMsiPackageCommand.GetProperty(db, "Manufacturer");
79 - this.Facade.MsiPackage.ProductLanguage = Convert.ToInt32(ProcessMsiPackageCommand.GetProperty(db, "ProductLanguage"), CultureInfo.InvariantCulture);
80 - this.Facade.MsiPackage.ProductVersion = ProcessMsiPackageCommand.GetProperty(db, "ProductVersion");
91 + msiPackage.ProductCode = ProcessMsiPackageCommand.GetProperty(db, "ProductCode");
92 + msiPackage.UpgradeCode = ProcessMsiPackageCommand.GetProperty(db, "UpgradeCode");
93 + msiPackage.Manufacturer = ProcessMsiPackageCommand.GetProperty(db, "Manufacturer");
94 + msiPackage.ProductLanguage = Convert.ToInt32(ProcessMsiPackageCommand.GetProperty(db, "ProductLanguage"), CultureInfo.InvariantCulture);
95 + msiPackage.ProductVersion = ProcessMsiPackageCommand.GetProperty(db, "ProductVersion");
96
82 - if (!Common.IsValidModuleOrBundleVersion(this.Facade.MsiPackage.ProductVersion))
97 + if (!Common.IsValidModuleOrBundleVersion(msiPackage.ProductVersion))
98 {
99 // not a proper .NET version (e.g., five fields); can we get a valid four-part version number?
100 string version = null;
86 - string[] versionParts = this.Facade.MsiPackage.ProductVersion.Split('.');
87 - int count = versionParts.Length;
101 + string[] versionParts = msiPackage.ProductVersion.Split('.');
102 + var count = versionParts.Length;
103 if (0 < count)
104 {
105 version = versionParts[0];
91 - for (int i = 1; i < 4 && i < count; ++i)
106 + for (var i = 1; i < 4 && i < count; ++i)
107 {
108 version = String.Concat(version, ".", versionParts[i]);
109 }
110 }
96 -
111 +
112 if (!String.IsNullOrEmpty(version) && Common.IsValidModuleOrBundleVersion(version))
113 {
99 - Messaging.Instance.OnMessage(WixWarnings.VersionTruncated(this.Facade.Package.SourceLineNumbers, this.Facade.MsiPackage.ProductVersion, sourcePath, version));
100 - this.Facade.MsiPackage.ProductVersion = version;
114 + this.Messaging.Write(WarningMessages.VersionTruncated(this.Facade.PackageTuple.SourceLineNumbers, msiPackage.ProductVersion, sourcePath, version));
115 + msiPackage.ProductVersion = version;
116 }
117 else
118 {
104 - Messaging.Instance.OnMessage(WixErrors.InvalidProductVersion(this.Facade.Package.SourceLineNumbers, this.Facade.MsiPackage.ProductVersion, sourcePath));
119 + this.Messaging.Write(ErrorMessages.InvalidProductVersion(this.Facade.PackageTuple.SourceLineNumbers, msiPackage.ProductVersion, sourcePath));
120 }
121 }
122
108 - if (String.IsNullOrEmpty(this.Facade.Package.CacheId))
123 + if (String.IsNullOrEmpty(this.Facade.PackageTuple.CacheId))
124 {
110 - this.Facade.Package.CacheId = String.Format("{0}v{1}", this.Facade.MsiPackage.ProductCode, this.Facade.MsiPackage.ProductVersion);
125 + this.Facade.PackageTuple.CacheId = String.Format("{0}v{1}", msiPackage.ProductCode, msiPackage.ProductVersion);
126 }
127
113 - if (String.IsNullOrEmpty(this.Facade.Package.DisplayName))
128 + if (String.IsNullOrEmpty(this.Facade.PackageTuple.DisplayName))
129 {
115 - this.Facade.Package.DisplayName = ProcessMsiPackageCommand.GetProperty(db, "ProductName");
130 + this.Facade.PackageTuple.DisplayName = ProcessMsiPackageCommand.GetProperty(db, "ProductName");
131 }
132
118 - if (String.IsNullOrEmpty(this.Facade.Package.Description))
133 + if (String.IsNullOrEmpty(this.Facade.PackageTuple.Description))
134 {
120 - this.Facade.Package.Description = ProcessMsiPackageCommand.GetProperty(db, "ARPCOMMENTS");
135 + this.Facade.PackageTuple.Description = ProcessMsiPackageCommand.GetProperty(db, "ARPCOMMENTS");
136 }
137
123 - ISet<string> payloadNames = this.GetPayloadTargetNames();
138 + var payloadNames = this.GetPayloadTargetNames(packagePayload.Id.Id);
139
125 - ISet<string> msiPropertyNames = this.GetMsiPropertyNames();
140 + var msiPropertyNames = this.GetMsiPropertyNames(packagePayload.Id.Id);
141
127 - this.SetPerMachineAppropriately(db, sourcePath);
142 + this.SetPerMachineAppropriately(db, msiPackage, sourcePath);
143
144 // Ensure the MSI package is appropriately marked visible or not.
130 - this.SetPackageVisibility(db, msiPropertyNames);
145 + this.SetPackageVisibility(db, msiPackage, msiPropertyNames);
146
147 // Unless the MSI or setup code overrides the default, set MSIFASTINSTALL for best performance.
148 if (!msiPropertyNames.Contains("MSIFASTINSTALL") && !ProcessMsiPackageCommand.HasProperty(db, "MSIFASTINSTALL"))
149 {
135 - this.AddMsiProperty("MSIFASTINSTALL", "7");
150 + this.AddMsiProperty(msiPackage, "MSIFASTINSTALL", "7");
151 }
152
153 this.CreateRelatedPackages(db);
154
155 // If feature selection is enabled, represent the Feature table in the manifest.
141 - if (this.Facade.MsiPackage.EnableFeatureSelection)
156 + if ((msiPackage.Attributes & WixBundleMsiPackageAttributes.EnableFeatureSelection) == WixBundleMsiPackageAttributes.EnableFeatureSelection)
157 {
158 this.CreateMsiFeatures(db);
159 }
@@ -148,90 +163,92 @@ namespace WixToolset.Core.Burn.Bundles
163
164 // Add all external files as package payloads and calculate the total install size as the rollup of
165 // File table's sizes.
151 - this.Facade.Package.InstallSize = this.ImportExternalFileAsPayloadsAndReturnInstallSize(db, packagePayload, longNamesInImage, compressed, payloadNames);
166 + this.Facade.PackageTuple.InstallSize = this.ImportExternalFileAsPayloadsAndReturnInstallSize(db, packagePayload, longNamesInImage, compressed, payloadNames);
167
168 // Add all dependency providers from the MSI.
154 - this.ImportDependencyProviders(db);
169 + this.ImportDependencyProviders(msiPackage, db);
170 }
171 }
172 catch (Dtf.InstallerException e)
173 {
159 - Messaging.Instance.OnMessage(WixErrors.UnableToReadPackageInformation(this.Facade.Package.SourceLineNumbers, sourcePath, e.Message));
174 + this.Messaging.Write(ErrorMessages.UnableToReadPackageInformation(this.Facade.PackageTuple.SourceLineNumbers, sourcePath, e.Message));
175 }
176 }
177
163 - private ISet<string> GetPayloadTargetNames()
178 + private ISet<string> GetPayloadTargetNames(string packageId)
179 {
165 - IEnumerable<string> payloadNames = this.PayloadTable.RowsAs<WixBundlePayloadRow>()
166 - .Where(r => r.Package == this.Facade.Package.WixChainItemId)
167 - .Select(r => r.Name);
180 + var payloadNames = this.Section.Tuples.OfType<WixBundlePayloadTuple>()
181 + .Where(p => p.PackageRef == packageId)
182 + .Select(p => p.Name);
183
184 return new HashSet<string>(payloadNames, StringComparer.OrdinalIgnoreCase);
185 }
186
172 - private ISet<string> GetMsiPropertyNames()
187 + private ISet<string> GetMsiPropertyNames(string packageId)
188 {
174 - IEnumerable<string> properties = this.MsiPropertyTable.RowsAs<WixBundleMsiPropertyRow>()
175 - .Where(r => r.ChainPackageId == this.Facade.Package.WixChainItemId)
176 - .Select(r => r.Name);
189 + var properties = this.Section.Tuples.OfType<WixBundleMsiPropertyTuple>()
190 + .Where(p => p.Id.Id == packageId)
191 + .Select(p => p.Name);
192
193 return new HashSet<string>(properties, StringComparer.Ordinal);
194 }
195
181 - private void SetPerMachineAppropriately(Dtf.Database db, string sourcePath)
196 + private void SetPerMachineAppropriately(Dtf.Database db, WixBundleMsiPackageTuple msiPackage, string sourcePath)
197 {
183 - if (this.Facade.MsiPackage.ForcePerMachine)
198 + if (msiPackage.ForcePerMachine)
199 {
185 - if (YesNoDefaultType.No == this.Facade.Package.PerMachine)
200 + if (YesNoDefaultType.No == this.Facade.PackageTuple.PerMachine)
201 {
187 - Messaging.Instance.OnMessage(WixWarnings.PerUserButForcingPerMachine(this.Facade.Package.SourceLineNumbers, sourcePath));
188 - this.Facade.Package.PerMachine = YesNoDefaultType.Yes; // ensure that we think the package is per-machine.
202 + this.Messaging.Write(WarningMessages.PerUserButForcingPerMachine(this.Facade.PackageTuple.SourceLineNumbers, sourcePath));
203 + this.Facade.PackageTuple.PerMachine = YesNoDefaultType.Yes; // ensure that we think the package is per-machine.
204 }
205
206 // Force ALLUSERS=1 via the MSI command-line.
192 - this.AddMsiProperty("ALLUSERS", "1");
207 + this.AddMsiProperty(msiPackage, "ALLUSERS", "1");
208 }
209 else
210 {
196 - string allusers = ProcessMsiPackageCommand.GetProperty(db, "ALLUSERS");
211 + var allusers = ProcessMsiPackageCommand.GetProperty(db, "ALLUSERS");
212
213 if (String.IsNullOrEmpty(allusers))
214 {
215 // Not forced per-machine and no ALLUSERS property, flip back to per-user.
201 - if (YesNoDefaultType.Yes == this.Facade.Package.PerMachine)
216 + if (YesNoDefaultType.Yes == this.Facade.PackageTuple.PerMachine)
217 {
203 - Messaging.Instance.OnMessage(WixWarnings.ImplicitlyPerUser(this.Facade.Package.SourceLineNumbers, sourcePath));
204 - this.Facade.Package.PerMachine = YesNoDefaultType.No;
218 + this.Messaging.Write(WarningMessages.ImplicitlyPerUser(this.Facade.PackageTuple.SourceLineNumbers, sourcePath));
219 + this.Facade.PackageTuple.PerMachine = YesNoDefaultType.No;
220 }
221 }
222 else if (allusers.Equals("1", StringComparison.Ordinal))
223 {
209 - if (YesNoDefaultType.No == this.Facade.Package.PerMachine)
224 + if (YesNoDefaultType.No == this.Facade.PackageTuple.PerMachine)
225 {
211 - Messaging.Instance.OnMessage(WixErrors.PerUserButAllUsersEquals1(this.Facade.Package.SourceLineNumbers, sourcePath));
226 + this.Messaging.Write(ErrorMessages.PerUserButAllUsersEquals1(this.Facade.PackageTuple.SourceLineNumbers, sourcePath));
227 }
228 }
229 else if (allusers.Equals("2", StringComparison.Ordinal))
230 {
216 - Messaging.Instance.OnMessage(WixWarnings.DiscouragedAllUsersValue(this.Facade.Package.SourceLineNumbers, sourcePath, (YesNoDefaultType.Yes == this.Facade.Package.PerMachine) ? "machine" : "user"));
231 + this.Messaging.Write(WarningMessages.DiscouragedAllUsersValue(this.Facade.PackageTuple.SourceLineNumbers, sourcePath, (YesNoDefaultType.Yes == this.Facade.PackageTuple.PerMachine) ? "machine" : "user"));
232 }
233 else
234 {
220 - Messaging.Instance.OnMessage(WixErrors.UnsupportedAllUsersValue(this.Facade.Package.SourceLineNumbers, sourcePath, allusers));
235 + this.Messaging.Write(ErrorMessages.UnsupportedAllUsersValue(this.Facade.PackageTuple.SourceLineNumbers, sourcePath, allusers));
236 }
237 }
238 }
239
225 - private void SetPackageVisibility(Dtf.Database db, ISet<string> msiPropertyNames)
240 + private void SetPackageVisibility(Dtf.Database db, WixBundleMsiPackageTuple msiPackage, ISet<string> msiPropertyNames)
241 {
227 - bool alreadyVisible = !ProcessMsiPackageCommand.HasProperty(db, "ARPSYSTEMCOMPONENT");
242 + var alreadyVisible = !ProcessMsiPackageCommand.HasProperty(db, "ARPSYSTEMCOMPONENT");
243 + var visible = (this.Facade.PackageTuple.Attributes & WixBundlePackageAttributes.Visible) == WixBundlePackageAttributes.Visible;
244
229 - if (alreadyVisible != this.Facade.Package.Visible) // if not already set to the correct visibility.
245 + // If not already set to the correct visibility.
246 + if (alreadyVisible != visible)
247 {
248 // If the authoring specifically added "ARPSYSTEMCOMPONENT", don't do it again.
249 if (!msiPropertyNames.Contains("ARPSYSTEMCOMPONENT"))
250 {
234 - this.AddMsiProperty("ARPSYSTEMCOMPONENT", this.Facade.Package.Visible ? String.Empty : "1");
251 + this.AddMsiProperty(msiPackage, "ARPSYSTEMCOMPONENT", visible ? String.Empty : "1");
252 }
253 }
254 }
@@ -241,30 +258,35 @@ namespace WixToolset.Core.Burn.Bundles
258 // Represent the Upgrade table as related packages.
259 if (db.Tables.Contains("Upgrade"))
260 {
244 - using (Dtf.View view = db.OpenView("SELECT `UpgradeCode`, `VersionMin`, `VersionMax`, `Language`, `Attributes` FROM `Upgrade`"))
261 + using (var view = db.OpenView("SELECT `UpgradeCode`, `VersionMin`, `VersionMax`, `Language`, `Attributes` FROM `Upgrade`"))
262 {
263 view.Execute();
264 while (true)
265 {
249 - using (Dtf.Record record = view.Fetch())
266 + using (var record = view.Fetch())
267 {
268 if (null == record)
269 {
270 break;
271 }
272
256 - WixBundleRelatedPackageRow related = (WixBundleRelatedPackageRow)this.RelatedPackageTable.CreateRow(this.Facade.Package.SourceLineNumbers);
257 - related.ChainPackageId = this.Facade.Package.WixChainItemId;
258 - related.Id = record.GetString(1);
259 - related.MinVersion = record.GetString(2);
260 - related.MaxVersion = record.GetString(3);
261 - related.Languages = record.GetString(4);
262 -
263 - int attributes = record.GetInteger(5);
264 - related.OnlyDetect = (attributes & MsiInterop.MsidbUpgradeAttributesOnlyDetect) == MsiInterop.MsidbUpgradeAttributesOnlyDetect;
265 - related.MinInclusive = (attributes & MsiInterop.MsidbUpgradeAttributesVersionMinInclusive) == MsiInterop.MsidbUpgradeAttributesVersionMinInclusive;
266 - related.MaxInclusive = (attributes & MsiInterop.MsidbUpgradeAttributesVersionMaxInclusive) == MsiInterop.MsidbUpgradeAttributesVersionMaxInclusive;
267 - related.LangInclusive = (attributes & MsiInterop.MsidbUpgradeAttributesLanguagesExclusive) == 0;
273 + var recordAttributes = record.GetInteger(5);
274 +
275 + var attributes = WixBundleRelatedPackageAttributes.None;
276 + attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect) == WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect ? WixBundleRelatedPackageAttributes.OnlyDetect : 0;
277 + attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive ? WixBundleRelatedPackageAttributes.MinInclusive : 0;
278 + attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive ? WixBundleRelatedPackageAttributes.MaxInclusive : 0;
279 + attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive ? WixBundleRelatedPackageAttributes.LangInclusive : 0;
280 +
281 + var related = new WixBundleRelatedPackageTuple(this.Facade.PackageTuple.SourceLineNumbers)
282 + {
283 + PackageRef = this.Facade.PackageId,
284 + RelatedId = record.GetString(1),
285 + MinVersion = record.GetString(2),
286 + MaxVersion = record.GetString(3),
287 + Languages = record.GetString(4),
288 + Attributes = attributes,
289 + };
290 }
291 }
292 }
@@ -275,26 +297,26 @@ namespace WixToolset.Core.Burn.Bundles
297 {
298 if (db.Tables.Contains("Feature"))
299 {
278 - using (Dtf.View featureView = db.OpenView("SELECT `Component_` FROM `FeatureComponents` WHERE `Feature_` = ?"))
279 - using (Dtf.View componentView = db.OpenView("SELECT `FileSize` FROM `File` WHERE `Component_` = ?"))
300 + using (var featureView = db.OpenView("SELECT `Component_` FROM `FeatureComponents` WHERE `Feature_` = ?"))
301 + using (var componentView = db.OpenView("SELECT `FileSize` FROM `File` WHERE `Component_` = ?"))
302 {
281 - using (Dtf.Record featureRecord = new Dtf.Record(1))
282 - using (Dtf.Record componentRecord = new Dtf.Record(1))
303 + using (var featureRecord = new Dtf.Record(1))
304 + using (var componentRecord = new Dtf.Record(1))
305 {
284 - using (Dtf.View allFeaturesView = db.OpenView("SELECT * FROM `Feature`"))
306 + using (var allFeaturesView = db.OpenView("SELECT * FROM `Feature`"))
307 {
308 allFeaturesView.Execute();
309
310 while (true)
311 {
290 - using (Dtf.Record allFeaturesResultRecord = allFeaturesView.Fetch())
312 + using (var allFeaturesResultRecord = allFeaturesView.Fetch())
313 {
314 if (null == allFeaturesResultRecord)
315 {
316 break;
317 }
318
297 - string featureName = allFeaturesResultRecord.GetString(1);
319 + var featureName = allFeaturesResultRecord.GetString(1);
320
321 // Calculate the Feature size.
322 featureRecord.SetString(1, featureName);
@@ -304,43 +326,46 @@ namespace WixToolset.Core.Burn.Bundles
326 long size = 0;
327 while (true)
328 {
307 - using (Dtf.Record componentResultRecord = featureView.Fetch())
329 + using (var componentResultRecord = featureView.Fetch())
330 {
331 if (null == componentResultRecord)
332 {
333 break;
334 }
313 - string component = componentResultRecord.GetString(1);
335 +
336 + var component = componentResultRecord.GetString(1);
337 componentRecord.SetString(1, component);
338 componentView.Execute(componentRecord);
339
340 while (true)
341 {
319 - using (Dtf.Record fileResultRecord = componentView.Fetch())
342 + using (var fileResultRecord = componentView.Fetch())
343 {
344 if (null == fileResultRecord)
345 {
346 break;
347 }
348
326 - string fileSize = fileResultRecord.GetString(1);
349 + var fileSize = fileResultRecord.GetString(1);
350 size += Convert.ToInt32(fileSize, CultureInfo.InvariantCulture.NumberFormat);
351 }
352 }
353 }
354 }
355
333 - WixBundleMsiFeatureRow feature = (WixBundleMsiFeatureRow)this.MsiFeatureTable.CreateRow(this.Facade.Package.SourceLineNumbers);
334 - feature.ChainPackageId = this.Facade.Package.WixChainItemId;
335 - feature.Name = featureName;
336 - feature.Parent = allFeaturesResultRecord.GetString(2);
337 - feature.Title = allFeaturesResultRecord.GetString(3);
338 - feature.Description = allFeaturesResultRecord.GetString(4);
339 - feature.Display = allFeaturesResultRecord.GetInteger(5);
340 - feature.Level = allFeaturesResultRecord.GetInteger(6);
341 - feature.Directory = allFeaturesResultRecord.GetString(7);
342 - feature.Attributes = allFeaturesResultRecord.GetInteger(8);
343 - feature.Size = size;
356 + var feature = new WixBundleMsiFeatureTuple(this.Facade.PackageTuple.SourceLineNumbers, new Identifier(AccessModifier.Private, this.Facade.PackageId, featureName))
357 + {
358 + PackageRef = this.Facade.PackageId,
359 + Name = featureName,
360 + Parent = allFeaturesResultRecord.GetString(2),
361 + Title = allFeaturesResultRecord.GetString(3),
362 + Description = allFeaturesResultRecord.GetString(4),
363 + Display = allFeaturesResultRecord.GetInteger(5),
364 + Level = allFeaturesResultRecord.GetInteger(6),
365 + Directory = allFeaturesResultRecord.GetString(7),
366 + Attributes = allFeaturesResultRecord.GetInteger(8),
367 + Size = size
368 + };
369 }
370 }
371 }
@@ -349,113 +374,119 @@ namespace WixToolset.Core.Burn.Bundles
374 }
375 }
376
352 - private void ImportExternalCabinetAsPayloads(Dtf.Database db, WixBundlePayloadRow packagePayload, ISet<string> payloadNames)
377 + private void ImportExternalCabinetAsPayloads(Dtf.Database db, WixBundlePayloadTuple packagePayload, ISet<string> payloadNames)
378 {
379 if (db.Tables.Contains("Media"))
380 {
356 - foreach (string cabinet in db.ExecuteStringQuery("SELECT `Cabinet` FROM `Media`"))
381 + foreach (var cabinet in db.ExecuteStringQuery("SELECT `Cabinet` FROM `Media`"))
382 {
383 if (!String.IsNullOrEmpty(cabinet) && !cabinet.StartsWith("#", StringComparison.Ordinal))
384 {
385 // If we didn't find the Payload as an existing child of the package, we need to
386 // add it. We expect the file to exist on-disk in the same relative location as
387 // the MSI expects to find it...
363 - string cabinetName = Path.Combine(Path.GetDirectoryName(packagePayload.Name), cabinet);
388 + var cabinetName = Path.Combine(Path.GetDirectoryName(packagePayload.Name), cabinet);
389
390 if (!payloadNames.Contains(cabinetName))
391 {
367 - string generatedId = Common.GenerateIdentifier("cab", packagePayload.Id, cabinet);
368 - string payloadSourceFile = this.ResolveRelatedFile(packagePayload.UnresolvedSourceFile, cabinet, "Cabinet", this.Facade.Package.SourceLineNumbers, BindStage.Normal);
369 -
370 - WixBundlePayloadRow payload = (WixBundlePayloadRow)this.PayloadTable.CreateRow(this.Facade.Package.SourceLineNumbers);
371 - payload.Id = generatedId;
372 - payload.Name = cabinetName;
373 - payload.SourceFile = payloadSourceFile;
374 - payload.Compressed = packagePayload.Compressed;
375 - payload.UnresolvedSourceFile = cabinetName;
376 - payload.Package = packagePayload.Package;
377 - payload.Container = packagePayload.Container;
378 - payload.ContentFile = true;
379 - payload.EnableSignatureValidation = packagePayload.EnableSignatureValidation;
380 - payload.Packaging = packagePayload.Packaging;
381 - payload.ParentPackagePayload = packagePayload.Id;
392 + var generatedId = Common.GenerateIdentifier("cab", packagePayload.Id.Id, cabinet);
393 + var payloadSourceFile = this.ResolveRelatedFile(packagePayload.SourceFile.Path, packagePayload.UnresolvedSourceFile, cabinet, "Cabinet", this.Facade.PackageTuple.SourceLineNumbers, BindStage.Normal);
394 +
395 + var tuple = new WixBundlePayloadTuple(this.Facade.PackageTuple.SourceLineNumbers, new Identifier(AccessModifier.Private, generatedId))
396 + {
397 + Name = cabinetName,
398 + SourceFile = new IntermediateFieldPathValue { Path = payloadSourceFile },
399 + Compressed = packagePayload.Compressed,
400 + UnresolvedSourceFile = cabinetName,
401 + PackageRef = packagePayload.PackageRef,
402 + ContainerRef = packagePayload.ContainerRef,
403 + ContentFile = true,
404 + EnableSignatureValidation = packagePayload.EnableSignatureValidation,
405 + Packaging = packagePayload.Packaging,
406 + ParentPackagePayloadRef = packagePayload.Id.Id,
407 + };
408 +
409 + this.Section.Tuples.Add(tuple);
410 }
411 }
412 }
413 }
414 }
415
388 - private long ImportExternalFileAsPayloadsAndReturnInstallSize(Dtf.Database db, WixBundlePayloadRow packagePayload, bool longNamesInImage, bool compressed, ISet<string> payloadNames)
416 + private long ImportExternalFileAsPayloadsAndReturnInstallSize(Dtf.Database db, WixBundlePayloadTuple packagePayload, bool longNamesInImage, bool compressed, ISet<string> payloadNames)
417 {
418 long size = 0;
419
420 if (db.Tables.Contains("Component") && db.Tables.Contains("Directory") && db.Tables.Contains("File"))
421 {
394 - Hashtable directories = new Hashtable();
422 + var directories = new Dictionary<string, IResolvedDirectory>();
423
424 // Load up the directory hash table so we will be able to resolve source paths
425 // for files in the MSI database.
398 - using (Dtf.View view = db.OpenView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`"))
426 + using (var view = db.OpenView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`"))
427 {
428 view.Execute();
429 while (true)
430 {
403 - using (Dtf.Record record = view.Fetch())
431 + using (var record = view.Fetch())
432 {
433 if (null == record)
434 {
435 break;
436 }
437
410 - string sourceName = Common.GetName(record.GetString(3), true, longNamesInImage);
411 - directories.Add(record.GetString(1), new ResolvedDirectory(record.GetString(2), sourceName));
438 + var sourceName = Common.GetName(record.GetString(3), true, longNamesInImage);
439 +
440 + var resolvedDirectory = this.BackendHelper.CreateResolvedDirectory(record.GetString(2), sourceName);
441 +
442 + directories.Add(record.GetString(1), resolvedDirectory);
443 }
444 }
445 }
446
447 // Resolve the source paths to external files and add each file size to the total
448 // install size of the package.
418 - using (Dtf.View view = db.OpenView("SELECT `Directory_`, `File`, `FileName`, `File`.`Attributes`, `FileSize` FROM `Component`, `File` WHERE `Component`.`Component`=`File`.`Component_`"))
449 + using (var view = db.OpenView("SELECT `Directory_`, `File`, `FileName`, `File`.`Attributes`, `FileSize` FROM `Component`, `File` WHERE `Component`.`Component`=`File`.`Component_`"))
450 {
451 view.Execute();
452 while (true)
453 {
423 - using (Dtf.Record record = view.Fetch())
454 + using (var record = view.Fetch())
455 {
456 if (null == record)
457 {
458 break;
459 }
460
430 - // Skip adding the loose files as payloads if it was suppressed.
431 - if (!this.Facade.MsiPackage.SuppressLooseFilePayloadGeneration)
461 + // If the file is explicitly uncompressed or the MSI is uncompressed and the file is not
462 + // explicitly marked compressed then this is an external file.
463 + var compressionBit = record.GetInteger(4);
464 + if (WindowsInstallerConstants.MsidbFileAttributesNoncompressed == (compressionBit & WindowsInstallerConstants.MsidbFileAttributesNoncompressed) ||
465 + (!compressed && 0 == (compressionBit & WindowsInstallerConstants.MsidbFileAttributesCompressed)))
466 {
433 - // If the file is explicitly uncompressed or the MSI is uncompressed and the file is not
434 - // explicitly marked compressed then this is an external file.
435 - if (MsiInterop.MsidbFileAttributesNoncompressed == (record.GetInteger(4) & MsiInterop.MsidbFileAttributesNoncompressed) ||
436 - (!compressed && 0 == (record.GetInteger(4) & MsiInterop.MsidbFileAttributesCompressed)))
467 + string fileSourcePath = this.PathResolver.GetFileSourcePath(directories, record.GetString(1), record.GetString(3), compressed, longNamesInImage);
468 + var name = Path.Combine(Path.GetDirectoryName(packagePayload.Name), fileSourcePath);
469 +
470 + if (!payloadNames.Contains(name))
471 {
438 - string fileSourcePath = Binder.GetFileSourcePath(directories, record.GetString(1), record.GetString(3), compressed, longNamesInImage);
439 - string name = Path.Combine(Path.GetDirectoryName(packagePayload.Name), fileSourcePath);
472 + var generatedId = Common.GenerateIdentifier("f", packagePayload.Id.Id, record.GetString(2));
473 + var payloadSourceFile = this.ResolveRelatedFile(packagePayload.SourceFile.Path, packagePayload.UnresolvedSourceFile, fileSourcePath, "File", this.Facade.PackageTuple.SourceLineNumbers, BindStage.Normal);
474
441 - if (!payloadNames.Contains(name))
475 + var tuple = new WixBundlePayloadTuple(this.Facade.PackageTuple.SourceLineNumbers, new Identifier(AccessModifier.Private, generatedId))
476 {
443 - string generatedId = Common.GenerateIdentifier("f", packagePayload.Id, record.GetString(2));
444 - string payloadSourceFile = this.ResolveRelatedFile(packagePayload.UnresolvedSourceFile, fileSourcePath, "File", this.Facade.Package.SourceLineNumbers, BindStage.Normal);
445 -
446 - WixBundlePayloadRow payload = (WixBundlePayloadRow)this.PayloadTable.CreateRow(this.Facade.Package.SourceLineNumbers);
447 - payload.Id = generatedId;
448 - payload.Name = name;
449 - payload.SourceFile = payloadSourceFile;
450 - payload.Compressed = packagePayload.Compressed;
451 - payload.UnresolvedSourceFile = name;
452 - payload.Package = packagePayload.Package;
453 - payload.Container = packagePayload.Container;
454 - payload.ContentFile = true;
455 - payload.EnableSignatureValidation = packagePayload.EnableSignatureValidation;
456 - payload.Packaging = packagePayload.Packaging;
457 - payload.ParentPackagePayload = packagePayload.Id;
458 - }
477 + Name = name,
478 + SourceFile = new IntermediateFieldPathValue { Path = payloadSourceFile },
479 + Compressed = packagePayload.Compressed,
480 + UnresolvedSourceFile = name,
481 + PackageRef = packagePayload.PackageRef,
482 + ContainerRef = packagePayload.ContainerRef,
483 + ContentFile = true,
484 + EnableSignatureValidation = packagePayload.EnableSignatureValidation,
485 + Packaging = packagePayload.Packaging,
486 + ParentPackagePayloadRef = packagePayload.Id.Id,
487 + };
488 +
489 + this.Section.Tuples.Add(tuple);
490 }
491 }
492
@@ -468,26 +499,30 @@ namespace WixToolset.Core.Burn.Bundles
499 return size;
500 }
501
471 - private void AddMsiProperty(string name, string value)
502 + private void AddMsiProperty(WixBundleMsiPackageTuple msiPackage, string name, string value)
503 {
473 - WixBundleMsiPropertyRow row = (WixBundleMsiPropertyRow)this.MsiPropertyTable.CreateRow(this.Facade.MsiPackage.SourceLineNumbers);
474 - row.ChainPackageId = this.Facade.Package.WixChainItemId;
475 - row.Name = name;
476 - row.Value = value;
504 + var tuple = new WixBundleMsiPropertyTuple(msiPackage.SourceLineNumbers, new Identifier(AccessModifier.Private, msiPackage.Id.Id, name))
505 + {
506 + PackageRef = msiPackage.Id.Id,
507 + Name = name,
508 + Value = value
509 + };
510 +
511 + this.Section.Tuples.Add(tuple);
512 }
513
479 - private void ImportDependencyProviders(Dtf.Database db)
514 + private void ImportDependencyProviders(WixBundleMsiPackageTuple msiPackage, Dtf.Database db)
515 {
516 if (db.Tables.Contains("WixDependencyProvider"))
517 {
483 - string query = "SELECT `ProviderKey`, `Version`, `DisplayName`, `Attributes` FROM `WixDependencyProvider`";
518 + var query = "SELECT `ProviderKey`, `Version`, `DisplayName`, `Attributes` FROM `WixDependencyProvider`";
519
485 - using (Dtf.View view = db.OpenView(query))
520 + using (var view = db.OpenView(query))
521 {
522 view.Execute();
523 while (true)
524 {
490 - using (Dtf.Record record = view.Fetch())
525 + using (var record = view.Fetch())
526 {
527 if (null == record)
528 {
@@ -495,34 +530,51 @@ namespace WixToolset.Core.Burn.Bundles
530 }
531
532 // Import the provider key and attributes.
498 - string providerKey = record.GetString(1);
499 - string version = record.GetString(2) ?? this.Facade.MsiPackage.ProductVersion;
500 - string displayName = record.GetString(3) ?? this.Facade.Package.DisplayName;
501 - int attributes = record.GetInteger(4);
502 -
503 - ProvidesDependency dependency = new ProvidesDependency(providerKey, version, displayName, attributes);
504 - dependency.Imported = true;
505 -
506 - this.Facade.Provides.Add(dependency);
533 + var tuple = new ProvidesDependencyTuple(msiPackage.SourceLineNumbers)
534 + {
535 + PackageRef = msiPackage.Id.Id,
536 + Key = record.GetString(1),
537 + Version = record.GetString(2) ?? msiPackage.ProductVersion,
538 + DisplayName = record.GetString(3) ?? this.Facade.PackageTuple.DisplayName,
539 + Attributes = record.GetInteger(4),
540 + Imported = true
541 + };
542 +
543 + this.Section.Tuples.Add(tuple);
544 }
545 }
546 }
547 }
548 }
549
513 - private string ResolveRelatedFile(string sourceFile, string relatedSource, string type, SourceLineNumber sourceLineNumbers, BindStage stage)
550 + private string ResolveRelatedFile(string resolvedSource, string unresolvedSource, string relatedSource, string type, SourceLineNumber sourceLineNumbers, BindStage stage)
551 {
552 + var checkedPaths = new List<string>();
553 +
554 foreach (var extension in this.BackendExtensions)
555 {
517 - var relatedFile = extension.ResolveRelatedFile(sourceFile, relatedSource, type, sourceLineNumbers, stage);
556 + var resolved = extension.ResolveRelatedFile(unresolvedSource, relatedSource, type, sourceLineNumbers, stage);
557
519 - if (!String.IsNullOrEmpty(relatedFile))
558 + if (resolved?.CheckedPaths != null)
559 {
521 - return relatedFile;
560 + checkedPaths.AddRange(resolved.CheckedPaths);
561 + }
562 +
563 + if (!String.IsNullOrEmpty(resolved?.Path))
564 + {
565 + return resolved?.Path;
566 }
567 }
568
525 - return null;
569 + var resolvedPath = Path.Combine(Path.GetDirectoryName(resolvedSource), relatedSource);
570 +
571 + if (!File.Exists(resolvedPath))
572 + {
573 + checkedPaths.Add(resolvedPath);
574 + this.Messaging.Write(ErrorMessages.FileNotFound(sourceLineNumbers, resolvedPath, type, checkedPaths));
575 + }
576 +
577 + return resolvedPath;
578 }
579
580 /// <summary>
@@ -571,6 +623,5 @@ namespace WixToolset.Core.Burn.Bundles
623
624 return String.Format(CultureInfo.InvariantCulture, ProcessMsiPackageCommand.PropertySqlFormat, property);
625 }
574 -#endif
626 }
627 }
src/WixToolset.Core.Burn/Bundles/ProcessMspPackageCommand.cs
+55 -47
@@ -10,6 +10,8 @@ namespace WixToolset.Core.Burn.Bundles
10 using System.Text;
11 using System.Xml;
12 using WixToolset.Data;
13 + using WixToolset.Data.Tuples;
14 + using WixToolset.Extensibility.Services;
15 using Dtf = WixToolset.Dtf.WindowsInstaller;
16
17 /// <summary>
@@ -17,80 +19,92 @@ namespace WixToolset.Core.Burn.Bundles
19 /// </summary>
20 internal class ProcessMspPackageCommand
21 {
20 -#if TODO
22 private const string PatchMetadataFormat = "SELECT `Value` FROM `MsiPatchMetadata` WHERE `Property` = '{0}'";
23 private static readonly Encoding XmlOutputEncoding = new UTF8Encoding(false);
24
24 - public RowDictionary<WixBundlePayloadRow> AuthoredPayloads { private get; set; }
25 + public ProcessMspPackageCommand(IMessaging messaging, IntermediateSection section, PackageFacade facade, Dictionary<string, WixBundlePayloadTuple> payloadTuples)
26 + {
27 + this.Messaging = messaging;
28 +
29 + this.AuthoredPayloads = payloadTuples;
30 + this.Section = section;
31 + this.Facade = facade;
32 + }
33 +
34 + public IMessaging Messaging { get; }
35 +
36 + public Dictionary<string, WixBundlePayloadTuple> AuthoredPayloads { private get; set; }
37
38 public PackageFacade Facade { private get; set; }
39
28 - public Table WixBundlePatchTargetCodeTable { private get; set; }
40 + public IntermediateSection Section { get; }
41
42 /// <summary>
43 /// Processes the Msp packages to add properties and payloads from the Msp packages.
44 /// </summary>
45 public void Execute()
46 {
35 - WixBundlePayloadRow packagePayload = this.AuthoredPayloads.Get(this.Facade.Package.PackagePayload);
47 + var packagePayload = this.AuthoredPayloads[this.Facade.PackageTuple.PayloadRef];
48 +
49 + var mspPackage = (WixBundleMspPackageTuple)this.Facade.SpecificPackageTuple;
50
37 - string sourcePath = packagePayload.FullFileName;
51 + var sourcePath = packagePayload.SourceFile.Path;
52
53 try
54 {
55 // Read data out of the msp database...
42 - using (Dtf.SummaryInfo sumInfo = new Dtf.SummaryInfo(sourcePath, false))
56 + using (var sumInfo = new Dtf.SummaryInfo(sourcePath, false))
57 {
44 - this.Facade.MspPackage.PatchCode = sumInfo.RevisionNumber.Substring(0, 38);
58 + mspPackage.PatchCode = sumInfo.RevisionNumber.Substring(0, 38);
59 }
60
47 - using (Dtf.Database db = new Dtf.Database(sourcePath))
61 + using (var db = new Dtf.Database(sourcePath))
62 {
49 - if (String.IsNullOrEmpty(this.Facade.Package.DisplayName))
63 + if (String.IsNullOrEmpty(this.Facade.PackageTuple.DisplayName))
64 {
51 - this.Facade.Package.DisplayName = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "DisplayName");
65 + this.Facade.PackageTuple.DisplayName = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "DisplayName");
66 }
67
54 - if (String.IsNullOrEmpty(this.Facade.Package.Description))
68 + if (String.IsNullOrEmpty(this.Facade.PackageTuple.Description))
69 {
56 - this.Facade.Package.Description = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "Description");
70 + this.Facade.PackageTuple.Description = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "Description");
71 }
72
59 - this.Facade.MspPackage.Manufacturer = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "ManufacturerName");
73 + mspPackage.Manufacturer = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "ManufacturerName");
74 }
75
62 - this.ProcessPatchXml(packagePayload, sourcePath);
76 + this.ProcessPatchXml(packagePayload, mspPackage, sourcePath);
77 }
78 catch (Dtf.InstallerException e)
79 {
66 - Messaging.Instance.OnMessage(WixErrors.UnableToReadPackageInformation(packagePayload.SourceLineNumbers, sourcePath, e.Message));
80 + this.Messaging.Write(ErrorMessages.UnableToReadPackageInformation(packagePayload.SourceLineNumbers, sourcePath, e.Message));
81 return;
82 }
83
70 - if (String.IsNullOrEmpty(this.Facade.Package.CacheId))
84 + if (String.IsNullOrEmpty(this.Facade.PackageTuple.CacheId))
85 {
72 - this.Facade.Package.CacheId = this.Facade.MspPackage.PatchCode;
86 + this.Facade.PackageTuple.CacheId = mspPackage.PatchCode;
87 }
88 }
89
76 - private void ProcessPatchXml(WixBundlePayloadRow packagePayload, string sourcePath)
90 + private void ProcessPatchXml(WixBundlePayloadTuple packagePayload, WixBundleMspPackageTuple mspPackage, string sourcePath)
91 {
78 - HashSet<string> uniqueTargetCodes = new HashSet<string>();
92 + var uniqueTargetCodes = new HashSet<string>();
93
80 - string patchXml = Dtf.Installer.ExtractPatchXmlData(sourcePath);
94 + var patchXml = Dtf.Installer.ExtractPatchXmlData(sourcePath);
95
82 - XmlDocument doc = new XmlDocument();
96 + var doc = new XmlDocument();
97 doc.LoadXml(patchXml);
98
85 - XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
99 + var nsmgr = new XmlNamespaceManager(doc.NameTable);
100 nsmgr.AddNamespace("p", "http://www.microsoft.com/msi/patch_applicability.xsd");
101
102 // Determine target ProductCodes and/or UpgradeCodes.
103 foreach (XmlNode node in doc.SelectNodes("/p:MsiPatch/p:TargetProduct", nsmgr))
104 {
105 // If this patch targets a product code, this is the best case.
92 - XmlNode targetCodeElement = node.SelectSingleNode("p:TargetProductCode", nsmgr);
93 - WixBundlePatchTargetCodeAttributes attributes = WixBundlePatchTargetCodeAttributes.None;
106 + var targetCodeElement = node.SelectSingleNode("p:TargetProductCode", nsmgr);
107 + var attributes = WixBundlePatchTargetCodeAttributes.None;
108
109 if (ProcessMspPackageCommand.TargetsCode(targetCodeElement))
110 {
@@ -105,45 +119,49 @@ namespace WixToolset.Core.Burn.Bundles
119 }
120 else // this patch targets an unknown number of products
121 {
108 - this.Facade.MspPackage.Attributes |= WixBundleMspPackageAttributes.TargetUnspecified;
122 + mspPackage.Attributes |= WixBundleMspPackageAttributes.TargetUnspecified;
123 }
124 }
125
112 - string targetCode = targetCodeElement.InnerText;
126 + var targetCode = targetCodeElement.InnerText;
127
128 if (uniqueTargetCodes.Add(targetCode))
129 {
116 - WixBundlePatchTargetCodeRow row = (WixBundlePatchTargetCodeRow)this.WixBundlePatchTargetCodeTable.CreateRow(packagePayload.SourceLineNumbers);
117 - row.MspPackageId = packagePayload.Id;
118 - row.TargetCode = targetCode;
119 - row.Attributes = attributes;
130 + var tuple = new WixBundlePatchTargetCodeTuple(packagePayload.SourceLineNumbers)
131 + {
132 + PackageRef = packagePayload.Id.Id,
133 + TargetCode = targetCode,
134 + Attributes = attributes
135 + };
136 +
137 + this.Section.Tuples.Add(tuple);
138 }
139 }
140
141 // Suppress patch sequence data for improved performance.
124 - XmlNode root = doc.DocumentElement;
142 + var root = doc.DocumentElement;
143 foreach (XmlNode node in root.SelectNodes("p:SequenceData", nsmgr))
144 {
145 root.RemoveChild(node);
146 }
147
148 // Save the XML as compact as possible.
131 - using (StringWriter writer = new StringWriter())
149 + using (var writer = new StringWriter())
150 {
133 - XmlWriterSettings settings = new XmlWriterSettings()
151 + var settings = new XmlWriterSettings()
152 {
153 Encoding = ProcessMspPackageCommand.XmlOutputEncoding,
154 Indent = false,
137 - NewLineChars = string.Empty,
155 + NewLineChars = String.Empty,
156 NewLineHandling = NewLineHandling.Replace,
157 };
158
141 - using (XmlWriter xmlWriter = XmlWriter.Create(writer, settings))
159 + using (var xmlWriter = XmlWriter.Create(writer, settings))
160 {
161 doc.WriteTo(xmlWriter);
162 }
163
146 - this.Facade.MspPackage.PatchXml = writer.ToString();
164 + mspPackage.PatchXml = writer.ToString();
165 }
166 }
167
@@ -175,16 +193,6 @@ namespace WixToolset.Core.Burn.Bundles
193 return String.Format(CultureInfo.InvariantCulture, ProcessMspPackageCommand.PatchMetadataFormat, property);
194 }
195
178 - private static bool TargetsCode(XmlNode node)
179 - {
180 - if (null != node)
181 - {
182 - XmlAttribute attr = node.Attributes["Validate"];
183 - return null != attr && "true".Equals(attr.Value);
184 - }
185 -
186 - return false;
187 - }
188 -#endif
196 + private static bool TargetsCode(XmlNode node) => "true" == node?.Attributes["Validate"]?.Value;
197 }
198 }
src/WixToolset.Core.Burn/Bundles/ProcessMsuPackageCommand.cs
+13 -7
@@ -3,29 +3,35 @@
3 namespace WixToolset.Core.Burn.Bundles
4 {
5 using System;
6 + using System.Collections.Generic;
7 using WixToolset.Data;
8 + using WixToolset.Data.Tuples;
9
10 /// <summary>
11 /// Processes the Msu packages to add properties and payloads from the Msu packages.
12 /// </summary>
13 internal class ProcessMsuPackageCommand
14 {
13 -#if TODO
14 - public RowDictionary<WixBundlePayload> AuthoredPayloads { private get; set; }
15 + public ProcessMsuPackageCommand(PackageFacade facade, Dictionary<string, WixBundlePayloadTuple> payloadTuples)
16 + {
17 + this.AuthoredPayloads = payloadTuples;
18 + this.Facade = facade;
19 + }
20 +
21 + public Dictionary<string, WixBundlePayloadTuple> AuthoredPayloads { private get; set; }
22
23 public PackageFacade Facade { private get; set; }
24
25 public void Execute()
26 {
20 - WixBundlePayloadRow packagePayload = this.AuthoredPayloads.Get(this.Facade.Package.PackagePayload);
27 + var packagePayload = this.AuthoredPayloads[this.Facade.PackageTuple.PayloadRef];
28
22 - if (String.IsNullOrEmpty(this.Facade.Package.CacheId))
29 + if (String.IsNullOrEmpty(this.Facade.PackageTuple.CacheId))
30 {
24 - this.Facade.Package.CacheId = packagePayload.Hash;
31 + this.Facade.PackageTuple.CacheId = packagePayload.Hash;
32 }
33
27 - this.Facade.Package.PerMachine = YesNoDefaultType.Yes; // MSUs are always per-machine.
34 + this.Facade.PackageTuple.PerMachine = YesNoDefaultType.Yes; // MSUs are always per-machine.
35 }
29 -#endif
36 }
37 }
src/WixToolset.Core.Burn/Bundles/ProcessPayloadsCommand.cs
+52 -37
@@ -9,58 +9,73 @@ namespace WixToolset.Core.Burn.Bundles
9 using System.Security.Cryptography;
10 using System.Security.Cryptography.X509Certificates;
11 using System.Text;
12 + using WixToolset.Data;
13 + using WixToolset.Data.Burn;
14 + using WixToolset.Data.Tuples;
15 + using WixToolset.Extensibility.Data;
16 + using WixToolset.Extensibility.Services;
17
18 internal class ProcessPayloadsCommand
19 {
15 -#if TODO
20 private static readonly Version EmptyVersion = new Version(0, 0, 0, 0);
21
18 - public IEnumerable<WixBundlePayloadRow> Payloads { private get; set; }
22 + public ProcessPayloadsCommand(IServiceProvider serviceProvider, IBackendHelper backendHelper, IEnumerable<WixBundlePayloadTuple> payloads, PackagingType defaultPackaging, string layoutDirectory)
23 + {
24 + this.Messaging = serviceProvider.GetService<IMessaging>();
25 +
26 + this.BackendHelper = backendHelper;
27 + this.Payloads = payloads;
28 + this.DefaultPackaging = defaultPackaging;
29 + this.LayoutDirectory = layoutDirectory;
30 + }
31 +
32 + public IEnumerable<IFileTransfer> FileTransfers { get; private set; }
33
20 - public PackagingType DefaultPackaging { private get; set; }
34 + private IMessaging Messaging { get; }
35
22 - public string LayoutDirectory { private get; set; }
36 + private IBackendHelper BackendHelper { get; }
37
24 - public IEnumerable<FileTransfer> FileTransfers { get; private set; }
38 + private IEnumerable<WixBundlePayloadTuple> Payloads { get; }
39 +
40 + private PackagingType DefaultPackaging { get; }
41 +
42 + private string LayoutDirectory { get; }
43
44 public void Execute()
45 {
28 - List<FileTransfer> fileTransfers = new List<FileTransfer>();
46 + var fileTransfers = new List<IFileTransfer>();
47
30 - foreach (WixBundlePayloadRow payload in this.Payloads)
48 + foreach (var payload in this.Payloads)
49 {
32 - string normalizedPath = payload.Name.Replace('\\', '/');
50 + var normalizedPath = payload.Name.Replace('\\', '/');
51 if (normalizedPath.StartsWith("../", StringComparison.Ordinal) || normalizedPath.Contains("/../"))
52 {
35 - Messaging.Instance.OnMessage(WixErrors.PayloadMustBeRelativeToCache(payload.SourceLineNumbers, "Payload", "Name", payload.Name));
53 + this.Messaging.Write(ErrorMessages.PayloadMustBeRelativeToCache(payload.SourceLineNumbers, "Payload", "Name", payload.Name));
54 }
55
56 // Embedded files (aka: files from binary .wixlibs) are not content files (because they are hidden
57 // in the .wixlib).
40 - ObjectField field = (ObjectField)payload.Fields[2];
41 - payload.ContentFile = !field.EmbeddedFileIndex.HasValue;
58 + var sourceFile = payload.SourceFile;
59 + payload.ContentFile = !sourceFile.EmbeddedFileIndex.HasValue;
60
61 this.UpdatePayloadPackagingType(payload);
62
45 - if (String.IsNullOrEmpty(payload.SourceFile))
63 + if (String.IsNullOrEmpty(sourceFile?.Path))
64 {
65 // Remote payloads obviously cannot be embedded.
66 Debug.Assert(PackagingType.Embedded != payload.Packaging);
67 }
68 else // not a remote payload so we have a lot more to update.
69 {
52 - this.UpdatePayloadFileInformation(payload);
70 + this.UpdatePayloadFileInformation(payload, sourceFile);
71
54 - this.UpdatePayloadVersionInformation(payload);
72 + this.UpdatePayloadVersionInformation(payload, sourceFile);
73
74 // External payloads need to be transfered.
75 if (PackagingType.External == payload.Packaging)
76 {
59 - FileTransfer transfer;
60 - if (FileTransfer.TryCreate(payload.FullFileName, Path.Combine(this.LayoutDirectory, payload.Name), false, "Payload", payload.SourceLineNumbers, out transfer))
61 - {
62 - fileTransfers.Add(transfer);
63 - }
77 + var transfer = this.BackendHelper.CreateFileTransfer(sourceFile.Path, Path.Combine(this.LayoutDirectory, payload.Name), false, payload.SourceLineNumbers);
78 + fileTransfers.Add(transfer);
79 }
80 }
81 }
@@ -68,41 +83,41 @@ namespace WixToolset.Core.Burn.Bundles
83 this.FileTransfers = fileTransfers;
84 }
85
71 - private void UpdatePayloadPackagingType(WixBundlePayloadRow payload)
86 + private void UpdatePayloadPackagingType(WixBundlePayloadTuple payload)
87 {
88 if (PackagingType.Unknown == payload.Packaging)
89 {
75 - if (YesNoDefaultType.Yes == payload.Compressed)
90 + if (!payload.Compressed.HasValue)
91 {
77 - payload.Packaging = PackagingType.Embedded;
92 + payload.Packaging = this.DefaultPackaging;
93 }
79 - else if (YesNoDefaultType.No == payload.Compressed)
94 + else if (payload.Compressed.Value)
95 {
81 - payload.Packaging = PackagingType.External;
96 + payload.Packaging = PackagingType.Embedded;
97 }
98 else
99 {
85 - payload.Packaging = this.DefaultPackaging;
100 + payload.Packaging = PackagingType.External;
101 }
102 }
103
104 // Embedded payloads that are not assigned a container already are placed in the default attached
105 // container.
91 - if (PackagingType.Embedded == payload.Packaging && String.IsNullOrEmpty(payload.Container))
106 + if (PackagingType.Embedded == payload.Packaging && String.IsNullOrEmpty(payload.ContainerRef))
107 {
93 - payload.Container = Compiler.BurnDefaultAttachedContainerId;
108 + payload.ContainerRef = BurnConstants.BurnDefaultAttachedContainerName;
109 }
110 }
111
97 - private void UpdatePayloadFileInformation(WixBundlePayloadRow payload)
112 + private void UpdatePayloadFileInformation(WixBundlePayloadTuple payload, IntermediateFieldPathValue sourceFile)
113 {
99 - FileInfo fileInfo = new FileInfo(payload.SourceFile);
114 + var fileInfo = new FileInfo(sourceFile.Path);
115
116 if (null != fileInfo)
117 {
118 payload.FileSize = (int)fileInfo.Length;
119
105 - payload.Hash = Common.GetFileHash(fileInfo.FullName);
120 + payload.Hash = BundleHashAlgorithm.Hash(fileInfo);
121
122 // Try to get the certificate if the payload is a signed file and we're not suppressing signature validation.
123 if (payload.EnableSignatureValidation)
@@ -122,9 +137,10 @@ namespace WixToolset.Core.Burn.Bundles
137 byte[] publicKeyIdentifierHash = new byte[128];
138 uint publicKeyIdentifierHashSize = (uint)publicKeyIdentifierHash.Length;
139
125 - WixToolset.Core.Native.NativeMethods.HashPublicKeyInfo(certificate.Handle, publicKeyIdentifierHash, ref publicKeyIdentifierHashSize);
126 - StringBuilder sb = new StringBuilder(((int)publicKeyIdentifierHashSize + 1) * 2);
127 - for (int i = 0; i < publicKeyIdentifierHashSize; ++i)
140 + Native.NativeMethods.HashPublicKeyInfo(certificate.Handle, publicKeyIdentifierHash, ref publicKeyIdentifierHashSize);
141 +
142 + var sb = new StringBuilder(((int)publicKeyIdentifierHashSize + 1) * 2);
143 + for (var i = 0; i < publicKeyIdentifierHashSize; ++i)
144 {
145 sb.AppendFormat("{0:X2}", publicKeyIdentifierHash[i]);
146 }
@@ -136,14 +152,14 @@ namespace WixToolset.Core.Burn.Bundles
152 }
153 }
154
139 - private void UpdatePayloadVersionInformation(WixBundlePayloadRow payload)
155 + private void UpdatePayloadVersionInformation(WixBundlePayloadTuple payload, IntermediateFieldPathValue sourceFile)
156 {
141 - FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(payload.SourceFile);
157 + var versionInfo = FileVersionInfo.GetVersionInfo(sourceFile.Path);
158
159 if (null != versionInfo)
160 {
161 // Use the fixed version info block for the file since the resource text may not be a dotted quad.
146 - Version version = new Version(versionInfo.ProductMajorPart, versionInfo.ProductMinorPart, versionInfo.ProductBuildPart, versionInfo.ProductPrivatePart);
162 + var version = new Version(versionInfo.ProductMajorPart, versionInfo.ProductMinorPart, versionInfo.ProductBuildPart, versionInfo.ProductPrivatePart);
163
164 if (ProcessPayloadsCommand.EmptyVersion != version)
165 {
@@ -154,6 +170,5 @@ namespace WixToolset.Core.Burn.Bundles
170 payload.DisplayName = versionInfo.ProductName;
171 }
172 }
157 -#endif
173 }
174 }
src/WixToolset.Core.Burn/Bundles/VerifyPayloadsWithCatalogCommand.cs
+29 -20
@@ -9,31 +9,42 @@ namespace WixToolset.Core.Burn.Bundles
9 using System.Runtime.InteropServices;
10 using System.Text;
11 using WixToolset.Data;
12 + using WixToolset.Data.Tuples;
13 + using WixToolset.Extensibility.Services;
14
15 internal class VerifyPayloadsWithCatalogCommand
16 {
15 -#if TODO
16 - public IEnumerable<WixBundleCatalogRow> Catalogs { private get; set; }
17 + public VerifyPayloadsWithCatalogCommand(IMessaging messaging, IEnumerable<WixBundleCatalogTuple> catalogs, IEnumerable<WixBundlePayloadTuple> payloads)
18 + {
19 + this.Messaging = messaging;
20 + this.Catalogs = catalogs;
21 + this.Payloads = payloads;
22 + }
23 +
24 + private IMessaging Messaging { get; }
25 +
26 + private IEnumerable<WixBundleCatalogTuple> Catalogs { get; }
27
18 - public IEnumerable<WixBundlePayloadRow> Payloads { private get; set; }
28 + private IEnumerable<WixBundlePayloadTuple> Payloads { get; }
29
30 public void Execute()
31 {
22 - List<CatalogIdWithPath> catalogIdsWithPaths = this.Catalogs
32 + var catalogIdsWithPaths = this.Catalogs
33 .Join(this.Payloads,
24 - catalog => catalog.Payload,
25 - payload => payload.Id,
26 - (catalog, payload) => new CatalogIdWithPath() { Id = catalog.Id, FullPath = Path.GetFullPath(payload.SourceFile) })
34 + catalog => catalog.PayloadRef,
35 + payload => payload.Id.Id,
36 + (catalog, payload) => new CatalogIdWithPath() { Id = catalog.Id.Id, FullPath = Path.GetFullPath(payload.SourceFile.Path) })
37 .ToList();
38
29 - foreach (WixBundlePayloadRow payloadInfo in this.Payloads)
39 + foreach (var payloadInfo in this.Payloads)
40 {
41 // Payloads that are not embedded should be verfied.
42 if (String.IsNullOrEmpty(payloadInfo.EmbeddedId))
43 {
34 - bool validated = false;
44 + var sourceFile = payloadInfo.SourceFile.Path;
45 + var validated = false;
46
36 - foreach (CatalogIdWithPath catalog in catalogIdsWithPaths)
47 + foreach (var catalog in catalogIdsWithPaths)
48 {
49 if (!validated)
50 {
@@ -41,11 +52,10 @@ namespace WixToolset.Core.Burn.Bundles
52 uint cryptHashSize = 20;
53 byte[] cryptHashBytes = new byte[cryptHashSize];
54 int error;
44 - IntPtr fileHandle = IntPtr.Zero;
45 - using (FileStream payloadStream = File.OpenRead(payloadInfo.FullFileName))
55 + using (var payloadStream = File.OpenRead(sourceFile))
56 {
57 // Get the file handle
48 - fileHandle = payloadStream.SafeFileHandle.DangerousGetHandle();
58 + var fileHandle = payloadStream.SafeFileHandle.DangerousGetHandle();
59
60 // 20 bytes is usually the hash size. Future hashes may be bigger
61 if (!VerifyInterop.CryptCATAdminCalcHashFromFileHandle(fileHandle, ref cryptHashSize, cryptHashBytes, 0))
@@ -64,7 +74,7 @@ namespace WixToolset.Core.Burn.Bundles
74
75 if (0 != error)
76 {
67 - Messaging.Instance.OnMessage(WixErrors.CatalogFileHashFailed(payloadInfo.FullFileName, error));
77 + this.Messaging.Write(ErrorMessages.CatalogFileHashFailed(sourceFile, error));
78 }
79 }
80 }
@@ -79,15 +89,15 @@ namespace WixToolset.Core.Burn.Bundles
89 catalogData.pbCalculatedFileHash = Marshal.AllocCoTaskMem((int)cryptHashSize);
90 Marshal.Copy(cryptHashBytes, 0, catalogData.pbCalculatedFileHash, (int)cryptHashSize);
91
82 - StringBuilder hashString = new StringBuilder();
83 - foreach (byte hashByte in cryptHashBytes)
92 + var hashString = new StringBuilder();
93 + foreach (var hashByte in cryptHashBytes)
94 {
95 hashString.Append(hashByte.ToString("X2"));
96 }
97 catalogData.pcwszMemberTag = hashString.ToString();
98
99 // The file names need to be lower case for older OSes
90 - catalogData.pcwszMemberFilePath = payloadInfo.FullFileName.ToLowerInvariant();
100 + catalogData.pcwszMemberFilePath = sourceFile.ToLowerInvariant();
101 catalogData.pcwszCatalogFilePath = catalog.FullPath.ToLowerInvariant();
102
103 // Create WINTRUST_DATA structure
@@ -108,7 +118,7 @@ namespace WixToolset.Core.Burn.Bundles
118 long verifyResult = VerifyInterop.WinVerifyTrust(noWindow, ref verifyGuid, ref trustData);
119 if (0 == verifyResult)
120 {
111 - payloadInfo.Catalog = catalog.Id;
121 + payloadInfo.CatalogRef = catalog.Id;
122 validated = true;
123 break;
124 }
@@ -132,7 +142,7 @@ namespace WixToolset.Core.Burn.Bundles
142 // Error message if the file was not validated by one of the catalogs
143 if (!validated)
144 {
135 - Messaging.Instance.OnMessage(WixErrors.CatalogVerificationFailed(payloadInfo.FullFileName));
145 + this.Messaging.Write(ErrorMessages.CatalogVerificationFailed(sourceFile));
146 }
147 }
148 }
@@ -144,6 +154,5 @@ namespace WixToolset.Core.Burn.Bundles
154
155 public string FullPath { get; set; }
156 }
147 -#endif
157 }
158 }
src/WixToolset.Core.Burn/BurnBackendFactory.cs
+1 -2
@@ -1,11 +1,10 @@
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.
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.Core.Burn
4 {
5 using System;
6 using System.IO;
7 using WixToolset.Extensibility;
8 - using WixToolset.Extensibility.Data;
8
9 internal class BurnBackendFactory : IBackendFactory
10 {
src/WixToolset.Core.Burn/VerifyInterop.cs
-2
@@ -3,8 +3,6 @@
3 namespace WixToolset
4 {
5 using System;
6 - using System.Collections;
7 - using System.Runtime.CompilerServices;
6 using System.Runtime.InteropServices;
7
8 internal class VerifyInterop
src/WixToolset.Core.Burn/WixToolset.Core.Burn.csproj
+1
@@ -15,6 +15,7 @@
15 </ItemGroup>
16
17 <ItemGroup>
18 + <PackageReference Include="System.Security.Permissions" Version="4.6.0" />
19 <PackageReference Include="WixToolset.Core.Native" Version="4.0.*" />
20 <PackageReference Include="WixToolset.Dtf.Resources" Version="4.0.*" NoWarn="NU1701" />
21 <PackageReference Include="WixToolset.Dtf.WindowsInstaller" Version="4.0.*" NoWarn="NU1701" />
src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+6 -2
@@ -30,6 +30,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
30
31 this.BackendHelper = context.ServiceProvider.GetService<IBackendHelper>();
32
33 + this.PathResolver = this.ServiceProvider.GetService<IPathResolver>();
34 +
35 this.TableDefinitions = WindowsInstallerStandardInternal.GetTableDefinitions();
36
37 this.CabbingThreadCount = context.CabbingThreadCount;
@@ -54,6 +56,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
56
57 private IBackendHelper BackendHelper { get; }
58
59 + private IPathResolver PathResolver { get; }
60 +
61 private int Codepage { get; }
62
63 private int CabbingThreadCount { get; }
@@ -241,7 +245,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
245
246 // Set generated component guids.
247 {
244 - var command = new CalculateComponentGuids(this.Messaging, this.BackendHelper, section);
248 + var command = new CalculateComponentGuids(this.Messaging, this.BackendHelper, this.PathResolver, section);
249 command.Execute();
250 }
251
@@ -501,7 +505,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
505 // Process uncompressed files.
506 if (!this.Messaging.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any())
507 {
504 - var command = new ProcessUncompressedFilesCommand(section, this.BackendHelper);
508 + var command = new ProcessUncompressedFilesCommand(section, this.BackendHelper, this.PathResolver);
509 command.Compressed = compressed;
510 command.FileFacades = uncompressedFiles;
511 command.LayoutDirectory = layoutDirectory;
src/WixToolset.Core.WindowsInstaller/Bind/CalculateComponentGuids.cs
+10 -6
@@ -6,9 +6,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Linq;
9 - using WixToolset.Core.Native;
9 using WixToolset.Data;
10 using WixToolset.Data.Tuples;
11 + using WixToolset.Extensibility.Data;
12 using WixToolset.Extensibility.Services;
13
14 /// <summary>
@@ -16,10 +16,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
16 /// </summary>
17 internal class CalculateComponentGuids
18 {
19 - internal CalculateComponentGuids(IMessaging messaging, IBackendHelper helper, IntermediateSection section)
19 + internal CalculateComponentGuids(IMessaging messaging, IBackendHelper helper, IPathResolver pathResolver, IntermediateSection section)
20 {
21 this.Messaging = messaging;
22 this.BackendHelper = helper;
23 + this.PathResolver = pathResolver;
24 this.Section = section;
25 }
26
@@ -27,12 +28,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind
28
29 private IBackendHelper BackendHelper { get; }
30
31 + private IPathResolver PathResolver { get; }
32 +
33 private IntermediateSection Section { get; }
34
35 public void Execute()
36 {
37 Dictionary<string, RegistryTuple> registryKeyRows = null;
35 - Dictionary<string, ResolvedDirectory> targetPathsByDirectoryId = null;
38 + Dictionary<string, IResolvedDirectory> targetPathsByDirectoryId = null;
39 Dictionary<string, string> componentIdGenSeeds = null;
40 Dictionary<string, List<FileTuple>> filesByComponentId = null;
41
@@ -73,7 +76,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
76 {
77 var directories = this.Section.Tuples.OfType<DirectoryTuple>().ToList();
78
76 - targetPathsByDirectoryId = new Dictionary<string, ResolvedDirectory>(directories.Count);
79 + targetPathsByDirectoryId = new Dictionary<string, IResolvedDirectory>(directories.Count);
80
81 // Get the target paths for all directories.
82 foreach (var directory in directories)
@@ -86,7 +89,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
89 continue;
90 }
91
89 - targetPathsByDirectoryId.Add(directory.Id.Id, new ResolvedDirectory(directory.ParentDirectoryRef, directory.Name));
92 + var resolvedDirectory = this.BackendHelper.CreateResolvedDirectory(directory.ParentDirectoryRef, directory.Name);
93 + targetPathsByDirectoryId.Add(directory.Id.Id, resolvedDirectory);
94 }
95 }
96
@@ -131,7 +135,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
135 if (fileRow.Id.Id == componentTuple.KeyPath)
136 {
137 // calculate the key file's canonical target path
134 - string directoryPath = PathResolver.GetDirectoryPath(targetPathsByDirectoryId, componentIdGenSeeds, componentTuple.DirectoryRef, true);
138 + string directoryPath = this.PathResolver.GetDirectoryPath(targetPathsByDirectoryId, componentIdGenSeeds, componentTuple.DirectoryRef, true);
139 string fileName = Common.GetName(fileRow.Name, false, true).ToLowerInvariant();
140 path = Path.Combine(directoryPath, fileName);
141
src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs
-1
@@ -8,7 +8,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
8 using System.IO;
9 using System.Linq;
10 using System.Runtime.InteropServices;
11 - using System.Threading;
11 using WixToolset.Core.Bind;
12 using WixToolset.Data;
13 using WixToolset.Data.Tuples;
src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs
+16
@@ -105,6 +105,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
105 this.AddMsiEmbeddedUITuple((MsiEmbeddedUITuple)tuple, output);
106 break;
107
108 + case TupleDefinitionType.MsiFileHash:
109 + this.AddMsiFileHashTuple((MsiFileHashTuple)tuple, output);
110 + break;
111 +
112 case TupleDefinitionType.MsiServiceConfig:
113 this.AddMsiServiceConfigTuple((MsiServiceConfigTuple)tuple, output);
114 break;
@@ -500,6 +504,18 @@ namespace WixToolset.Core.WindowsInstaller.Bind
504 row[4] = tuple.Source;
505 }
506
507 + private void AddMsiFileHashTuple(MsiFileHashTuple tuple, Output output)
508 + {
509 + var table = output.EnsureTable(this.TableDefinitions["MsiFileHash"]);
510 + var row = table.CreateRow(tuple.SourceLineNumbers);
511 + row[0] = tuple.Id.Id;
512 + row[1] = tuple.Options;
513 + row[2] = tuple.HashPart1;
514 + row[3] = tuple.HashPart2;
515 + row[4] = tuple.HashPart3;
516 + row[5] = tuple.HashPart4;
517 + }
518 +
519 private void AddMsiServiceConfigTuple(MsiServiceConfigTuple tuple, Output output)
520 {
521 var events = tuple.OnInstall ? WindowsInstallerConstants.MsidbServiceConfigEventInstall : 0;
src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs
+9 -4
@@ -18,16 +18,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind
18 /// </summary>
19 internal class ProcessUncompressedFilesCommand
20 {
21 - public ProcessUncompressedFilesCommand(IntermediateSection section, IBackendHelper backendHelper)
21 + public ProcessUncompressedFilesCommand(IntermediateSection section, IBackendHelper backendHelper, IPathResolver pathResolver)
22 {
23 this.Section = section;
24 this.BackendHelper = backendHelper;
25 + this.PathResolver = pathResolver;
26 }
27
28 private IntermediateSection Section { get; }
29
30 public IBackendHelper BackendHelper { get; }
31
32 + public IPathResolver PathResolver { get; }
33 +
34 public string DatabasePath { private get; set; }
35
36 public IEnumerable<FileFacade> FileFacades { private get; set; }
@@ -50,7 +53,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
53
54 var trackedFiles = new List<ITrackedFile>();
55
53 - var directories = new Dictionary<string, ResolvedDirectory>();
56 + var directories = new Dictionary<string, IResolvedDirectory>();
57
58 var mediaRows = this.Section.Tuples.OfType<MediaTuple>().ToDictionary(t => t.DiskId);
59
@@ -69,7 +72,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
72
73 string sourceName = Common.GetName(directoryRecord.GetString(3), true, this.LongNamesInImage);
74
72 - directories.Add(directoryRecord.GetString(1), new ResolvedDirectory(directoryRecord.GetString(2), sourceName));
75 + var resolvedDirectory = this.BackendHelper.CreateResolvedDirectory(directoryRecord.GetString(2), sourceName);
76 +
77 + directories.Add(directoryRecord.GetString(1), resolvedDirectory);
78 }
79 }
80 }
@@ -99,7 +104,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
104 throw new WixException(ErrorMessages.FileIdentifierNotFound(facade.File.SourceLineNumbers, facade.File.Id.Id));
105 }
106
102 - relativeFileLayoutPath = PathResolver.GetFileSourcePath(directories, fileRecord[1], fileRecord[2], this.Compressed, this.LongNamesInImage);
107 + relativeFileLayoutPath = this.PathResolver.GetFileSourcePath(directories, fileRecord[1], fileRecord[2], this.Compressed, this.LongNamesInImage);
108 }
109
110 // finally put together the base media layout path and the relative file layout path
src/WixToolset.Core.WindowsInstaller/Bind/ResolvedDirectory.cs deleted
-31
@@ -1,31 +0,0 @@
1 -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 -
3 -namespace WixToolset.Core.WindowsInstaller.Bind
4 -{
5 - /// <summary>
6 - /// Structure used for resolved directory information.
7 - /// </summary>
8 - internal struct ResolvedDirectory
9 - {
10 - /// <summary>
11 - /// Constructor for ResolvedDirectory.
12 - /// </summary>
13 - /// <param name="directoryParent">Parent directory.</param>
14 - /// <param name="name">The directory name.</param>
15 - public ResolvedDirectory(string directoryParent, string name)
16 - {
17 - this.DirectoryParent = directoryParent;
18 - this.Name = name;
19 - this.Path = null;
20 - }
21 -
22 - /// <summary>The directory parent.</summary>
23 - public string DirectoryParent { get; set; }
24 -
25 - /// <summary>The name of this directory.</summary>
26 - public string Name { get; set; }
27 -
28 - /// <summary>The path of this directory.</summary>
29 - public string Path { get; set; }
30 - }
31 -}
src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
-1
@@ -162,7 +162,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
162 this.Section.Tuples.Add(facade.Hash);
163 }
164
165 - facade.Hash.FileRef = facade.File.Id.Id;
165 facade.Hash.Options = 0;
166 facade.Hash.HashPart1 = hash[0];
167 facade.Hash.HashPart2 = hash[1];
src/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs
+4 -4
@@ -30,10 +30,10 @@ namespace WixToolset.Core.Bind
30 {
31 // If the uri to the file that contains the embedded file does not already have embedded files
32 // being extracted, create the dictionary to track that.
33 - if (!filesWithEmbeddedFiles.TryGetValue(uri, out var extracts))
33 + if (!this.filesWithEmbeddedFiles.TryGetValue(uri, out var extracts))
34 {
35 extracts = new SortedList<int, string>();
36 - filesWithEmbeddedFiles.Add(uri, extracts);
36 + this.filesWithEmbeddedFiles.Add(uri, extracts);
37 }
38
39 // If the embedded file is not already tracked in the dictionary of extracts, add it.
@@ -52,7 +52,7 @@ namespace WixToolset.Core.Bind
52
53 public IEnumerable<ExpectedExtractFile> GetExpectedEmbeddedFiles()
54 {
55 - foreach (var uriWithExtracts in filesWithEmbeddedFiles)
55 + foreach (var uriWithExtracts in this.filesWithEmbeddedFiles)
56 {
57 foreach (var extracts in uriWithExtracts.Value)
58 {
@@ -68,7 +68,7 @@ namespace WixToolset.Core.Bind
68
69 public IEnumerable<ExpectedExtractFile> GetExtractFilesForUri(Uri uri)
70 {
71 - if (!filesWithEmbeddedFiles.TryGetValue(uri, out var extracts))
71 + if (!this.filesWithEmbeddedFiles.TryGetValue(uri, out var extracts))
72 {
73 extracts = new SortedList<int, string>();
74 }
src/WixToolset.Core/Bind/FileResolver.cs
+7 -1
@@ -70,11 +70,17 @@ namespace WixToolset.Core.Bind
70 /// <param name="type">Optional type of source file being resolved.</param>
71 /// <param name="sourceLineNumbers">Optional source line of source file being resolved.</param>
72 /// <param name="bindStage">The binding stage used to determine what collection of bind paths will be used</param>
73 + /// <param name="alreadyCheckedPaths">Optional collection of paths already checked.</param>
74 /// <returns>Should return a valid path for the stream to be imported.</returns>
74 - public string ResolveFile(string source, IntermediateTupleDefinition tupleDefinition, SourceLineNumber sourceLineNumbers, BindStage bindStage)
75 + public string ResolveFile(string source, IntermediateTupleDefinition tupleDefinition, SourceLineNumber sourceLineNumbers, BindStage bindStage, IEnumerable<string> alreadyCheckedPaths = null)
76 {
77 var checkedPaths = new List<string>();
78
79 + if (alreadyCheckedPaths != null)
80 + {
81 + checkedPaths.AddRange(alreadyCheckedPaths);
82 + }
83 +
84 foreach (var extension in this.ResolverExtensions)
85 {
86 var resolved = extension.ResolveFile(source, tupleDefinition, sourceLineNumbers, bindStage);
src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs
+1 -1
@@ -113,7 +113,7 @@ namespace WixToolset.Core.Bind
113 }
114 }
115
116 - public static string ResolveDelayedVariables(SourceLineNumber sourceLineNumbers, string value, IDictionary<string, string> resolutionData)
116 + private static string ResolveDelayedVariables(SourceLineNumber sourceLineNumbers, string value, IDictionary<string, string> resolutionData)
117 {
118 var matches = Common.WixVariableRegex.Matches(value);
119
src/WixToolset.Core/BindContext.cs
+3 -1
@@ -1,4 +1,4 @@
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.
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.Core
4 {
@@ -19,6 +19,8 @@ namespace WixToolset.Core
19
20 public IEnumerable<BindPath> BindPaths { get; set; }
21
22 + public string BurnStubPath { get; set; }
23 +
24 public int CabbingThreadCount { get; set; }
25
26 public string CabCachePath { get; set; }
src/WixToolset.Core/CommandLine/BuildCommand.cs
+9 -2
@@ -122,7 +122,7 @@ namespace WixToolset.Core.CommandLine
122 }
123 else
124 {
125 - this.BindPhase(wixipl, wxls, filterCultures, this.commandLine.CabCachePath, this.commandLine.BindPaths);
125 + this.BindPhase(wixipl, wxls, filterCultures, this.commandLine.CabCachePath, this.commandLine.BindPaths, this.commandLine.BurnStubPath);
126 }
127 }
128 }
@@ -257,7 +257,7 @@ namespace WixToolset.Core.CommandLine
257 return linker.Link(context);
258 }
259
260 - private void BindPhase(Intermediate output, IEnumerable<Localization> localizations, IEnumerable<string> filterCultures, string cabCachePath, IEnumerable<IBindPath> bindPaths)
260 + private void BindPhase(Intermediate output, IEnumerable<Localization> localizations, IEnumerable<string> filterCultures, string cabCachePath, IEnumerable<IBindPath> bindPaths, string burnStubPath)
261 {
262 var intermediateFolder = this.IntermediateFolder;
263 if (String.IsNullOrEmpty(intermediateFolder))
@@ -290,6 +290,7 @@ namespace WixToolset.Core.CommandLine
290 {
291 var context = this.ServiceProvider.GetService<IBindContext>();
292 //context.CabbingThreadCount = this.CabbingThreadCount;
293 + context.BurnStubPath = burnStubPath;
294 context.CabCachePath = cabCachePath;
295 context.Codepage = resolveResult.Codepage;
296 //context.DefaultCompressionLevel = this.DefaultCompressionLevel;
@@ -399,6 +400,8 @@ namespace WixToolset.Core.CommandLine
400
401 public List<IBindPath> BindPaths { get; } = new List<IBindPath>();
402
403 + public string BurnStubPath { get; private set; }
404 +
405 public string CabCachePath { get; private set; }
406
407 public List<string> Cultures { get; } = new List<string>();
@@ -480,6 +483,10 @@ namespace WixToolset.Core.CommandLine
483 }
484 break;
485 }
486 + case "burnstub":
487 + this.BurnStubPath = parser.GetNextArgumentOrError(arg);
488 + return true;
489 +
490 case "cc":
491 this.CabCachePath = parser.GetNextArgumentOrError(arg);
492 return true;
src/WixToolset.Core/Compiler.cs
+1 -1
@@ -818,7 +818,7 @@ namespace WixToolset.Core
818 {
819 this.Core.AddTuple(new IconTuple(sourceLineNumbers, id)
820 {
821 - Data = sourceFile
821 + Data = new IntermediateFieldPathValue { Path = sourceFile }
822 });
823 }
824
src/WixToolset.Core/Compiler_Bundle.cs
+61 -78
@@ -9,6 +9,7 @@ namespace WixToolset.Core
9 using System.IO;
10 using System.Xml.Linq;
11 using WixToolset.Data;
12 + using WixToolset.Data.Burn;
13 using WixToolset.Data.Tuples;
14 using WixToolset.Extensibility;
15
@@ -17,15 +18,9 @@ namespace WixToolset.Core
18 /// </summary>
19 internal partial class Compiler : ICompiler
20 {
20 - public static readonly Identifier BurnUXContainerId = new Identifier(AccessModifier.Private, "WixUXContainer");
21 - public static readonly Identifier BurnDefaultAttachedContainerId = new Identifier(AccessModifier.Private, "WixAttachedContainer");
22 - public static readonly Identifier BundleLayoutOnlyPayloads = new Identifier(AccessModifier.Private, "BundleLayoutOnlyPayloads");
23 -
24 - // The following constants must stay in sync with src\burn\engine\core.h
25 - private const string BURN_BUNDLE_NAME = "WixBundleName";
26 - private const string BURN_BUNDLE_ORIGINAL_SOURCE = "WixBundleOriginalSource";
27 - private const string BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = "WixBundleOriginalSourceFolder";
28 - private const string BURN_BUNDLE_LAST_USED_SOURCE = "WixBundleLastUsedSource";
21 + private static readonly Identifier BurnUXContainerId = new Identifier(AccessModifier.Private, BurnConstants.BurnUXContainerName);
22 + private static readonly Identifier BurnDefaultAttachedContainerId = new Identifier(AccessModifier.Private, BurnConstants.BurnDefaultAttachedContainerName);
23 + private static readonly Identifier BundleLayoutOnlyPayloads = new Identifier(AccessModifier.Private, BurnConstants.BundleLayoutOnlyPayloadsName);
24
25 /// <summary>
26 /// Parses an ApprovedExeForElevation element.
@@ -78,11 +73,11 @@ namespace WixToolset.Core
73 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
74 }
75
81 - var attributes = BundleApprovedExeForElevationAttributes.None;
76 + var attributes = WixApprovedExeForElevationAttributes.None;
77
78 if (win64 == YesNoType.Yes)
79 {
85 - attributes |= BundleApprovedExeForElevationAttributes.Win64;
80 + attributes |= WixApprovedExeForElevationAttributes.Win64;
81 }
82
83 this.Core.ParseForExtensionElements(node);
@@ -92,7 +87,7 @@ namespace WixToolset.Core
87 var tuple = new WixApprovedExeForElevationTuple(sourceLineNumbers, id)
88 {
89 Key = key,
95 - Value = valueName,
90 + ValueName = valueName,
91 Attributes = attributes
92 };
93
@@ -110,8 +105,7 @@ namespace WixToolset.Core
105 string copyright = null;
106 string aboutUrl = null;
107 var compressed = YesNoDefaultType.Default;
113 - var disableModify = -1;
114 - var disableRemove = YesNoType.NotSet;
108 + WixBundleAttributes attributes = 0;
109 string helpTelephone = null;
110 string helpUrl = null;
111 string manufacturer = null;
@@ -152,13 +146,12 @@ namespace WixToolset.Core
146 switch (value)
147 {
148 case "button":
155 - disableModify = 2;
149 + attributes |= WixBundleAttributes.SingleChangeUninstallButton;
150 break;
151 case "yes":
158 - disableModify = 1;
152 + attributes |= WixBundleAttributes.DisableModify;
153 break;
154 case "no":
161 - disableModify = 0;
155 break;
156 default:
157 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, value, "button", "yes", "no"));
@@ -166,10 +159,10 @@ namespace WixToolset.Core
159 }
160 break;
161 case "DisableRemove":
169 - disableRemove = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
170 - break;
171 - case "DisableRepair":
172 - this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
162 + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
163 + {
164 + attributes |= WixBundleAttributes.DisableRemove;
165 + }
166 break;
167 case "HelpTelephone":
168 helpTelephone = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
@@ -239,13 +232,13 @@ namespace WixToolset.Core
232
233 if (String.IsNullOrEmpty(name))
234 {
242 - logVariablePrefixAndExtension = String.Concat("WixBundleLog:Setup.log");
235 + logVariablePrefixAndExtension = String.Concat("WixBundleLog:Setup:.log");
236 }
237 else
238 {
239 // Ensure only allowable path characters are in "name" (and change spaces to underscores).
240 fileSystemSafeBundleName = CompilerCore.MakeValidLongFileName(name.Replace(' ', '_'), "_");
248 - logVariablePrefixAndExtension = String.Concat("WixBundleLog:", fileSystemSafeBundleName, ".log");
241 + logVariablePrefixAndExtension = String.Concat("WixBundleLog:", fileSystemSafeBundleName, ":.log");
242 }
243
244 this.activeName = String.IsNullOrEmpty(name) ? Common.GenerateGuid() : name;
@@ -351,6 +344,37 @@ namespace WixToolset.Core
344
345 if (!this.Core.EncounteredError)
346 {
347 + var tuple = new WixBundleTuple(sourceLineNumbers)
348 + {
349 + UpgradeCode = upgradeCode,
350 + Version = version,
351 + Copyright = copyright,
352 + Name = name,
353 + Manufacturer = manufacturer,
354 + Attributes = attributes,
355 + AboutUrl = aboutUrl,
356 + HelpUrl = helpUrl,
357 + HelpTelephone = helpTelephone,
358 + UpdateUrl = updateUrl,
359 + Compressed = YesNoDefaultType.Yes == compressed ? true : YesNoDefaultType.No == compressed ? (bool?)false : null,
360 + IconSourceFile = iconSourceFile,
361 + SplashScreenSourceFile = splashScreenSourceFile,
362 + Condition = condition,
363 + Tag = tag,
364 + Platform = this.CurrentPlatform,
365 + ParentName = parentName,
366 + };
367 +
368 + if (!String.IsNullOrEmpty(logVariablePrefixAndExtension))
369 + {
370 + var split = logVariablePrefixAndExtension.Split(':');
371 + tuple.LogPathVariable = split[0];
372 + tuple.LogPrefix = split[1];
373 + tuple.LogExtension = split[2];
374 + }
375 +
376 + this.Core.AddTuple(tuple);;
377 +
378 if (null != upgradeCode)
379 {
380 this.Core.AddTuple(new WixRelatedBundleTuple(sourceLineNumbers)
@@ -360,64 +384,32 @@ namespace WixToolset.Core
384 });
385 }
386
363 - this.Core.AddTuple(new WixBundleContainerTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, Compiler.BurnDefaultAttachedContainerId))
387 + this.Core.AddTuple(new WixBundleContainerTuple(sourceLineNumbers, Compiler.BurnDefaultAttachedContainerId)
388 {
389 Name = "bundle-attached.cab",
390 Type = ContainerType.Attached
391 });
392
369 - var bundleTuple = this.Core.CreateTuple(sourceLineNumbers, TupleDefinitionType.WixBundle);
370 - bundleTuple.Set(0, version);
371 - bundleTuple.Set(1, copyright);
372 - bundleTuple.Set(2, name);
373 - bundleTuple.Set(3, aboutUrl);
374 - if (-1 != disableModify)
375 - {
376 - bundleTuple.Set(4, disableModify);
377 - }
378 - if (YesNoType.NotSet != disableRemove)
379 - {
380 - bundleTuple.Set(5, (YesNoType.Yes == disableRemove) ? 1 : 0);
381 - }
382 - // row.Set(6] - (deprecated) "disable repair"
383 - bundleTuple.Set(7, helpTelephone);
384 - bundleTuple.Set(8, helpUrl);
385 - bundleTuple.Set(9, manufacturer);
386 - bundleTuple.Set(10, updateUrl);
387 - if (YesNoDefaultType.Default != compressed)
388 - {
389 - bundleTuple.Set(11, (YesNoDefaultType.Yes == compressed) ? 1 : 0);
390 - }
391 -
392 - bundleTuple.Set(12, logVariablePrefixAndExtension);
393 - bundleTuple.Set(13, iconSourceFile);
394 - bundleTuple.Set(14, splashScreenSourceFile);
395 - bundleTuple.Set(15, condition);
396 - bundleTuple.Set(16, tag);
397 - bundleTuple.Set(17, this.CurrentPlatform.ToString());
398 - bundleTuple.Set(18, parentName);
399 - bundleTuple.Set(19, upgradeCode);
400 -
393 // Ensure that the bundle stores the well-known persisted values.
402 - this.Core.AddTuple(new WixBundleVariableTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, Compiler.BURN_BUNDLE_NAME))
394 + this.Core.AddTuple(new WixBundleVariableTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, BurnConstants.BURN_BUNDLE_NAME))
395 {
396 Hidden = false,
397 Persisted = true
398 });
399
408 - this.Core.AddTuple(new WixBundleVariableTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, Compiler.BURN_BUNDLE_ORIGINAL_SOURCE))
400 + this.Core.AddTuple(new WixBundleVariableTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, BurnConstants.BURN_BUNDLE_ORIGINAL_SOURCE))
401 {
402 Hidden = false,
403 Persisted = true
404 });
405
414 - this.Core.AddTuple(new WixBundleVariableTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, Compiler.BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER))
406 + this.Core.AddTuple(new WixBundleVariableTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, BurnConstants.BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER))
407 {
408 Hidden = false,
409 Persisted = true
410 });
411
420 - this.Core.AddTuple(new WixBundleVariableTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, Compiler.BURN_BUNDLE_LAST_USED_SOURCE))
412 + this.Core.AddTuple(new WixBundleVariableTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, BurnConstants.BURN_BUNDLE_LAST_USED_SOURCE))
413 {
414 Hidden = false,
415 Persisted = true
@@ -473,7 +465,7 @@ namespace WixToolset.Core
465
466 this.Core.ParseForExtensionElements(node);
467
476 - return YesNoType.Yes == disableLog ? null : String.Concat(variable, ":", logPrefix, logExtension);
468 + return YesNoType.Yes == disableLog ? null : String.Join(":", variable, logPrefix, logExtension);
469 }
470
471 /// <summary>
@@ -1126,9 +1118,9 @@ namespace WixToolset.Core
1118 tuple = new WixBundlePayloadTuple(sourceLineNumbers, id)
1119 {
1120 Name = String.IsNullOrEmpty(name) ? Path.GetFileName(sourceFile) : name,
1129 - SourceFile = sourceFile,
1121 + SourceFile = new IntermediateFieldPathValue { Path = sourceFile },
1122 DownloadUrl = downloadUrl,
1131 - Compressed = compressed,
1123 + Compressed = (compressed == YesNoDefaultType.Yes) ? true : (compressed == YesNoDefaultType.No) ? (bool?)false : null,
1124 UnresolvedSourceFile = sourceFile, // duplicate of sourceFile but in a string column so it won't get resolved to a full path during binding.
1125 DisplayName = displayName,
1126 Description = description,
@@ -1665,14 +1657,12 @@ namespace WixToolset.Core
1657 var vital = YesNoType.Yes;
1658 string installCommand = null;
1659 string repairCommand = null;
1668 - var repairable = YesNoType.NotSet;
1660 string uninstallCommand = null;
1661 var perMachine = YesNoDefaultType.NotSet;
1662 string detectCondition = null;
1663 string protocol = null;
1664 var installSize = CompilerConstants.IntegerNotSet;
1665 string msuKB = null;
1675 - var suppressLooseFilePayloadGeneration = YesNoType.NotSet;
1666 var enableSignatureVerification = YesNoType.No;
1667 var compressed = YesNoDefaultType.Default;
1668 var displayInternalUI = YesNoType.NotSet;
@@ -1779,7 +1769,6 @@ namespace WixToolset.Core
1769 break;
1770 case "RepairCommand":
1771 repairCommand = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
1782 - repairable = YesNoType.Yes;
1772 allowed = (packageType == WixBundlePackageType.Exe);
1773 break;
1774 case "UninstallCommand":
@@ -1808,11 +1797,6 @@ namespace WixToolset.Core
1797 case "Compressed":
1798 compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
1799 break;
1811 - case "SuppressLooseFilePayloadGeneration":
1812 - this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
1813 - suppressLooseFilePayloadGeneration = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1814 - allowed = (packageType == WixBundlePackageType.Msi);
1815 - break;
1800 case "EnableSignatureVerification":
1801 enableSignatureVerification = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1802 break;
@@ -2076,7 +2060,7 @@ namespace WixToolset.Core
2060 case WixBundlePackageType.Exe:
2061 this.Core.AddTuple(new WixBundleExePackageTuple(sourceLineNumbers, id)
2062 {
2079 - Attributes = (YesNoType.Yes == repairable) ? WixBundleExePackageAttributes.Repairable : 0,
2063 + Attributes = WixBundleExePackageAttributes.None,
2064 DetectCondition = detectCondition,
2065 InstallCommand = installCommand,
2066 RepairCommand = repairCommand,
@@ -2090,7 +2074,6 @@ namespace WixToolset.Core
2074 msiAttributes |= (YesNoType.Yes == displayInternalUI) ? WixBundleMsiPackageAttributes.DisplayInternalUI : 0;
2075 msiAttributes |= (YesNoType.Yes == enableFeatureSelection) ? WixBundleMsiPackageAttributes.EnableFeatureSelection : 0;
2076 msiAttributes |= (YesNoType.Yes == forcePerMachine) ? WixBundleMsiPackageAttributes.ForcePerMachine : 0;
2093 - msiAttributes |= (YesNoType.Yes == suppressLooseFilePayloadGeneration) ? WixBundleMsiPackageAttributes.SuppressLooseFilePayloadGeneration : 0;
2077
2078 this.Core.AddTuple(new WixBundleMsiPackageTuple(sourceLineNumbers, id)
2079 {
@@ -2458,9 +2441,9 @@ namespace WixToolset.Core
2441
2442 if (!this.Core.EncounteredError)
2443 {
2461 - var tuple = new WixBundleMsiPropertyTuple(sourceLineNumbers)
2444 + var tuple = new WixBundleMsiPropertyTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, packageId, name))
2445 {
2463 - WixBundlePackageRef = packageId,
2446 + PackageRef = packageId,
2447 Name = name,
2448 Value = value
2449 };
@@ -2514,10 +2497,10 @@ namespace WixToolset.Core
2497
2498 if (!this.Core.EncounteredError)
2499 {
2517 - this.Core.AddTuple(new WixBundleSlipstreamMspTuple(sourceLineNumbers)
2500 + this.Core.AddTuple(new WixBundleSlipstreamMspTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, packageId, id))
2501 {
2519 - WixBundlePackageRef = packageId,
2520 - MspWixBundlePackageRef = id
2502 + TargetPackageRef = packageId,
2503 + MspPackageRef = id
2504 });
2505 }
2506 }
src/WixToolset.Core/ExtensibilityServices/BackendHelper.cs
+10 -1
@@ -1,4 +1,4 @@
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.
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.Core.ExtensibilityServices
4 {
@@ -40,6 +40,15 @@ namespace WixToolset.Core.ExtensibilityServices
40 return Uuid.NewUuid(namespaceGuid, value).ToString("B").ToUpperInvariant();
41 }
42
43 + public IResolvedDirectory CreateResolvedDirectory(string directoryParent, string name)
44 + {
45 + return new ResolvedDirectory
46 + {
47 + DirectoryParent = directoryParent,
48 + Name = name
49 + };
50 + }
51 +
52 public ITrackedFile TrackFile(string path, TrackedFileType type, SourceLineNumber sourceLineNumbers = null)
53 {
54 return new TrackedFile(path, type, sourceLineNumbers);
src/WixToolset.Core/ExtensibilityServices/PathResolver.cs renamed
+9 -24
@@ -1,24 +1,18 @@
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 -namespace WixToolset.Core.WindowsInstaller.Bind
3 +namespace WixToolset.Core.ExtensibilityServices
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using WixToolset.Data;
9 using WixToolset.Data.WindowsInstaller;
10 + using WixToolset.Extensibility.Data;
11 + using WixToolset.Extensibility.Services;
12
11 - internal static class PathResolver
13 + internal class PathResolver : IPathResolver
14 {
13 - /// <summary>
14 - /// Get the source path of a directory.
15 - /// </summary>
16 - /// <param name="directories">All cached directories.</param>
17 - /// <param name="componentIdGenSeeds">Hash table of Component GUID generation seeds indexed by directory id.</param>
18 - /// <param name="directory">Directory identifier.</param>
19 - /// <param name="canonicalize">Canonicalize the path for standard directories.</param>
20 - /// <returns>Source path of a directory.</returns>
21 - public static string GetDirectoryPath(Dictionary<string, ResolvedDirectory> directories, Dictionary<string, string> componentIdGenSeeds, string directory, bool canonicalize)
15 + public string GetDirectoryPath(Dictionary<string, IResolvedDirectory> directories, Dictionary<string, string> componentIdGenSeeds, string directory, bool canonicalize)
16 {
17 if (!directories.TryGetValue(directory, out var resolvedDirectory))
18 {
@@ -51,7 +45,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
45 }
46 else
47 {
54 - string parentPath = GetDirectoryPath(directories, componentIdGenSeeds, resolvedDirectory.DirectoryParent, canonicalize);
48 + var parentPath = this.GetDirectoryPath(directories, componentIdGenSeeds, resolvedDirectory.DirectoryParent, canonicalize);
49
50 if (null != resolvedDirectory.Name)
51 {
@@ -68,18 +62,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
62 return resolvedDirectory.Path;
63 }
64
71 - /// <summary>
72 - /// Gets the source path of a file.
73 - /// </summary>
74 - /// <param name="directories">All cached directories in <see cref="ResolvedDirectory"/>.</param>
75 - /// <param name="directoryId">Parent directory identifier.</param>
76 - /// <param name="fileName">File name (in long|source format).</param>
77 - /// <param name="compressed">Specifies the package is compressed.</param>
78 - /// <param name="useLongName">Specifies the package uses long file names.</param>
79 - /// <returns>Source path of file relative to package directory.</returns>
80 - public static string GetFileSourcePath(Dictionary<string, ResolvedDirectory> directories, string directoryId, string fileName, bool compressed, bool useLongName)
65 + public string GetFileSourcePath(Dictionary<string, IResolvedDirectory> directories, string directoryId, string fileName, bool compressed, bool useLongName)
66 {
82 - string fileSourcePath = Common.GetName(fileName, true, useLongName);
67 + var fileSourcePath = Common.GetName(fileName, true, useLongName);
68
69 if (compressed)
70 {
@@ -90,7 +75,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
75 {
76 // Get the relative path of where we want the file to be layed out as specified
77 // in the Directory table.
93 - string directoryPath = PathResolver.GetDirectoryPath(directories, null, directoryId, false);
78 + var directoryPath = this.GetDirectoryPath(directories, null, directoryId, false);
79 fileSourcePath = Path.Combine(directoryPath, fileSourcePath);
80 }
81
src/WixToolset.Core/ExtensibilityServices/ResolvedDirectory.cs new
+15
@@ -0,0 +1,15 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolset.Core.ExtensibilityServices
4 +{
5 + using WixToolset.Extensibility.Data;
6 +
7 + internal class ResolvedDirectory : IResolvedDirectory
8 + {
9 + public string DirectoryParent { get; set; }
10 +
11 + public string Name { get; set; }
12 +
13 + public string Path { get; set; }
14 + }
15 +}
src/WixToolset.Core/ExtensibilityServices/WindowsInstallerBackendHelper.cs
+17 -15
@@ -1,8 +1,7 @@
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.
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.Core.ExtensibilityServices
4 {
5 - using System;
5 using System.Linq;
6 using WixToolset.Data;
7 using WixToolset.Data.WindowsInstaller;
@@ -10,14 +9,9 @@ namespace WixToolset.Core.ExtensibilityServices
9
10 internal class WindowsInstallerBackendHelper : IWindowsInstallerBackendHelper
11 {
13 - public WindowsInstallerBackendHelper(IServiceProvider serviceProvider)
14 - {
15 - this.ServiceProvider = serviceProvider;
16 - }
12 + public bool TryAddTupleToOutputMatchingTableDefinitions(IntermediateTuple tuple, Output output, TableDefinition[] tableDefinitions) => this.TryAddTupleToOutputMatchingTableDefinitions(tuple, output, tableDefinitions, false);
13
18 - private IServiceProvider ServiceProvider { get; }
19 -
20 - public bool TryAddTupleToOutputMatchingTableDefinitions(IntermediateTuple tuple, Output output, TableDefinition[] tableDefinitions)
14 + public bool TryAddTupleToOutputMatchingTableDefinitions(IntermediateTuple tuple, Output output, TableDefinition[] tableDefinitions, bool columnZeroIsId)
15 {
16 var tableDefinition = tableDefinitions.FirstOrDefault(t => t.Name == tuple.Definition.Name);
17
@@ -28,6 +22,14 @@ namespace WixToolset.Core.ExtensibilityServices
22
23 var table = output.EnsureTable(tableDefinition);
24 var row = table.CreateRow(tuple.SourceLineNumbers);
25 + var rowOffset = 0;
26 +
27 + if (columnZeroIsId)
28 + {
29 + row[0] = tuple.Id.Id;
30 + rowOffset = 1;
31 + }
32 +
33 for (var i = 0; i < tuple.Fields.Length; ++i)
34 {
35 if (i < tableDefinition.Columns.Length)
@@ -36,13 +38,13 @@ namespace WixToolset.Core.ExtensibilityServices
38
39 switch (column.Type)
40 {
39 - case ColumnType.Number:
40 - row[i] = tuple.AsNumber(i);
41 - break;
41 + case ColumnType.Number:
42 + row[i + rowOffset] = column.Nullable ? tuple.AsNullableNumber(i) : tuple.AsNumber(i);
43 + break;
44
43 - default:
44 - row[i] = tuple.AsString(i);
45 - break;
45 + default:
46 + row[i + rowOffset] = tuple.AsString(i);
47 + break;
48 }
49 }
50 }
src/WixToolset.Core/Link/WixGroupingOrdering.cs
+15 -16
@@ -12,19 +12,19 @@ namespace WixToolset.Core.Link
12 using WixToolset.Data;
13 using WixToolset.Data.Tuples;
14 using WixToolset.Extensibility.Services;
15 + using WixToolset.Data.Burn;
16
17 /// <summary>
18 /// Grouping and Ordering class of the WiX toolset.
19 /// </summary>
20 internal class WixGroupingOrdering
21 {
21 - private readonly IMessaging messageHandler;
22 + private readonly IMessaging Messaging;
23 private List<string> groupTypes;
24 private List<string> itemTypes;
25 private ItemCollection items;
26 private readonly List<int> rowsUsed;
27 private bool loaded;
27 - private bool encounteredError;
28
29 /// <summary>
30 /// Creates a WixGroupingOrdering object.
@@ -36,11 +36,10 @@ namespace WixToolset.Core.Link
36 public WixGroupingOrdering(IntermediateSection entrySections, IMessaging messageHandler)
37 {
38 this.EntrySection = entrySections;
39 - this.messageHandler = messageHandler;
39 + this.Messaging = messageHandler;
40
41 this.rowsUsed = new List<int>();
42 this.loaded = false;
43 - this.encounteredError = false;
43 }
44
45 private IntermediateSection EntrySection { get; }
@@ -71,7 +70,7 @@ namespace WixToolset.Core.Link
70 Debug.Assert(this.groupTypes.Contains(parentTypeString));
71
72 this.CreateOrderedList(parentTypeString, parentId, out var orderedItems);
74 - if (this.encounteredError)
73 + if (this.Messaging.EncounteredError)
74 {
75 return;
76 }
@@ -95,7 +94,7 @@ namespace WixToolset.Core.Link
94 Debug.Assert(this.groupTypes.Contains(parentTypeString));
95
96 this.LoadFlattenOrderGroups();
98 - if (this.encounteredError)
97 + if (this.Messaging.EncounteredError)
98 {
99 return;
100 }
@@ -127,14 +126,14 @@ namespace WixToolset.Core.Link
126 orderedItems = null;
127
128 this.LoadFlattenOrderGroups();
130 - if (this.encounteredError)
129 + if (this.Messaging.EncounteredError)
130 {
131 return;
132 }
133
134 if (!this.items.TryGetValue(parentType, parentId, out var parentItem))
135 {
137 - this.messageHandler.Write(ErrorMessages.IdentifierNotFound(parentType, parentId));
136 + this.Messaging.Write(ErrorMessages.IdentifierNotFound(parentType, parentId));
137 return;
138 }
139
@@ -216,7 +215,7 @@ namespace WixToolset.Core.Link
215 // dependencies. Group references, however, we can check directly.
216 this.FindCircularGroupReferences();
217
219 - if (!this.encounteredError)
218 + if (!this.Messaging.EncounteredError)
219 {
220 this.FlattenGroups();
221 this.FlattenOrdering();
@@ -304,7 +303,7 @@ namespace WixToolset.Core.Link
303 if (this.FindCircularGroupReference(item, item, itemsSeen, out circularReference))
304 {
305 itemsInKnownLoops.Add(itemsSeen);
307 - this.messageHandler.Write(ErrorMessages.ReferenceLoopDetected(item.Row.SourceLineNumbers, circularReference));
306 + this.Messaging.Write(ErrorMessages.ReferenceLoopDetected(item.Row.SourceLineNumbers, circularReference));
307 }
308 }
309 }
@@ -376,12 +375,12 @@ namespace WixToolset.Core.Link
375
376 if (!this.items.TryGetValue(rowItemType, rowItemName, out var item))
377 {
379 - this.messageHandler.Write(ErrorMessages.IdentifierNotFound(rowItemType, rowItemName));
378 + this.Messaging.Write(ErrorMessages.IdentifierNotFound(rowItemType, rowItemName));
379 }
380
381 if (!this.items.TryGetValue(rowDependsOnType, rowDependsOnName, out var dependsOn))
382 {
384 - this.messageHandler.Write(ErrorMessages.IdentifierNotFound(rowDependsOnType, rowDependsOnName));
383 + this.Messaging.Write(ErrorMessages.IdentifierNotFound(rowDependsOnType, rowDependsOnName));
384 }
385
386 if (null == item || null == dependsOn)
@@ -389,7 +388,7 @@ namespace WixToolset.Core.Link
388 continue;
389 }
390
392 - item.AddAfter(dependsOn, this.messageHandler);
391 + item.AddAfter(dependsOn, this.Messaging);
392 }
393 }
394
@@ -404,12 +403,12 @@ namespace WixToolset.Core.Link
403 // ordering.
404 foreach (Item item in this.items)
405 {
407 - item.PropagateAfterToChildItems(this.messageHandler);
406 + item.PropagateAfterToChildItems(this.Messaging);
407 }
408
409 foreach (Item item in this.items)
410 {
412 - item.FlattenAfters(this.messageHandler);
411 + item.FlattenAfters(this.Messaging);
412 }
413 }
414
@@ -668,7 +667,7 @@ namespace WixToolset.Core.Link
667 {
668 if (String.Equals(nameof(ComplexReferenceChildType.Package), this.Type, StringComparison.Ordinal) ||
669 (String.Equals(nameof(ComplexReferenceParentType.Container), this.Type, StringComparison.Ordinal) &&
671 - !String.Equals(Compiler.BurnUXContainerId.Id, this.Id, StringComparison.Ordinal)))
670 + !String.Equals(BurnConstants.BurnUXContainerName, this.Id, StringComparison.Ordinal)))
671 {
672 return false;
673 }
src/WixToolset.Core/WixToolsetServiceProvider.cs
+3 -1
@@ -24,7 +24,8 @@ namespace WixToolset.Core
24 this.AddService((provider, singletons) => AddSingleton<IParseHelper>(singletons, new ParseHelper(provider)));
25 this.AddService((provider, singletons) => AddSingleton<IPreprocessHelper>(singletons, new PreprocessHelper(provider)));
26 this.AddService((provider, singletons) => AddSingleton<IBackendHelper>(singletons, new BackendHelper(provider)));
27 - this.AddService((provider, singletons) => AddSingleton<IWindowsInstallerBackendHelper>(singletons, new WindowsInstallerBackendHelper(provider)));
27 + this.AddService((provider, singletons) => AddSingleton<IPathResolver>(singletons, new PathResolver()));
28 + this.AddService((provider, singletons) => AddSingleton<IWindowsInstallerBackendHelper>(singletons, new WindowsInstallerBackendHelper()));
29
30 // Transients.
31 this.AddService<ICommandLineArguments>((provider, singletons) => new CommandLineArguments(provider));
@@ -47,6 +48,7 @@ namespace WixToolset.Core
48 this.AddService<IDecompileResult>((provider, singletons) => new DecompileResult());
49 this.AddService<IIncludedFile>((provider, singletons) => new IncludedFile());
50 this.AddService<IPreprocessResult>((provider, singletons) => new PreprocessResult());
51 + this.AddService<IResolvedDirectory>((provider, singletons) => new ResolvedDirectory());
52 this.AddService<IResolveFileResult>((provider, singletons) => new ResolveFileResult());
53 this.AddService<IResolveResult>((provider, singletons) => new ResolveResult());
54 this.AddService<IResolvedCabinet>((provider, singletons) => new ResolvedCabinet());
src/test/WixToolsetTest.CoreIntegration/BundleFixture.cs new
+52
@@ -0,0 +1,52 @@
1 +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 +
3 +namespace WixToolsetTest.CoreIntegration
4 +{
5 + using System.IO;
6 + using System.Linq;
7 + using WixBuildTools.TestSupport;
8 + using WixToolset.Core.TestPackage;
9 + using WixToolset.Data;
10 + using WixToolset.Data.Tuples;
11 + using Xunit;
12 +
13 + public class BundleFixture
14 + {
15 + [Fact]
16 + public void CanBuildSimpleBundle()
17 + {
18 + var burnStubPath = TestData.Get(@"TestData\.Data\burn.exe");
19 + var folder = TestData.Get(@"TestData\SimpleBundle");
20 +
21 + using (var fs = new DisposableFileSystem())
22 + {
23 + var baseFolder = fs.GetFolder();
24 + var intermediateFolder = Path.Combine(baseFolder, "obj");
25 +
26 + var result = WixRunner.Execute(new[]
27 + {
28 + "build",
29 + Path.Combine(folder, "Bundle.wxs"),
30 + "-loc", Path.Combine(folder, "Bundle.en-us.wxl"),
31 + "-bindpath", Path.Combine(folder, "data"),
32 + "-intermediateFolder", intermediateFolder,
33 + "-burnStub", burnStubPath,
34 + "-o", Path.Combine(baseFolder, @"bin\test.exe")
35 + });
36 +
37 + result.AssertSuccess();
38 +
39 + Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\test.exe")));
40 +#if TODO
41 + Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\test.wixpdb")));
42 +#endif
43 +
44 + var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"test.wir"));
45 + var section = intermediate.Sections.Single();
46 +
47 + var msiTuple = section.Tuples.OfType<WixBundlePackageTuple>().Single();
48 + Assert.Equal("test.msi", msiTuple.Id.Id );
49 + }
50 + }
51 + }
52 +}
src/test/WixToolsetTest.CoreIntegration/TestData/.Data/burn.exe
Binary files /dev/null and b/src/test/WixToolsetTest.CoreIntegration/TestData/.Data/burn.exe differ
src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/Bundle.en-us.wxl new
+10
@@ -0,0 +1,10 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +
3 +<!--
4 +This file contains the declaration of all the localizable strings.
5 +-->
6 +<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
7 +
8 + <String Id="BundleName">~TestBundle</String>
9 +
10 +</WixLocalization>
src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/Bundle.wxs new
+11
@@ -0,0 +1,11 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3 + <Bundle Name="!(loc.BundleName)" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
4 + <BootstrapperApplication SourceFile="fakeba.dll" />
5 + <Chain>
6 + <MsiPackage SourceFile="test.msi">
7 + <MsiProperty Name="TEST" Value="1" />
8 + </MsiPackage>
9 + </Chain>
10 + </Bundle>
11 +</Wix>
src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/MsiPackage/Shared.dll new
+1
@@ -0,0 +1 @@
1 +This is Shared.dll.
\ No newline at end of file
src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/MsiPackage/test.txt new
+1
@@ -0,0 +1 @@
1 +This is test.txt
\ No newline at end of file
src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/fakeba.dll new
+1
@@ -0,0 +1 @@
1 +This is a fakeba.dll
\ No newline at end of file
src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/test.msi
Binary files /dev/null and b/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/test.msi differ
src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj
+7
@@ -13,6 +13,7 @@
13 </PropertyGroup>
14
15 <ItemGroup>
16 + <Content Include="TestData\.Data\burn.exe" CopyToOutputDirectory="PreserveNewest" />
17 <Content Include="TestData\AppSearch\ComponentSearch.wxs" CopyToOutputDirectory="PreserveNewest" />
18 <Content Include="TestData\AppSearch\DirectorySearch.wxs" CopyToOutputDirectory="PreserveNewest" />
19 <Content Include="TestData\AppSearch\FileSearch.wxs" CopyToOutputDirectory="PreserveNewest" />
@@ -31,6 +32,9 @@
32 <Content Include="TestData\ReserveCost\ReserveCost.wxs" CopyToOutputDirectory="PreserveNewest" />
33 <Content Include="TestData\ServiceInstall\OwnProcess.wxs" CopyToOutputDirectory="PreserveNewest" />
34 <Content Include="TestData\Shortcut\ShortcutProperty.wxs" CopyToOutputDirectory="PreserveNewest" />
35 + <Content Include="TestData\SimpleBundle\data\fakeba.dll" CopyToOutputDirectory="PreserveNewest" />
36 + <Content Include="TestData\SimpleBundle\data\MsiPackage\Shared.dll" CopyToOutputDirectory="PreserveNewest" />
37 + <Content Include="TestData\SimpleBundle\data\MsiPackage\test.txt" CopyToOutputDirectory="PreserveNewest" />
38 <Content Include="TestData\SimpleModule\data\test.txt" CopyToOutputDirectory="PreserveNewest" />
39 <Content Include="TestData\SimpleModule\Module.en-us.wxl" CopyToOutputDirectory="PreserveNewest" />
40 <Content Include="TestData\SimpleModule\Module.wxs" CopyToOutputDirectory="PreserveNewest" />
@@ -66,6 +70,9 @@
70 <Content Include="TestData\SetProperty\Package.en-us.wxl" CopyToOutputDirectory="PreserveNewest" />
71 <Content Include="TestData\SetProperty\Package.wxs" CopyToOutputDirectory="PreserveNewest" />
72 <Content Include="TestData\SetProperty\PackageComponents.wxs" CopyToOutputDirectory="PreserveNewest" />
73 + <Content Include="TestData\SimpleBundle\data\test.msi" CopyToOutputDirectory="PreserveNewest" />
74 + <Content Include="TestData\SimpleBundle\Bundle.en-us.wxl" CopyToOutputDirectory="PreserveNewest" />
75 + <Content Include="TestData\SimpleBundle\Bundle.wxs" CopyToOutputDirectory="PreserveNewest" />
76 <Content Include="TestData\SingleFile\data\test.txt" CopyToOutputDirectory="PreserveNewest" />
77 <Content Include="TestData\SingleFile\Package.en-us.wxl" CopyToOutputDirectory="PreserveNewest" />
78 <Content Include="TestData\SingleFile\Package.wxs" CopyToOutputDirectory="PreserveNewest" />