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