diff options
author | Rob Mensching <rob@firegiant.com> | 2020-01-24 15:27:20 -0800 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2020-02-05 16:15:47 -0800 |
commit | 6ff680e386b1543ad1a58d1b1d465ce8aa20bc7d (patch) | |
tree | c717333cd10d5592e59dfb898b391275bba1f389 /src | |
parent | 6e2e67ab55c75f4655397588c0dcc64f50d22f92 (diff) | |
download | wix-6ff680e386b1543ad1a58d1b1d465ce8aa20bc7d.tar.gz wix-6ff680e386b1543ad1a58d1b1d465ce8aa20bc7d.tar.bz2 wix-6ff680e386b1543ad1a58d1b1d465ce8aa20bc7d.zip |
Start on new patch infrastructure
Diffstat (limited to 'src')
58 files changed, 4481 insertions, 1192 deletions
diff --git a/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs b/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs index c2164744..283cd115 100644 --- a/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs +++ b/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs | |||
@@ -208,7 +208,6 @@ namespace WixToolset.Core.Burn | |||
208 | variableCache.Add(String.Concat("packageManufacturer.", facade.PackageId), msiPackage.Manufacturer); | 208 | variableCache.Add(String.Concat("packageManufacturer.", facade.PackageId), msiPackage.Manufacturer); |
209 | } | 209 | } |
210 | } | 210 | } |
211 | |||
212 | } | 211 | } |
213 | break; | 212 | break; |
214 | 213 | ||
diff --git a/src/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs b/src/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs index 612e0e11..34e601a7 100644 --- a/src/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs +++ b/src/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs | |||
@@ -113,7 +113,7 @@ namespace WixToolset.Core.Burn.Bundles | |||
113 | ++attachedContainerIndex; | 113 | ++attachedContainerIndex; |
114 | } | 114 | } |
115 | 115 | ||
116 | this.CreateContainer(container, containerPayloads, null); | 116 | this.CreateContainer(container, containerPayloads); |
117 | } | 117 | } |
118 | } | 118 | } |
119 | 119 | ||
@@ -122,7 +122,7 @@ namespace WixToolset.Core.Burn.Bundles | |||
122 | this.FileTransfers = fileTransfers; | 122 | this.FileTransfers = fileTransfers; |
123 | } | 123 | } |
124 | 124 | ||
125 | private void CreateContainer(WixBundleContainerTuple container, IEnumerable<WixBundlePayloadTuple> containerPayloads, string manifestFile) | 125 | private void CreateContainer(WixBundleContainerTuple container, IEnumerable<WixBundlePayloadTuple> containerPayloads) |
126 | { | 126 | { |
127 | var command = new CreateContainerCommand(containerPayloads, container.WorkingPath, this.DefaultCompressionLevel); | 127 | var command = new CreateContainerCommand(containerPayloads, container.WorkingPath, this.DefaultCompressionLevel); |
128 | command.Execute(); | 128 | command.Execute(); |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs index 2199bbde..2bfd587f 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs | |||
@@ -57,7 +57,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
57 | 57 | ||
58 | var mediaRows = new Dictionary<int, MediaTuple>(); | 58 | var mediaRows = new Dictionary<int, MediaTuple>(); |
59 | 59 | ||
60 | List<FileFacade> uncompressedFiles = new List<FileFacade>(); | 60 | var uncompressedFiles = new List<FileFacade>(); |
61 | 61 | ||
62 | var mediaTable = this.Section.Tuples.OfType<MediaTuple>().ToList(); | 62 | var mediaTable = this.Section.Tuples.OfType<MediaTuple>().ToList(); |
63 | var mediaTemplateTable = this.Section.Tuples.OfType<WixMediaTemplateTuple>().ToList(); | 63 | var mediaTemplateTable = this.Section.Tuples.OfType<WixMediaTemplateTuple>().ToList(); |
@@ -109,8 +109,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
109 | 109 | ||
110 | ulong currentPreCabSize = 0; | 110 | ulong currentPreCabSize = 0; |
111 | ulong maxPreCabSizeInBytes; | 111 | ulong maxPreCabSizeInBytes; |
112 | int maxPreCabSizeInMB = 0; | 112 | var maxPreCabSizeInMB = 0; |
113 | int currentCabIndex = 0; | 113 | var currentCabIndex = 0; |
114 | 114 | ||
115 | MediaTuple currentMediaRow = null; | 115 | MediaTuple currentMediaRow = null; |
116 | 116 | ||
@@ -131,7 +131,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
131 | this.CabinetNameTemplate = mediaTemplateRow.CabinetTemplate; | 131 | this.CabinetNameTemplate = mediaTemplateRow.CabinetTemplate; |
132 | } | 132 | } |
133 | 133 | ||
134 | string mumsString = Environment.GetEnvironmentVariable("WIX_MUMS"); | 134 | var mumsString = Environment.GetEnvironmentVariable("WIX_MUMS"); |
135 | 135 | ||
136 | try | 136 | try |
137 | { | 137 | { |
@@ -170,13 +170,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
170 | { | 170 | { |
171 | // Associate current file with last cab (irrespective of the size) and cab index is not incremented anymore. | 171 | // Associate current file with last cab (irrespective of the size) and cab index is not incremented anymore. |
172 | var cabinetFiles = filesByCabinetMedia[currentMediaRow]; | 172 | var cabinetFiles = filesByCabinetMedia[currentMediaRow]; |
173 | facade.File.DiskId = currentCabIndex; | 173 | facade.DiskId = currentCabIndex; |
174 | cabinetFiles.Add(facade); | 174 | cabinetFiles.Add(facade); |
175 | continue; | 175 | continue; |
176 | } | 176 | } |
177 | 177 | ||
178 | // Update current cab size. | 178 | // Update current cab size. |
179 | currentPreCabSize += (ulong)facade.File.FileSize; | 179 | currentPreCabSize += (ulong)facade.FileSize; |
180 | 180 | ||
181 | if (currentPreCabSize > maxPreCabSizeInBytes) | 181 | if (currentPreCabSize > maxPreCabSizeInBytes) |
182 | { | 182 | { |
@@ -186,10 +186,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
186 | filesByCabinetMedia.Add(currentMediaRow, new List<FileFacade>()); | 186 | filesByCabinetMedia.Add(currentMediaRow, new List<FileFacade>()); |
187 | 187 | ||
188 | var cabinetFileRows = filesByCabinetMedia[currentMediaRow]; | 188 | var cabinetFileRows = filesByCabinetMedia[currentMediaRow]; |
189 | facade.File.DiskId = currentCabIndex; | 189 | facade.DiskId = currentCabIndex; |
190 | cabinetFileRows.Add(facade); | 190 | cabinetFileRows.Add(facade); |
191 | // Now files larger than MaxUncompressedMediaSize will be the only file in its cabinet so as to respect MaxUncompressedMediaSize | 191 | // Now files larger than MaxUncompressedMediaSize will be the only file in its cabinet so as to respect MaxUncompressedMediaSize |
192 | currentPreCabSize = (ulong)facade.File.FileSize; | 192 | currentPreCabSize = (ulong)facade.FileSize; |
193 | } | 193 | } |
194 | else | 194 | else |
195 | { | 195 | { |
@@ -204,7 +204,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
204 | 204 | ||
205 | // Associate current file with current cab. | 205 | // Associate current file with current cab. |
206 | var cabinetFiles = filesByCabinetMedia[currentMediaRow]; | 206 | var cabinetFiles = filesByCabinetMedia[currentMediaRow]; |
207 | facade.File.DiskId = currentCabIndex; | 207 | facade.DiskId = currentCabIndex; |
208 | cabinetFiles.Add(facade); | 208 | cabinetFiles.Add(facade); |
209 | } | 209 | } |
210 | } | 210 | } |
@@ -260,18 +260,18 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
260 | } | 260 | } |
261 | } | 261 | } |
262 | 262 | ||
263 | foreach (FileFacade facade in fileFacades) | 263 | foreach (var facade in fileFacades) |
264 | { | 264 | { |
265 | if (!mediaRows.TryGetValue(facade.DiskId, out var mediaRow)) | 265 | if (!mediaRows.TryGetValue(facade.DiskId, out var mediaRow)) |
266 | { | 266 | { |
267 | this.Messaging.Write(ErrorMessages.MissingMedia(facade.File.SourceLineNumbers, facade.DiskId)); | 267 | this.Messaging.Write(ErrorMessages.MissingMedia(facade.SourceLineNumber, facade.DiskId)); |
268 | continue; | 268 | continue; |
269 | } | 269 | } |
270 | 270 | ||
271 | // When building a product, if the current file is to be uncompressed or if | 271 | // When building a product, if the current file is to be uncompressed or if |
272 | // the package set not to be compressed, don't cab it. | 272 | // the package set not to be compressed, don't cab it. |
273 | var compressed = (facade.File.Attributes & FileTupleAttributes.Compressed) == FileTupleAttributes.Compressed; | 273 | var compressed = facade.Compressed; |
274 | var uncompressed = (facade.File.Attributes & FileTupleAttributes.Uncompressed) == FileTupleAttributes.Uncompressed; | 274 | var uncompressed = facade.Uncompressed; |
275 | if (SectionType.Product == this.Section.Type && (uncompressed || (!compressed && !this.FilesCompressed))) | 275 | if (SectionType.Product == this.Section.Type && (uncompressed || (!compressed && !this.FilesCompressed))) |
276 | { | 276 | { |
277 | uncompressedFiles.Add(facade); | 277 | uncompressedFiles.Add(facade); |
@@ -284,7 +284,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
284 | } | 284 | } |
285 | else | 285 | else |
286 | { | 286 | { |
287 | this.Messaging.Write(ErrorMessages.ExpectedMediaCabinet(facade.File.SourceLineNumbers, facade.File.Id.Id, facade.DiskId)); | 287 | this.Messaging.Write(ErrorMessages.ExpectedMediaCabinet(facade.SourceLineNumber, facade.Id, facade.DiskId)); |
288 | } | 288 | } |
289 | } | 289 | } |
290 | } | 290 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs new file mode 100644 index 00000000..aa5ca20a --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs | |||
@@ -0,0 +1,1322 @@ | |||
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 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Globalization; | ||
8 | using System.Linq; | ||
9 | using System.Text.RegularExpressions; | ||
10 | using WixToolset.Core.WindowsInstaller; | ||
11 | using WixToolset.Core.WindowsInstaller.Msi; | ||
12 | using WixToolset.Data; | ||
13 | using WixToolset.Data.Tuples; | ||
14 | using WixToolset.Data.WindowsInstaller; | ||
15 | using WixToolset.Data.WindowsInstaller.Rows; | ||
16 | using WixToolset.Extensibility.Services; | ||
17 | |||
18 | /// <summary> | ||
19 | /// Include transforms in a patch. | ||
20 | /// </summary> | ||
21 | internal class AttachPatchTransformsCommand | ||
22 | { | ||
23 | private static readonly string[] PatchUninstallBreakingTables = new[] | ||
24 | { | ||
25 | "AppId", | ||
26 | "BindImage", | ||
27 | "Class", | ||
28 | "Complus", | ||
29 | "CreateFolder", | ||
30 | "DuplicateFile", | ||
31 | "Environment", | ||
32 | "Extension", | ||
33 | "Font", | ||
34 | "IniFile", | ||
35 | "IsolatedComponent", | ||
36 | "LockPermissions", | ||
37 | "MIME", | ||
38 | "MoveFile", | ||
39 | "MsiLockPermissionsEx", | ||
40 | "MsiServiceConfig", | ||
41 | "MsiServiceConfigFailureActions", | ||
42 | "ODBCAttribute", | ||
43 | "ODBCDataSource", | ||
44 | "ODBCDriver", | ||
45 | "ODBCSourceAttribute", | ||
46 | "ODBCTranslator", | ||
47 | "ProgId", | ||
48 | "PublishComponent", | ||
49 | "RemoveIniFile", | ||
50 | "SelfReg", | ||
51 | "ServiceControl", | ||
52 | "ServiceInstall", | ||
53 | "TypeLib", | ||
54 | "Verb", | ||
55 | }; | ||
56 | |||
57 | private readonly TableDefinitionCollection tableDefinitions; | ||
58 | |||
59 | public AttachPatchTransformsCommand(IMessaging messaging, Intermediate intermediate, IEnumerable<PatchTransform> transforms) | ||
60 | { | ||
61 | this.tableDefinitions = new TableDefinitionCollection(WindowsInstallerStandardInternal.GetTableDefinitions()); | ||
62 | this.Messaging = messaging; | ||
63 | this.Intermediate = intermediate; | ||
64 | this.Transforms = transforms; | ||
65 | } | ||
66 | |||
67 | private IMessaging Messaging { get; } | ||
68 | |||
69 | private Intermediate Intermediate { get; } | ||
70 | |||
71 | private IEnumerable<PatchTransform> Transforms { get; } | ||
72 | |||
73 | public IEnumerable<SubStorage> SubStorages { get; private set; } | ||
74 | |||
75 | public IEnumerable<SubStorage> Execute() | ||
76 | { | ||
77 | var subStorages = new List<SubStorage>(); | ||
78 | |||
79 | if (this.Transforms == null || !this.Transforms.Any()) | ||
80 | { | ||
81 | this.Messaging.Write(ErrorMessages.PatchWithoutTransforms()); | ||
82 | return subStorages; | ||
83 | } | ||
84 | |||
85 | var summaryInfo = this.ExtractPatchSummaryInfo(); | ||
86 | |||
87 | var section = this.Intermediate.Sections.First(); | ||
88 | |||
89 | var tuples = this.Intermediate.Sections.SelectMany(s => s.Tuples).ToList(); | ||
90 | |||
91 | // Get the patch id from the WixPatchId tuple. | ||
92 | var patchIdTuple = tuples.OfType<WixPatchIdTuple>().FirstOrDefault(); | ||
93 | |||
94 | if (String.IsNullOrEmpty(patchIdTuple.Id?.Id)) | ||
95 | { | ||
96 | this.Messaging.Write(ErrorMessages.ExpectedPatchIdInWixMsp()); | ||
97 | return subStorages; | ||
98 | } | ||
99 | |||
100 | if (String.IsNullOrEmpty(patchIdTuple.ClientPatchId)) | ||
101 | { | ||
102 | this.Messaging.Write(ErrorMessages.ExpectedClientPatchIdInWixMsp()); | ||
103 | return subStorages; | ||
104 | } | ||
105 | |||
106 | // enumerate patch.Media to map diskId to Media row | ||
107 | var patchMediaByDiskId = tuples.OfType<MediaTuple>().ToDictionary(t => t.DiskId); | ||
108 | |||
109 | if (patchMediaByDiskId.Count == 0) | ||
110 | { | ||
111 | this.Messaging.Write(ErrorMessages.ExpectedMediaRowsInWixMsp()); | ||
112 | return subStorages; | ||
113 | } | ||
114 | |||
115 | // populate MSP summary information | ||
116 | var patchMetadata = this.PopulateSummaryInformation(summaryInfo, tuples, patchIdTuple, section.Codepage); | ||
117 | |||
118 | // enumerate transforms | ||
119 | var productCodes = new SortedSet<string>(); | ||
120 | var transformNames = new List<string>(); | ||
121 | var validTransform = new List<Tuple<string, WindowsInstallerData>>(); | ||
122 | |||
123 | var baselineTuplesById = tuples.OfType<WixPatchBaselineTuple>().ToDictionary(t => t.Id.Id); | ||
124 | |||
125 | foreach (var mainTransform in this.Transforms) | ||
126 | { | ||
127 | var baselineTuple = baselineTuplesById[mainTransform.Baseline]; | ||
128 | |||
129 | var patchRefTuples = tuples.OfType<WixPatchRefTuple>().ToList(); | ||
130 | if (patchRefTuples.Count > 0) | ||
131 | { | ||
132 | if (!this.ReduceTransform(mainTransform.Transform, patchRefTuples)) | ||
133 | { | ||
134 | // transform has none of the content authored into this patch | ||
135 | continue; | ||
136 | } | ||
137 | } | ||
138 | |||
139 | // Validate the transform doesn't break any patch specific rules. | ||
140 | this.Validate(mainTransform); | ||
141 | |||
142 | // ensure consistent File.Sequence within each Media | ||
143 | var mediaTuple = patchMediaByDiskId[baselineTuple.DiskId]; | ||
144 | |||
145 | // Ensure that files are sequenced after the last file in any transform. | ||
146 | var transformMediaTable = mainTransform.Transform.Tables["Media"]; | ||
147 | if (null != transformMediaTable && 0 < transformMediaTable.Rows.Count) | ||
148 | { | ||
149 | foreach (MediaRow transformMediaRow in transformMediaTable.Rows) | ||
150 | { | ||
151 | if (mediaTuple.LastSequence < transformMediaRow.LastSequence) | ||
152 | { | ||
153 | // The Binder will pre-increment the sequence. | ||
154 | mediaTuple.LastSequence = transformMediaRow.LastSequence; | ||
155 | } | ||
156 | } | ||
157 | } | ||
158 | |||
159 | // Use the Media/@DiskId if greater than the last sequence for backward compatibility. | ||
160 | if (mediaTuple.LastSequence < mediaTuple.DiskId) | ||
161 | { | ||
162 | mediaTuple.LastSequence = mediaTuple.DiskId; | ||
163 | } | ||
164 | |||
165 | // Ignore media table in the transform. | ||
166 | mainTransform.Transform.Tables.Remove("Media"); | ||
167 | mainTransform.Transform.Tables.Remove("WixMedia"); | ||
168 | mainTransform.Transform.Tables.Remove("MsiDigitalSignature"); | ||
169 | |||
170 | var pairedTransform = this.BuildPairedTransform(summaryInfo, patchMetadata, patchIdTuple, mainTransform.Transform, mediaTuple, baselineTuple, out var productCode); | ||
171 | |||
172 | productCode = productCode.ToUpperInvariant(); | ||
173 | productCodes.Add(productCode); | ||
174 | validTransform.Add(Tuple.Create(productCode, mainTransform.Transform)); | ||
175 | |||
176 | // attach these transforms to the patch object | ||
177 | // TODO: is this an acceptable way to auto-generate transform stream names? | ||
178 | var transformName = mainTransform.Baseline + "." + validTransform.Count.ToString(CultureInfo.InvariantCulture); | ||
179 | subStorages.Add(new SubStorage(transformName, mainTransform.Transform)); | ||
180 | subStorages.Add(new SubStorage("#" + transformName, pairedTransform)); | ||
181 | |||
182 | transformNames.Add(":" + transformName); | ||
183 | transformNames.Add(":#" + transformName); | ||
184 | } | ||
185 | |||
186 | if (validTransform.Count == 0) | ||
187 | { | ||
188 | this.Messaging.Write(ErrorMessages.PatchWithoutValidTransforms()); | ||
189 | return subStorages; | ||
190 | } | ||
191 | |||
192 | // Validate that a patch authored as removable is actually removable | ||
193 | if (patchMetadata.TryGetValue("AllowRemoval", out var allowRemoval) && allowRemoval.Value == "1") | ||
194 | { | ||
195 | var uninstallable = true; | ||
196 | |||
197 | foreach (var entry in validTransform) | ||
198 | { | ||
199 | uninstallable &= this.CheckUninstallableTransform(entry.Item1, entry.Item2); | ||
200 | } | ||
201 | |||
202 | if (!uninstallable) | ||
203 | { | ||
204 | this.Messaging.Write(ErrorMessages.PatchNotRemovable()); | ||
205 | return subStorages; | ||
206 | } | ||
207 | } | ||
208 | |||
209 | // Finish filling tables with transform-dependent data. | ||
210 | productCodes = FinalizePatchProductCodes(tuples, productCodes); | ||
211 | |||
212 | // Semicolon delimited list of the product codes that can accept the patch. | ||
213 | summaryInfo.Add(SumaryInformationType.PatchProductCodes, new SummaryInformationTuple(patchIdTuple.SourceLineNumbers) | ||
214 | { | ||
215 | PropertyId = SumaryInformationType.PatchProductCodes, | ||
216 | Value = String.Join(";", productCodes) | ||
217 | }); | ||
218 | |||
219 | // Semicolon delimited list of transform substorage names in the order they are applied. | ||
220 | summaryInfo.Add(SumaryInformationType.TransformNames, new SummaryInformationTuple(patchIdTuple.SourceLineNumbers) | ||
221 | { | ||
222 | PropertyId = SumaryInformationType.TransformNames, | ||
223 | Value = String.Join(";", transformNames) | ||
224 | }); | ||
225 | |||
226 | // Put the summary information that was extracted back in now that it is updated. | ||
227 | foreach (var readSummaryInfo in summaryInfo.Values.OrderBy(s => s.PropertyId)) | ||
228 | { | ||
229 | section.Tuples.Add(readSummaryInfo); | ||
230 | } | ||
231 | |||
232 | this.SubStorages = subStorages; | ||
233 | |||
234 | return subStorages; | ||
235 | } | ||
236 | |||
237 | private Dictionary<SumaryInformationType, SummaryInformationTuple> ExtractPatchSummaryInfo() | ||
238 | { | ||
239 | var result = new Dictionary<SumaryInformationType, SummaryInformationTuple>(); | ||
240 | |||
241 | foreach (var section in this.Intermediate.Sections) | ||
242 | { | ||
243 | for (var i = section.Tuples.Count - 1; i >= 0; i--) | ||
244 | { | ||
245 | if (section.Tuples[i] is SummaryInformationTuple patchSummaryInfo) | ||
246 | { | ||
247 | // Remove all summary information from the tuples and remember those that | ||
248 | // are not calculated or reserved. | ||
249 | section.Tuples.RemoveAt(i); | ||
250 | |||
251 | if (patchSummaryInfo.PropertyId != SumaryInformationType.PatchProductCodes && | ||
252 | patchSummaryInfo.PropertyId != SumaryInformationType.PatchCode && | ||
253 | patchSummaryInfo.PropertyId != SumaryInformationType.PatchInstallerRequirement && | ||
254 | patchSummaryInfo.PropertyId != SumaryInformationType.Reserved11 && | ||
255 | patchSummaryInfo.PropertyId != SumaryInformationType.Reserved14 && | ||
256 | patchSummaryInfo.PropertyId != SumaryInformationType.Reserved16) | ||
257 | { | ||
258 | result.Add(patchSummaryInfo.PropertyId, patchSummaryInfo); | ||
259 | } | ||
260 | } | ||
261 | } | ||
262 | } | ||
263 | |||
264 | return result; | ||
265 | } | ||
266 | |||
267 | private Dictionary<string, MsiPatchMetadataTuple> PopulateSummaryInformation(Dictionary<SumaryInformationType, SummaryInformationTuple> summaryInfo, List<IntermediateTuple> tuples, WixPatchIdTuple patchIdTuple, int codepage) | ||
268 | { | ||
269 | // PID_CODEPAGE | ||
270 | if (!summaryInfo.ContainsKey(SumaryInformationType.Codepage)) | ||
271 | { | ||
272 | // Set the code page by default to the same code page for the | ||
273 | // string pool in the database. | ||
274 | AddSummaryInformation(SumaryInformationType.Codepage, codepage.ToString(CultureInfo.InvariantCulture), patchIdTuple.SourceLineNumbers); | ||
275 | } | ||
276 | |||
277 | // GUID patch code for the patch. | ||
278 | AddSummaryInformation(SumaryInformationType.PatchCode, patchIdTuple.Id.Id, patchIdTuple.SourceLineNumbers); | ||
279 | |||
280 | // Indicates the minimum Windows Installer version that is required to install the patch. | ||
281 | AddSummaryInformation(SumaryInformationType.PatchInstallerRequirement, ((int)SummaryInformation.InstallerRequirement.Version31).ToString(CultureInfo.InvariantCulture), patchIdTuple.SourceLineNumbers); | ||
282 | |||
283 | if (!summaryInfo.ContainsKey(SumaryInformationType.Security)) | ||
284 | { | ||
285 | AddSummaryInformation(SumaryInformationType.Security, "4", patchIdTuple.SourceLineNumbers); // Read-only enforced; | ||
286 | } | ||
287 | |||
288 | // Use authored comments or default to display name. | ||
289 | MsiPatchMetadataTuple commentsTuple = null; | ||
290 | |||
291 | var metadataTuples = tuples.OfType<MsiPatchMetadataTuple>().Where(t => String.IsNullOrEmpty(t.Company)).ToDictionary(t => t.Property); | ||
292 | |||
293 | if (!summaryInfo.ContainsKey(SumaryInformationType.Title) && | ||
294 | metadataTuples.TryGetValue("DisplayName", out var displayName)) | ||
295 | { | ||
296 | AddSummaryInformation(SumaryInformationType.Title, displayName.Value, displayName.SourceLineNumbers); | ||
297 | |||
298 | // Default comments to use display name as-is. | ||
299 | commentsTuple = displayName; | ||
300 | } | ||
301 | |||
302 | // TODO: This code below seems unnecessary given the codepage is set at the top of this method. | ||
303 | //if (!summaryInfo.ContainsKey(SumaryInformationType.Codepage) && | ||
304 | // metadataValues.TryGetValue("CodePage", out var codepage)) | ||
305 | //{ | ||
306 | // AddSummaryInformation(SumaryInformationType.Codepage, codepage); | ||
307 | //} | ||
308 | |||
309 | if (!summaryInfo.ContainsKey(SumaryInformationType.PatchPackageName) && | ||
310 | metadataTuples.TryGetValue("Description", out var description)) | ||
311 | { | ||
312 | AddSummaryInformation(SumaryInformationType.PatchPackageName, description.Value, description.SourceLineNumbers); | ||
313 | } | ||
314 | |||
315 | if (!summaryInfo.ContainsKey(SumaryInformationType.Author) && | ||
316 | metadataTuples.TryGetValue("ManufacturerName", out var manufacturer)) | ||
317 | { | ||
318 | AddSummaryInformation(SumaryInformationType.Author, manufacturer.Value, manufacturer.SourceLineNumbers); | ||
319 | } | ||
320 | |||
321 | // Special metadata marshalled through the build. | ||
322 | //var wixMetadataValues = tuples.OfType<WixPatchMetadataTuple>().ToDictionary(t => t.Id.Id, t => t.Value); | ||
323 | |||
324 | //if (wixMetadataValues.TryGetValue("Comments", out var wixComments)) | ||
325 | if (metadataTuples.TryGetValue("Comments", out var wixComments)) | ||
326 | { | ||
327 | commentsTuple = wixComments; | ||
328 | } | ||
329 | |||
330 | // Write the package comments to summary info. | ||
331 | if (!summaryInfo.ContainsKey(SumaryInformationType.Comments) && | ||
332 | commentsTuple != null) | ||
333 | { | ||
334 | AddSummaryInformation(SumaryInformationType.Comments, commentsTuple.Value, commentsTuple.SourceLineNumbers); | ||
335 | } | ||
336 | |||
337 | return metadataTuples; | ||
338 | |||
339 | void AddSummaryInformation(SumaryInformationType type, string value, SourceLineNumber sourceLineNumber) | ||
340 | { | ||
341 | summaryInfo.Add(type, new SummaryInformationTuple(sourceLineNumber) | ||
342 | { | ||
343 | PropertyId = type, | ||
344 | Value = value | ||
345 | }); | ||
346 | } | ||
347 | } | ||
348 | |||
349 | /// <summary> | ||
350 | /// Ensure transform is uninstallable. | ||
351 | /// </summary> | ||
352 | /// <param name="productCode">Product code in transform.</param> | ||
353 | /// <param name="transform">Transform generated by torch.</param> | ||
354 | /// <returns>True if the transform is uninstallable</returns> | ||
355 | private bool CheckUninstallableTransform(string productCode, WindowsInstallerData transform) | ||
356 | { | ||
357 | var success = true; | ||
358 | |||
359 | foreach (var tableName in PatchUninstallBreakingTables) | ||
360 | { | ||
361 | if (transform.TryGetTable(tableName, out var table)) | ||
362 | { | ||
363 | foreach (var row in table.Rows) | ||
364 | { | ||
365 | if (row.Operation == RowOperation.Add) | ||
366 | { | ||
367 | success = false; | ||
368 | |||
369 | var primaryKey = row.GetPrimaryKey('/') ?? String.Empty; | ||
370 | |||
371 | this.Messaging.Write(ErrorMessages.NewRowAddedInTable(row.SourceLineNumbers, productCode, table.Name, primaryKey)); | ||
372 | } | ||
373 | } | ||
374 | } | ||
375 | } | ||
376 | |||
377 | return success; | ||
378 | } | ||
379 | |||
380 | /// <summary> | ||
381 | /// Reduce the transform according to the patch references. | ||
382 | /// </summary> | ||
383 | /// <param name="transform">transform generated by torch.</param> | ||
384 | /// <param name="patchRefTuples">Table contains patch family filter.</param> | ||
385 | /// <returns>true if the transform is not empty</returns> | ||
386 | private bool ReduceTransform(WindowsInstallerData transform, IEnumerable<WixPatchRefTuple> patchRefTuples) | ||
387 | { | ||
388 | // identify sections to keep | ||
389 | var oldSections = new Dictionary<string, Row>(); | ||
390 | var newSections = new Dictionary<string, Row>(); | ||
391 | var tableKeyRows = new Dictionary<string, Dictionary<string, Row>>(); | ||
392 | var sequenceList = new List<Table>(); | ||
393 | var componentFeatureAddsIndex = new Dictionary<string, List<string>>(); | ||
394 | var customActionTable = new Dictionary<string, Row>(); | ||
395 | var directoryTableAdds = new Dictionary<string, Row>(); | ||
396 | var featureTableAdds = new Dictionary<string, Row>(); | ||
397 | var keptComponents = new Dictionary<string, Row>(); | ||
398 | var keptDirectories = new Dictionary<string, Row>(); | ||
399 | var keptFeatures = new Dictionary<string, Row>(); | ||
400 | var keptLockPermissions = new HashSet<string>(); | ||
401 | var keptMsiLockPermissionExs = new HashSet<string>(); | ||
402 | |||
403 | var componentCreateFolderIndex = new Dictionary<string, List<string>>(); | ||
404 | var directoryLockPermissionsIndex = new Dictionary<string, List<Row>>(); | ||
405 | var directoryMsiLockPermissionsExIndex = new Dictionary<string, List<Row>>(); | ||
406 | |||
407 | foreach (var patchRefTuple in patchRefTuples) | ||
408 | { | ||
409 | var tableName = patchRefTuple.Table; | ||
410 | var key = patchRefTuple.PrimaryKeys; | ||
411 | |||
412 | // Short circuit filtering if all changes should be included. | ||
413 | if ("*" == tableName && "*" == key) | ||
414 | { | ||
415 | RemoveProductCodeFromTransform(transform); | ||
416 | return true; | ||
417 | } | ||
418 | |||
419 | if (!transform.Tables.TryGetTable(tableName, out var table)) | ||
420 | { | ||
421 | // Table not found. | ||
422 | continue; | ||
423 | } | ||
424 | |||
425 | // Index the table. | ||
426 | if (!tableKeyRows.TryGetValue(tableName, out var keyRows)) | ||
427 | { | ||
428 | keyRows = new Dictionary<string, Row>(); | ||
429 | tableKeyRows.Add(tableName, keyRows); | ||
430 | |||
431 | foreach (var newRow in table.Rows) | ||
432 | { | ||
433 | var primaryKey = newRow.GetPrimaryKey(); | ||
434 | keyRows.Add(primaryKey, newRow); | ||
435 | } | ||
436 | } | ||
437 | |||
438 | if (!keyRows.TryGetValue(key, out var row)) | ||
439 | { | ||
440 | // Row not found. | ||
441 | continue; | ||
442 | } | ||
443 | |||
444 | // Differ.sectionDelimiter | ||
445 | var sections = row.SectionId.Split('/'); | ||
446 | oldSections[sections[0]] = row; | ||
447 | newSections[sections[1]] = row; | ||
448 | } | ||
449 | |||
450 | // throw away sections not referenced | ||
451 | var keptRows = 0; | ||
452 | Table directoryTable = null; | ||
453 | Table featureTable = null; | ||
454 | Table lockPermissionsTable = null; | ||
455 | Table msiLockPermissionsTable = null; | ||
456 | |||
457 | foreach (var table in transform.Tables) | ||
458 | { | ||
459 | if ("_SummaryInformation" == table.Name) | ||
460 | { | ||
461 | continue; | ||
462 | } | ||
463 | |||
464 | if (table.Name == "AdminExecuteSequence" | ||
465 | || table.Name == "AdminUISequence" | ||
466 | || table.Name == "AdvtExecuteSequence" | ||
467 | || table.Name == "InstallUISequence" | ||
468 | || table.Name == "InstallExecuteSequence") | ||
469 | { | ||
470 | sequenceList.Add(table); | ||
471 | continue; | ||
472 | } | ||
473 | |||
474 | for (var i = 0; i < table.Rows.Count; i++) | ||
475 | { | ||
476 | var row = table.Rows[i]; | ||
477 | |||
478 | if (table.Name == "CreateFolder") | ||
479 | { | ||
480 | var createFolderComponentId = row.FieldAsString(1); | ||
481 | |||
482 | if (!componentCreateFolderIndex.TryGetValue(createFolderComponentId, out var directoryList)) | ||
483 | { | ||
484 | directoryList = new List<string>(); | ||
485 | componentCreateFolderIndex.Add(createFolderComponentId, directoryList); | ||
486 | } | ||
487 | |||
488 | directoryList.Add(row.FieldAsString(0)); | ||
489 | } | ||
490 | |||
491 | if (table.Name == "CustomAction") | ||
492 | { | ||
493 | customActionTable.Add(row.FieldAsString(0), row); | ||
494 | } | ||
495 | |||
496 | if (table.Name == "Directory") | ||
497 | { | ||
498 | directoryTable = table; | ||
499 | if (RowOperation.Add == row.Operation) | ||
500 | { | ||
501 | directoryTableAdds.Add(row.FieldAsString(0), row); | ||
502 | } | ||
503 | } | ||
504 | |||
505 | if (table.Name == "Feature") | ||
506 | { | ||
507 | featureTable = table; | ||
508 | if (RowOperation.Add == row.Operation) | ||
509 | { | ||
510 | featureTableAdds.Add(row.FieldAsString(0), row); | ||
511 | } | ||
512 | } | ||
513 | |||
514 | if (table.Name == "FeatureComponents") | ||
515 | { | ||
516 | if (RowOperation.Add == row.Operation) | ||
517 | { | ||
518 | var featureId = row.FieldAsString(0); | ||
519 | var componentId = row.FieldAsString(1); | ||
520 | |||
521 | if (!componentFeatureAddsIndex.TryGetValue(componentId, out var featureList)) | ||
522 | { | ||
523 | featureList = new List<string>(); | ||
524 | componentFeatureAddsIndex.Add(componentId, featureList); | ||
525 | } | ||
526 | |||
527 | featureList.Add(featureId); | ||
528 | } | ||
529 | } | ||
530 | |||
531 | if (table.Name == "LockPermissions") | ||
532 | { | ||
533 | lockPermissionsTable = table; | ||
534 | if ("CreateFolder" == row.FieldAsString(1)) | ||
535 | { | ||
536 | var directoryId = row.FieldAsString(0); | ||
537 | |||
538 | if (!directoryLockPermissionsIndex.TryGetValue(directoryId, out var rowList)) | ||
539 | { | ||
540 | rowList = new List<Row>(); | ||
541 | directoryLockPermissionsIndex.Add(directoryId, rowList); | ||
542 | } | ||
543 | |||
544 | rowList.Add(row); | ||
545 | } | ||
546 | } | ||
547 | |||
548 | if (table.Name == "MsiLockPermissionsEx") | ||
549 | { | ||
550 | msiLockPermissionsTable = table; | ||
551 | if ("CreateFolder" == row.FieldAsString(1)) | ||
552 | { | ||
553 | var directoryId = row.FieldAsString(0); | ||
554 | |||
555 | if (!directoryMsiLockPermissionsExIndex.TryGetValue(directoryId, out var rowList)) | ||
556 | { | ||
557 | rowList = new List<Row>(); | ||
558 | directoryMsiLockPermissionsExIndex.Add(directoryId, rowList); | ||
559 | } | ||
560 | |||
561 | rowList.Add(row); | ||
562 | } | ||
563 | } | ||
564 | |||
565 | if (null == row.SectionId) | ||
566 | { | ||
567 | table.Rows.RemoveAt(i); | ||
568 | i--; | ||
569 | } | ||
570 | else | ||
571 | { | ||
572 | var sections = row.SectionId.Split('/'); | ||
573 | // ignore the row without section id. | ||
574 | if (0 == sections[0].Length && 0 == sections[1].Length) | ||
575 | { | ||
576 | table.Rows.RemoveAt(i); | ||
577 | i--; | ||
578 | } | ||
579 | else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections)) | ||
580 | { | ||
581 | if ("Component" == table.Name) | ||
582 | { | ||
583 | keptComponents.Add(row.FieldAsString(0), row); | ||
584 | } | ||
585 | |||
586 | if ("Directory" == table.Name) | ||
587 | { | ||
588 | keptDirectories.Add(row.FieldAsString(0), row); | ||
589 | } | ||
590 | |||
591 | if ("Feature" == table.Name) | ||
592 | { | ||
593 | keptFeatures.Add(row.FieldAsString(0), row); | ||
594 | } | ||
595 | |||
596 | keptRows++; | ||
597 | } | ||
598 | else | ||
599 | { | ||
600 | table.Rows.RemoveAt(i); | ||
601 | i--; | ||
602 | } | ||
603 | } | ||
604 | } | ||
605 | } | ||
606 | |||
607 | keptRows += ReduceTransformSequenceTable(sequenceList, oldSections, newSections, customActionTable); | ||
608 | |||
609 | if (null != directoryTable) | ||
610 | { | ||
611 | foreach (var componentRow in keptComponents.Values) | ||
612 | { | ||
613 | var componentId = componentRow.FieldAsString(0); | ||
614 | |||
615 | if (RowOperation.Add == componentRow.Operation) | ||
616 | { | ||
617 | // Make sure each added component has its required directory and feature heirarchy. | ||
618 | var directoryId = componentRow.FieldAsString(2); | ||
619 | while (null != directoryId && directoryTableAdds.TryGetValue(directoryId, out var directoryRow)) | ||
620 | { | ||
621 | if (!keptDirectories.ContainsKey(directoryId)) | ||
622 | { | ||
623 | directoryTable.Rows.Add(directoryRow); | ||
624 | keptDirectories.Add(directoryId, directoryRow); | ||
625 | keptRows++; | ||
626 | } | ||
627 | |||
628 | directoryId = directoryRow.FieldAsString(1); | ||
629 | } | ||
630 | |||
631 | if (componentFeatureAddsIndex.TryGetValue(componentId, out var componentFeatureIds)) | ||
632 | { | ||
633 | foreach (var featureId in componentFeatureIds) | ||
634 | { | ||
635 | var currentFeatureId = featureId; | ||
636 | while (null != currentFeatureId && featureTableAdds.TryGetValue(currentFeatureId, out var featureRow)) | ||
637 | { | ||
638 | if (!keptFeatures.ContainsKey(currentFeatureId)) | ||
639 | { | ||
640 | featureTable.Rows.Add(featureRow); | ||
641 | keptFeatures.Add(currentFeatureId, featureRow); | ||
642 | keptRows++; | ||
643 | } | ||
644 | |||
645 | currentFeatureId = featureRow.FieldAsString(1); | ||
646 | } | ||
647 | } | ||
648 | } | ||
649 | } | ||
650 | |||
651 | // Hook in changes LockPermissions and MsiLockPermissions for folders for each component that has been kept. | ||
652 | foreach (var keptComponentId in keptComponents.Keys) | ||
653 | { | ||
654 | if (componentCreateFolderIndex.TryGetValue(keptComponentId, out var directoryList)) | ||
655 | { | ||
656 | foreach (var directoryId in directoryList) | ||
657 | { | ||
658 | if (directoryLockPermissionsIndex.TryGetValue(directoryId, out var lockPermissionsRowList)) | ||
659 | { | ||
660 | foreach (var lockPermissionsRow in lockPermissionsRowList) | ||
661 | { | ||
662 | var key = lockPermissionsRow.GetPrimaryKey('/'); | ||
663 | if (keptLockPermissions.Add(key)) | ||
664 | { | ||
665 | lockPermissionsTable.Rows.Add(lockPermissionsRow); | ||
666 | keptRows++; | ||
667 | } | ||
668 | } | ||
669 | } | ||
670 | |||
671 | if (directoryMsiLockPermissionsExIndex.TryGetValue(directoryId, out var msiLockPermissionsExRowList)) | ||
672 | { | ||
673 | foreach (var msiLockPermissionsExRow in msiLockPermissionsExRowList) | ||
674 | { | ||
675 | var key = msiLockPermissionsExRow.GetPrimaryKey('/'); | ||
676 | if (keptMsiLockPermissionExs.Add(key)) | ||
677 | { | ||
678 | msiLockPermissionsTable.Rows.Add(msiLockPermissionsExRow); | ||
679 | keptRows++; | ||
680 | } | ||
681 | } | ||
682 | } | ||
683 | } | ||
684 | } | ||
685 | } | ||
686 | } | ||
687 | } | ||
688 | |||
689 | keptRows += ReduceTransformSequenceTable(sequenceList, oldSections, newSections, customActionTable); | ||
690 | |||
691 | // Delete tables that are empty. | ||
692 | var tablesToDelete = transform.Tables.Where(t => t.Rows.Count == 0).Select(t => t.Name); | ||
693 | |||
694 | foreach (var tableName in tablesToDelete) | ||
695 | { | ||
696 | transform.Tables.Remove(tableName); | ||
697 | } | ||
698 | |||
699 | return keptRows > 0; | ||
700 | } | ||
701 | |||
702 | private void Validate(PatchTransform patchTransform) | ||
703 | { | ||
704 | var transformPath = patchTransform.Baseline; // TODO: this is used in error messages, how best to set it? | ||
705 | var transform = patchTransform.Transform; | ||
706 | |||
707 | // Changing the ProdocutCode in a patch transform is not recommended. | ||
708 | if (transform.TryGetTable("Property", out var propertyTable)) | ||
709 | { | ||
710 | foreach (var row in propertyTable.Rows) | ||
711 | { | ||
712 | // Only interested in modified rows; fast check. | ||
713 | if (RowOperation.Modify == row.Operation && | ||
714 | "ProductCode".Equals(row.FieldAsString(0), StringComparison.Ordinal)) | ||
715 | { | ||
716 | this.Messaging.Write(WarningMessages.MajorUpgradePatchNotRecommended()); | ||
717 | } | ||
718 | } | ||
719 | } | ||
720 | |||
721 | // If there is nothing in the component table we can return early because the remaining checks are component based. | ||
722 | if (!transform.TryGetTable("Component", out var componentTable)) | ||
723 | { | ||
724 | return; | ||
725 | } | ||
726 | |||
727 | // Index Feature table row operations | ||
728 | var featureOps = new Dictionary<string, RowOperation>(); | ||
729 | if (transform.TryGetTable("Feature", out var featureTable)) | ||
730 | { | ||
731 | foreach (var row in featureTable.Rows) | ||
732 | { | ||
733 | featureOps[row.FieldAsString(0)] = row.Operation; | ||
734 | } | ||
735 | } | ||
736 | |||
737 | // Index Component table and check for keypath modifications | ||
738 | var componentKeyPath = new Dictionary<string, string>(); | ||
739 | var deletedComponent = new Dictionary<string, Row>(); | ||
740 | foreach (var row in componentTable.Rows) | ||
741 | { | ||
742 | var id = row.FieldAsString(0); | ||
743 | var keypath = row.FieldAsString(5) ?? String.Empty; | ||
744 | |||
745 | componentKeyPath.Add(id, keypath); | ||
746 | |||
747 | if (RowOperation.Delete == row.Operation) | ||
748 | { | ||
749 | deletedComponent.Add(id, row); | ||
750 | } | ||
751 | else if (RowOperation.Modify == row.Operation) | ||
752 | { | ||
753 | if (row.Fields[1].Modified) | ||
754 | { | ||
755 | // Changing the guid of a component is equal to deleting the old one and adding a new one. | ||
756 | deletedComponent.Add(id, row); | ||
757 | } | ||
758 | |||
759 | // If the keypath is modified its an error | ||
760 | if (row.Fields[5].Modified) | ||
761 | { | ||
762 | this.Messaging.Write(ErrorMessages.InvalidKeypathChange(row.SourceLineNumbers, id, transformPath)); | ||
763 | } | ||
764 | } | ||
765 | } | ||
766 | |||
767 | // Verify changes in the file table | ||
768 | if (transform.TryGetTable("File", out var fileTable)) | ||
769 | { | ||
770 | var componentWithChangedKeyPath = new Dictionary<string, string>(); | ||
771 | foreach (var row in fileTable.Rows) | ||
772 | { | ||
773 | if (RowOperation.None == row.Operation) | ||
774 | { | ||
775 | continue; | ||
776 | } | ||
777 | |||
778 | var fileId = row.FieldAsString(0); | ||
779 | var componentId = row.FieldAsString(1); | ||
780 | |||
781 | // If this file is the keypath of a component | ||
782 | if (componentKeyPath.TryGetValue(componentId, out var keyPath) && keyPath.Equals(fileId, StringComparison.Ordinal)) | ||
783 | { | ||
784 | if (row.Fields[2].Modified) | ||
785 | { | ||
786 | // You can't change the filename of a file that is the keypath of a component. | ||
787 | this.Messaging.Write(ErrorMessages.InvalidKeypathChange(row.SourceLineNumbers, componentId, transformPath)); | ||
788 | } | ||
789 | |||
790 | if (!componentWithChangedKeyPath.ContainsKey(componentId)) | ||
791 | { | ||
792 | componentWithChangedKeyPath.Add(componentId, fileId); | ||
793 | } | ||
794 | } | ||
795 | |||
796 | if (RowOperation.Delete == row.Operation) | ||
797 | { | ||
798 | // If the file is removed from a component that is not deleted. | ||
799 | if (!deletedComponent.ContainsKey(componentId)) | ||
800 | { | ||
801 | var foundRemoveFileEntry = false; | ||
802 | var filename = Common.GetName(row.FieldAsString(2), false, true); | ||
803 | |||
804 | if (transform.TryGetTable("RemoveFile", out var removeFileTable)) | ||
805 | { | ||
806 | foreach (var removeFileRow in removeFileTable.Rows) | ||
807 | { | ||
808 | if (RowOperation.Delete == removeFileRow.Operation) | ||
809 | { | ||
810 | continue; | ||
811 | } | ||
812 | |||
813 | if (componentId == removeFileRow.FieldAsString(1)) | ||
814 | { | ||
815 | // Check if there is a RemoveFile entry for this file | ||
816 | if (null != removeFileRow[2]) | ||
817 | { | ||
818 | var removeFileName = Common.GetName(removeFileRow.FieldAsString(2), false, true); | ||
819 | |||
820 | // Convert the MSI format for a wildcard string to Regex format. | ||
821 | removeFileName = removeFileName.Replace('.', '|').Replace('?', '.').Replace("*", ".*").Replace("|", "\\."); | ||
822 | |||
823 | var regex = new Regex(removeFileName, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); | ||
824 | if (regex.IsMatch(filename)) | ||
825 | { | ||
826 | foundRemoveFileEntry = true; | ||
827 | break; | ||
828 | } | ||
829 | } | ||
830 | } | ||
831 | } | ||
832 | } | ||
833 | |||
834 | if (!foundRemoveFileEntry) | ||
835 | { | ||
836 | this.Messaging.Write(WarningMessages.InvalidRemoveFile(row.SourceLineNumbers, fileId, componentId)); | ||
837 | } | ||
838 | } | ||
839 | } | ||
840 | } | ||
841 | } | ||
842 | |||
843 | var featureComponentsTable = transform.Tables["FeatureComponents"]; | ||
844 | |||
845 | if (0 < deletedComponent.Count) | ||
846 | { | ||
847 | // Index FeatureComponents table. | ||
848 | var featureComponents = new Dictionary<string, List<string>>(); | ||
849 | |||
850 | if (null != featureComponentsTable) | ||
851 | { | ||
852 | foreach (var row in featureComponentsTable.Rows) | ||
853 | { | ||
854 | var componentId = row.FieldAsString(1); | ||
855 | |||
856 | if (!featureComponents.TryGetValue(componentId, out var features)) | ||
857 | { | ||
858 | features = new List<string>(); | ||
859 | featureComponents.Add(componentId, features); | ||
860 | } | ||
861 | |||
862 | features.Add(row.FieldAsString(0)); | ||
863 | } | ||
864 | } | ||
865 | |||
866 | // Check to make sure if a component was deleted, the feature was too. | ||
867 | foreach (var entry in deletedComponent) | ||
868 | { | ||
869 | if (featureComponents.TryGetValue(entry.Key, out var features)) | ||
870 | { | ||
871 | foreach (var featureId in features) | ||
872 | { | ||
873 | if (!featureOps.TryGetValue(featureId, out var op) || op != RowOperation.Delete) | ||
874 | { | ||
875 | // The feature was not deleted. | ||
876 | this.Messaging.Write(ErrorMessages.InvalidRemoveComponent(((Row)entry.Value).SourceLineNumbers, entry.Key.ToString(), featureId, transformPath)); | ||
877 | } | ||
878 | } | ||
879 | } | ||
880 | } | ||
881 | } | ||
882 | |||
883 | // Warn if new components are added to existing features | ||
884 | if (null != featureComponentsTable) | ||
885 | { | ||
886 | foreach (var row in featureComponentsTable.Rows) | ||
887 | { | ||
888 | if (RowOperation.Add == row.Operation) | ||
889 | { | ||
890 | // Check if the feature is in the Feature table | ||
891 | var feature_ = row.FieldAsString(0); | ||
892 | var component_ = row.FieldAsString(1); | ||
893 | |||
894 | // Features may not be present if not referenced | ||
895 | if (!featureOps.ContainsKey(feature_) || RowOperation.Add != (RowOperation)featureOps[feature_]) | ||
896 | { | ||
897 | this.Messaging.Write(WarningMessages.NewComponentAddedToExistingFeature(row.SourceLineNumbers, component_, feature_, transformPath)); | ||
898 | } | ||
899 | } | ||
900 | } | ||
901 | } | ||
902 | } | ||
903 | |||
904 | /// <summary> | ||
905 | /// Remove the ProductCode property from the transform. | ||
906 | /// </summary> | ||
907 | /// <param name="transform">The transform.</param> | ||
908 | /// <remarks> | ||
909 | /// Changing the ProductCode is not supported in a patch. | ||
910 | /// </remarks> | ||
911 | private static void RemoveProductCodeFromTransform(WindowsInstallerData transform) | ||
912 | { | ||
913 | if (transform.Tables.TryGetTable("Property", out var propertyTable)) | ||
914 | { | ||
915 | for (var i = 0; i < propertyTable.Rows.Count; ++i) | ||
916 | { | ||
917 | var propertyRow = propertyTable.Rows[i]; | ||
918 | var property = (string)propertyRow[0]; | ||
919 | |||
920 | if ("ProductCode" == property) | ||
921 | { | ||
922 | propertyTable.Rows.RemoveAt(i); | ||
923 | break; | ||
924 | } | ||
925 | } | ||
926 | } | ||
927 | } | ||
928 | |||
929 | /// <summary> | ||
930 | /// Check if the section is in a PatchFamily. | ||
931 | /// </summary> | ||
932 | /// <param name="oldSection">Section id in target wixout</param> | ||
933 | /// <param name="newSection">Section id in upgrade wixout</param> | ||
934 | /// <param name="oldSections">Dictionary contains section id should be kept in the baseline wixout.</param> | ||
935 | /// <param name="newSections">Dictionary contains section id should be kept in the upgrade wixout.</param> | ||
936 | /// <returns>true if section in patch family</returns> | ||
937 | private static bool IsInPatchFamily(string oldSection, string newSection, Dictionary<string, Row> oldSections, Dictionary<string, Row> newSections) | ||
938 | { | ||
939 | var result = false; | ||
940 | |||
941 | if ((String.IsNullOrEmpty(oldSection) && newSections.ContainsKey(newSection)) || (String.IsNullOrEmpty(newSection) && oldSections.ContainsKey(oldSection))) | ||
942 | { | ||
943 | result = true; | ||
944 | } | ||
945 | else if (!String.IsNullOrEmpty(oldSection) && !String.IsNullOrEmpty(newSection) && (oldSections.ContainsKey(oldSection) || newSections.ContainsKey(newSection))) | ||
946 | { | ||
947 | result = true; | ||
948 | } | ||
949 | |||
950 | return result; | ||
951 | } | ||
952 | |||
953 | /// <summary> | ||
954 | /// Reduce the transform sequence tables. | ||
955 | /// </summary> | ||
956 | /// <param name="sequenceList">ArrayList of tables to be reduced</param> | ||
957 | /// <param name="oldSections">Hashtable contains section id should be kept in the baseline wixout.</param> | ||
958 | /// <param name="newSections">Hashtable contains section id should be kept in the target wixout.</param> | ||
959 | /// <param name="customAction">Hashtable contains all the rows in the CustomAction table.</param> | ||
960 | /// <returns>Number of rows left</returns> | ||
961 | private static int ReduceTransformSequenceTable(List<Table> sequenceList, Dictionary<string, Row> oldSections, Dictionary<string, Row> newSections, Dictionary<string, Row> customAction) | ||
962 | { | ||
963 | var keptRows = 0; | ||
964 | |||
965 | foreach (var currentTable in sequenceList) | ||
966 | { | ||
967 | for (var i = 0; i < currentTable.Rows.Count; i++) | ||
968 | { | ||
969 | var row = currentTable.Rows[i]; | ||
970 | var actionName = row.Fields[0].Data.ToString(); | ||
971 | var sections = row.SectionId.Split('/'); | ||
972 | var isSectionIdEmpty = (sections[0].Length == 0 && sections[1].Length == 0); | ||
973 | |||
974 | if (row.Operation == RowOperation.None) | ||
975 | { | ||
976 | // ignore the rows without section id. | ||
977 | if (isSectionIdEmpty) | ||
978 | { | ||
979 | currentTable.Rows.RemoveAt(i); | ||
980 | i--; | ||
981 | } | ||
982 | else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections)) | ||
983 | { | ||
984 | keptRows++; | ||
985 | } | ||
986 | else | ||
987 | { | ||
988 | currentTable.Rows.RemoveAt(i); | ||
989 | i--; | ||
990 | } | ||
991 | } | ||
992 | else if (row.Operation == RowOperation.Modify) | ||
993 | { | ||
994 | var sequenceChanged = row.Fields[2].Modified; | ||
995 | var conditionChanged = row.Fields[1].Modified; | ||
996 | |||
997 | if (sequenceChanged && !conditionChanged) | ||
998 | { | ||
999 | keptRows++; | ||
1000 | } | ||
1001 | else if (!sequenceChanged && conditionChanged) | ||
1002 | { | ||
1003 | if (isSectionIdEmpty) | ||
1004 | { | ||
1005 | currentTable.Rows.RemoveAt(i); | ||
1006 | i--; | ||
1007 | } | ||
1008 | else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections)) | ||
1009 | { | ||
1010 | keptRows++; | ||
1011 | } | ||
1012 | else | ||
1013 | { | ||
1014 | currentTable.Rows.RemoveAt(i); | ||
1015 | i--; | ||
1016 | } | ||
1017 | } | ||
1018 | else if (sequenceChanged && conditionChanged) | ||
1019 | { | ||
1020 | if (isSectionIdEmpty) | ||
1021 | { | ||
1022 | row.Fields[1].Modified = false; | ||
1023 | keptRows++; | ||
1024 | } | ||
1025 | else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections)) | ||
1026 | { | ||
1027 | keptRows++; | ||
1028 | } | ||
1029 | else | ||
1030 | { | ||
1031 | row.Fields[1].Modified = false; | ||
1032 | keptRows++; | ||
1033 | } | ||
1034 | } | ||
1035 | } | ||
1036 | else if (row.Operation == RowOperation.Delete) | ||
1037 | { | ||
1038 | if (isSectionIdEmpty) | ||
1039 | { | ||
1040 | // it is a stardard action which is added by wix, we should keep this action. | ||
1041 | row.Operation = RowOperation.None; | ||
1042 | keptRows++; | ||
1043 | } | ||
1044 | else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections)) | ||
1045 | { | ||
1046 | keptRows++; | ||
1047 | } | ||
1048 | else | ||
1049 | { | ||
1050 | if (customAction.ContainsKey(actionName)) | ||
1051 | { | ||
1052 | currentTable.Rows.RemoveAt(i); | ||
1053 | i--; | ||
1054 | } | ||
1055 | else | ||
1056 | { | ||
1057 | // it is a stardard action, we should keep this action. | ||
1058 | row.Operation = RowOperation.None; | ||
1059 | keptRows++; | ||
1060 | } | ||
1061 | } | ||
1062 | } | ||
1063 | else if (row.Operation == RowOperation.Add) | ||
1064 | { | ||
1065 | if (isSectionIdEmpty) | ||
1066 | { | ||
1067 | keptRows++; | ||
1068 | } | ||
1069 | else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections)) | ||
1070 | { | ||
1071 | keptRows++; | ||
1072 | } | ||
1073 | else | ||
1074 | { | ||
1075 | if (customAction.ContainsKey(actionName)) | ||
1076 | { | ||
1077 | currentTable.Rows.RemoveAt(i); | ||
1078 | i--; | ||
1079 | } | ||
1080 | else | ||
1081 | { | ||
1082 | keptRows++; | ||
1083 | } | ||
1084 | } | ||
1085 | } | ||
1086 | } | ||
1087 | } | ||
1088 | |||
1089 | return keptRows; | ||
1090 | } | ||
1091 | |||
1092 | /// <summary> | ||
1093 | /// Create the #transform for the given main transform. | ||
1094 | /// </summary> | ||
1095 | private WindowsInstallerData BuildPairedTransform(Dictionary<SumaryInformationType, SummaryInformationTuple> summaryInfo, Dictionary<string, MsiPatchMetadataTuple> patchMetadata, WixPatchIdTuple patchIdTuple, WindowsInstallerData mainTransform, MediaTuple mediaTuple, WixPatchBaselineTuple baselineTuple, out string productCode) | ||
1096 | { | ||
1097 | productCode = null; | ||
1098 | |||
1099 | var pairedTransform = new WindowsInstallerData(null) | ||
1100 | { | ||
1101 | Type = OutputType.Transform, | ||
1102 | Codepage = mainTransform.Codepage | ||
1103 | }; | ||
1104 | |||
1105 | // lookup productVersion property to correct summaryInformation | ||
1106 | var newProductVersion = mainTransform.Tables["Property"]?.Rows.FirstOrDefault(r => r.FieldAsString(0) == "ProductVersion")?.FieldAsString(1); | ||
1107 | |||
1108 | var mainSummaryTable = mainTransform.Tables["_SummaryInformation"]; | ||
1109 | var mainSummaryRows = mainSummaryTable.Rows.ToDictionary(r => r.FieldAsInteger(0)); | ||
1110 | |||
1111 | var baselineValidationFlags = ((int)baselineTuple.ValidationFlags).ToString(CultureInfo.InvariantCulture); | ||
1112 | |||
1113 | if (!mainSummaryRows.ContainsKey((int)SumaryInformationType.TransformValidationFlags)) | ||
1114 | { | ||
1115 | var mainSummaryRow = mainSummaryTable.CreateRow(baselineTuple.SourceLineNumbers); | ||
1116 | mainSummaryRow[0] = (int)SumaryInformationType.TransformValidationFlags; | ||
1117 | mainSummaryRow[1] = baselineValidationFlags; | ||
1118 | } | ||
1119 | |||
1120 | // copy summary information from core transform | ||
1121 | var pairedSummaryTable = pairedTransform.EnsureTable(this.tableDefinitions["_SummaryInformation"]); | ||
1122 | |||
1123 | foreach (var mainSummaryRow in mainSummaryTable.Rows) | ||
1124 | { | ||
1125 | var type = (SumaryInformationType)mainSummaryRow.FieldAsInteger(0); | ||
1126 | var value = mainSummaryRow.FieldAsString(1); | ||
1127 | switch (type) | ||
1128 | { | ||
1129 | case SumaryInformationType.TransformProductCodes: | ||
1130 | var propertyData = value.Split(';'); | ||
1131 | var oldProductVersion = propertyData[0].Substring(38); | ||
1132 | var upgradeCode = propertyData[2]; | ||
1133 | productCode = propertyData[0].Substring(0, 38); | ||
1134 | |||
1135 | if (newProductVersion == null) | ||
1136 | { | ||
1137 | newProductVersion = oldProductVersion; | ||
1138 | } | ||
1139 | |||
1140 | // Force mainTranform to 'old;new;upgrade' and pairedTransform to 'new;new;upgrade' | ||
1141 | mainSummaryRow[1] = String.Concat(productCode, oldProductVersion, ';', productCode, newProductVersion, ';', upgradeCode); | ||
1142 | value = String.Concat(productCode, newProductVersion, ';', productCode, newProductVersion, ';', upgradeCode); | ||
1143 | break; | ||
1144 | case SumaryInformationType.TransformValidationFlags: // use validation flags authored into the patch XML. | ||
1145 | value = baselineValidationFlags; | ||
1146 | mainSummaryRow[1] = value; | ||
1147 | break; | ||
1148 | } | ||
1149 | |||
1150 | var pairedSummaryRow = pairedSummaryTable.CreateRow(mainSummaryRow.SourceLineNumbers); | ||
1151 | pairedSummaryRow[0] = mainSummaryRow[0]; | ||
1152 | pairedSummaryRow[1] = value; | ||
1153 | } | ||
1154 | |||
1155 | if (productCode == null) | ||
1156 | { | ||
1157 | this.Messaging.Write(ErrorMessages.CouldNotDetermineProductCodeFromTransformSummaryInfo()); | ||
1158 | return null; | ||
1159 | } | ||
1160 | |||
1161 | // copy File table | ||
1162 | if (mainTransform.Tables.TryGetTable("File", out var mainFileTable) && 0 < mainFileTable.Rows.Count) | ||
1163 | { | ||
1164 | #if TODO_PATCHING | ||
1165 | // We require file source information. | ||
1166 | var mainWixFileTable = mainTransform.Tables["WixFile"]; | ||
1167 | if (null == mainWixFileTable) | ||
1168 | { | ||
1169 | this.Messaging.Write(ErrorMessages.AdminImageRequired(productCode)); | ||
1170 | return null; | ||
1171 | } | ||
1172 | |||
1173 | var mainFileRows = new RowDictionary<FileRow>(mainFileTable); | ||
1174 | |||
1175 | var pairedFileTable = pairedTransform.EnsureTable(mainFileTable.Definition); | ||
1176 | { | ||
1177 | var mainFileRow = mainFileRows[mainWixFileRow.File]; | ||
1178 | |||
1179 | // set File.Sequence to non null to satisfy transform bind | ||
1180 | mainFileRow.Sequence = 1; | ||
1181 | |||
1182 | // delete's don't need rows in the paired transform | ||
1183 | if (mainFileRow.Operation == RowOperation.Delete) | ||
1184 | { | ||
1185 | continue; | ||
1186 | } | ||
1187 | |||
1188 | var pairedFileRow = (FileRow)pairedFileTable.CreateRow(null); | ||
1189 | pairedFileRow.Operation = RowOperation.Modify; | ||
1190 | for (var i = 0; i < mainFileRow.Fields.Length; i++) | ||
1191 | { | ||
1192 | pairedFileRow[i] = mainFileRow[i]; | ||
1193 | } | ||
1194 | |||
1195 | // override authored media for patch bind | ||
1196 | mainWixFileRow.DiskId = mediaTuple.DiskId; | ||
1197 | |||
1198 | // suppress any change to File.Sequence to avoid bloat | ||
1199 | mainFileRow.Fields[7].Modified = false; | ||
1200 | |||
1201 | // force File row to appear in the transform | ||
1202 | switch (mainFileRow.Operation) | ||
1203 | { | ||
1204 | case RowOperation.Modify: | ||
1205 | case RowOperation.Add: | ||
1206 | pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded; | ||
1207 | pairedFileRow.Fields[6].Modified = true; | ||
1208 | pairedFileRow.Operation = mainFileRow.Operation; | ||
1209 | break; | ||
1210 | default: | ||
1211 | pairedFileRow.Fields[6].Modified = false; | ||
1212 | break; | ||
1213 | } | ||
1214 | } | ||
1215 | #endif | ||
1216 | } | ||
1217 | |||
1218 | // Add Media row to pairedTransform | ||
1219 | var pairedMediaTable = pairedTransform.EnsureTable(this.tableDefinitions["Media"]); | ||
1220 | var pairedMediaRow = pairedMediaTable.CreateRow(mediaTuple.SourceLineNumbers); | ||
1221 | pairedMediaRow.Operation = RowOperation.Add; | ||
1222 | pairedMediaRow[0] = mediaTuple.DiskId; | ||
1223 | pairedMediaRow[1] = mediaTuple.LastSequence ?? 0; | ||
1224 | pairedMediaRow[2] = mediaTuple.DiskPrompt; | ||
1225 | pairedMediaRow[3] = mediaTuple.Cabinet; | ||
1226 | pairedMediaRow[4] = mediaTuple.VolumeLabel; | ||
1227 | pairedMediaRow[5] = mediaTuple.Source; | ||
1228 | |||
1229 | // Add PatchPackage for this Media | ||
1230 | var pairedPackageTable = pairedTransform.EnsureTable(this.tableDefinitions["PatchPackage"]); | ||
1231 | pairedPackageTable.Operation = TableOperation.Add; | ||
1232 | var pairedPackageRow = pairedPackageTable.CreateRow(mediaTuple.SourceLineNumbers); | ||
1233 | pairedPackageRow.Operation = RowOperation.Add; | ||
1234 | pairedPackageRow[0] = patchIdTuple.Id.Id; | ||
1235 | pairedPackageRow[1] = mediaTuple.DiskId; | ||
1236 | |||
1237 | // Add the property to the patch transform's Property table. | ||
1238 | var pairedPropertyTable = pairedTransform.EnsureTable(this.tableDefinitions["Property"]); | ||
1239 | pairedPropertyTable.Operation = TableOperation.Add; | ||
1240 | |||
1241 | // Add property to both identify client patches and whether those patches are removable or not | ||
1242 | patchMetadata.TryGetValue("AllowRemoval", out var allowRemovalTuple); | ||
1243 | |||
1244 | var pairedPropertyRow = pairedPropertyTable.CreateRow(allowRemovalTuple?.SourceLineNumbers); | ||
1245 | pairedPropertyRow.Operation = RowOperation.Add; | ||
1246 | pairedPropertyRow[0] = String.Concat(patchIdTuple.ClientPatchId, ".AllowRemoval"); | ||
1247 | pairedPropertyRow[1] = allowRemovalTuple?.Value ?? "0"; | ||
1248 | |||
1249 | // Add this patch code GUID to the patch transform to identify | ||
1250 | // which patches are installed, including in multi-patch | ||
1251 | // installations. | ||
1252 | pairedPropertyRow = pairedPropertyTable.CreateRow(patchIdTuple.SourceLineNumbers); | ||
1253 | pairedPropertyRow.Operation = RowOperation.Add; | ||
1254 | pairedPropertyRow[0] = String.Concat(patchIdTuple.ClientPatchId, ".PatchCode"); | ||
1255 | pairedPropertyRow[1] = patchIdTuple.Id.Id; | ||
1256 | |||
1257 | // Add PATCHNEWPACKAGECODE to apply to admin layouts. | ||
1258 | pairedPropertyRow = pairedPropertyTable.CreateRow(patchIdTuple.SourceLineNumbers); | ||
1259 | pairedPropertyRow.Operation = RowOperation.Add; | ||
1260 | pairedPropertyRow[0] = "PATCHNEWPACKAGECODE"; | ||
1261 | pairedPropertyRow[1] = patchIdTuple.Id.Id; | ||
1262 | |||
1263 | // Add PATCHNEWSUMMARYCOMMENTS and PATCHNEWSUMMARYSUBJECT to apply to admin layouts. | ||
1264 | if (summaryInfo.TryGetValue(SumaryInformationType.Subject, out var subjectTuple)) | ||
1265 | { | ||
1266 | pairedPropertyRow = pairedPropertyTable.CreateRow(subjectTuple.SourceLineNumbers); | ||
1267 | pairedPropertyRow.Operation = RowOperation.Add; | ||
1268 | pairedPropertyRow[0] = "PATCHNEWSUMMARYSUBJECT"; | ||
1269 | pairedPropertyRow[1] = subjectTuple.Value; | ||
1270 | } | ||
1271 | |||
1272 | if (summaryInfo.TryGetValue(SumaryInformationType.Comments, out var commentsTuple)) | ||
1273 | { | ||
1274 | pairedPropertyRow = pairedPropertyTable.CreateRow(commentsTuple.SourceLineNumbers); | ||
1275 | pairedPropertyRow.Operation = RowOperation.Add; | ||
1276 | pairedPropertyRow[0] = "PATCHNEWSUMMARYCOMMENTS"; | ||
1277 | pairedPropertyRow[1] = commentsTuple.Value; | ||
1278 | } | ||
1279 | |||
1280 | return pairedTransform; | ||
1281 | } | ||
1282 | |||
1283 | private static SortedSet<string> FinalizePatchProductCodes(List<IntermediateTuple> tuples, SortedSet<string> productCodes) | ||
1284 | { | ||
1285 | var patchTargetTuples = tuples.OfType<WixPatchTargetTuple>().ToList(); | ||
1286 | |||
1287 | if (patchTargetTuples.Count > 0) | ||
1288 | { | ||
1289 | var targets = new SortedSet<string>(); | ||
1290 | var replace = true; | ||
1291 | foreach (var wixPatchTargetRow in patchTargetTuples) | ||
1292 | { | ||
1293 | var target = wixPatchTargetRow.ProductCode.ToUpperInvariant(); | ||
1294 | if (target == "*") | ||
1295 | { | ||
1296 | replace = false; | ||
1297 | } | ||
1298 | else | ||
1299 | { | ||
1300 | targets.Add(target); | ||
1301 | } | ||
1302 | } | ||
1303 | |||
1304 | // Replace the target ProductCodes with the authored list. | ||
1305 | if (replace) | ||
1306 | { | ||
1307 | productCodes = targets; | ||
1308 | } | ||
1309 | else | ||
1310 | { | ||
1311 | // Copy the authored target ProductCodes into the list. | ||
1312 | foreach (var target in targets) | ||
1313 | { | ||
1314 | productCodes.Add(target); | ||
1315 | } | ||
1316 | } | ||
1317 | } | ||
1318 | |||
1319 | return productCodes; | ||
1320 | } | ||
1321 | } | ||
1322 | } | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs index 175203ce..34104ef5 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs | |||
@@ -24,7 +24,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
24 | 24 | ||
25 | private bool disposed; | 25 | private bool disposed; |
26 | 26 | ||
27 | public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, Validator validator) | 27 | public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, Validator validator):this(context, backendExtension, null, validator) |
28 | { | ||
29 | } | ||
30 | |||
31 | public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, IEnumerable<SubStorage> subStorages, Validator validator) | ||
28 | { | 32 | { |
29 | this.ServiceProvider = context.ServiceProvider; | 33 | this.ServiceProvider = context.ServiceProvider; |
30 | 34 | ||
@@ -45,6 +49,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
45 | this.OutputPath = context.OutputPath; | 49 | this.OutputPath = context.OutputPath; |
46 | this.OutputPdbPath = context.OutputPdbPath; | 50 | this.OutputPdbPath = context.OutputPdbPath; |
47 | this.IntermediateFolder = context.IntermediateFolder; | 51 | this.IntermediateFolder = context.IntermediateFolder; |
52 | this.SubStorages = subStorages; | ||
48 | this.Validator = validator; | 53 | this.Validator = validator; |
49 | 54 | ||
50 | this.BackendExtensions = backendExtension; | 55 | this.BackendExtensions = backendExtension; |
@@ -76,6 +81,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
76 | 81 | ||
77 | private IEnumerable<IWindowsInstallerBackendBinderExtension> BackendExtensions { get; } | 82 | private IEnumerable<IWindowsInstallerBackendBinderExtension> BackendExtensions { get; } |
78 | 83 | ||
84 | private IEnumerable<SubStorage> SubStorages { get; } | ||
85 | |||
79 | private Intermediate Intermediate { get; } | 86 | private Intermediate Intermediate { get; } |
80 | 87 | ||
81 | private string OutputPath { get; } | 88 | private string OutputPath { get; } |
@@ -112,18 +119,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
112 | // Load standard tables, authored custom tables, and extension custom tables. | 119 | // Load standard tables, authored custom tables, and extension custom tables. |
113 | TableDefinitionCollection tableDefinitions; | 120 | TableDefinitionCollection tableDefinitions; |
114 | { | 121 | { |
115 | var command = new LoadTableDefinitionsCommand(section); | 122 | var command = new LoadTableDefinitionsCommand(section, this.BackendExtensions); |
116 | command.Execute(); | 123 | command.Execute(); |
117 | 124 | ||
118 | tableDefinitions = command.TableDefinitions; | 125 | tableDefinitions = command.TableDefinitions; |
119 | |||
120 | foreach (var backendExtension in this.BackendExtensions) | ||
121 | { | ||
122 | foreach (var tableDefinition in backendExtension.TableDefinitions) | ||
123 | { | ||
124 | tableDefinitions.Add(tableDefinition); | ||
125 | } | ||
126 | } | ||
127 | } | 126 | } |
128 | 127 | ||
129 | // Process the summary information table before the other tables. | 128 | // Process the summary information table before the other tables. |
@@ -186,8 +185,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
186 | 185 | ||
187 | // Sequence all the actions. | 186 | // Sequence all the actions. |
188 | { | 187 | { |
189 | var command = new SequenceActionsCommand(section); | 188 | var command = new SequenceActionsCommand(this.Messaging, section); |
190 | command.Messaging = this.Messaging; | ||
191 | command.Execute(); | 189 | command.Execute(); |
192 | } | 190 | } |
193 | 191 | ||
@@ -196,7 +194,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
196 | command.Execute(); | 194 | command.Execute(); |
197 | } | 195 | } |
198 | 196 | ||
199 | #if TODO_FINISH_PATCH | 197 | #if TODO_PATCHING |
200 | ////if (OutputType.Patch == this.Output.Type) | 198 | ////if (OutputType.Patch == this.Output.Type) |
201 | ////{ | 199 | ////{ |
202 | //// foreach (SubStorage substorage in this.Output.SubStorages) | 200 | //// foreach (SubStorage substorage in this.Output.SubStorages) |
@@ -223,130 +221,162 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
223 | return; | 221 | return; |
224 | } | 222 | } |
225 | 223 | ||
226 | this.Messaging.Write(VerboseMessages.UpdatingFileInformation()); | 224 | // Call extension |
225 | var ExtensionSaidSkip = false; | ||
227 | 226 | ||
228 | // This must occur after all variables and source paths have been resolved. | 227 | WindowsInstallerData output; |
229 | List<FileFacade> fileFacades; | 228 | if (ExtensionSaidSkip) |
230 | { | 229 | { |
231 | var command = new GetFileFacadesCommand(section); | 230 | // Time to create the output object, since we're bypassing everything that touches files. |
231 | var command = new CreateOutputFromIRCommand(this.Messaging, section, tableDefinitions, this.BackendExtensions); | ||
232 | command.Execute(); | 232 | command.Execute(); |
233 | 233 | ||
234 | fileFacades = command.FileFacades; | 234 | output = command.Output; |
235 | } | 235 | } |
236 | 236 | else | |
237 | // Extract files that come from binary .wixlibs and WixExtensions (this does not extract files from merge modules). | ||
238 | { | 237 | { |
239 | var command = new ExtractEmbeddedFilesCommand(this.ExpectedEmbeddedFiles); | 238 | this.Messaging.Write(VerboseMessages.UpdatingFileInformation()); |
240 | command.Execute(); | ||
241 | } | ||
242 | 239 | ||
243 | // Gather information about files that do not come from merge modules. | 240 | // Extract files that come from binary .wixlibs and WixExtensions (this does not extract files from merge modules). |
244 | { | 241 | { |
245 | var command = new UpdateFileFacadesCommand(this.Messaging, section); | 242 | var command = new ExtractEmbeddedFilesCommand(this.ExpectedEmbeddedFiles); |
246 | command.FileFacades = fileFacades; | 243 | command.Execute(); |
247 | command.UpdateFileFacades = fileFacades.Where(f => !f.FromModule); | 244 | } |
248 | command.OverwriteHash = true; | ||
249 | command.TableDefinitions = tableDefinitions; | ||
250 | command.VariableCache = variableCache; | ||
251 | command.Execute(); | ||
252 | } | ||
253 | 245 | ||
254 | // Now that the variable cache is populated, resolve any delayed fields. | 246 | // This must occur after all variables and source paths have been resolved. |
255 | if (this.DelayedFields.Any()) | 247 | List<FileFacade> fileFacades; |
256 | { | 248 | { |
257 | var command = new ResolveDelayedFieldsCommand(this.Messaging, this.DelayedFields, variableCache); | 249 | var command = new GetFileFacadesCommand(section); |
258 | command.Execute(); | 250 | command.Execute(); |
259 | } | ||
260 | 251 | ||
261 | // Set generated component guids. | 252 | fileFacades = command.FileFacades; |
262 | { | 253 | } |
263 | var command = new CalculateComponentGuids(this.Messaging, this.BackendHelper, this.PathResolver, section); | ||
264 | command.Execute(); | ||
265 | } | ||
266 | 254 | ||
267 | // Retrieve file information from merge modules. | 255 | // Retrieve file information from merge modules. |
268 | if (SectionType.Product == section.Type) | 256 | if (SectionType.Product == section.Type) |
269 | { | 257 | { |
270 | var wixMergeTuples = section.Tuples.OfType<WixMergeTuple>().ToList(); | 258 | var wixMergeTuples = section.Tuples.OfType<WixMergeTuple>().ToList(); |
259 | |||
260 | if (wixMergeTuples.Any()) | ||
261 | { | ||
262 | containsMergeModules = true; | ||
263 | |||
264 | var command = new ExtractMergeModuleFilesCommand(this.Messaging, section, wixMergeTuples); | ||
265 | command.FileFacades = fileFacades; | ||
266 | command.OutputInstallerVersion = installerVersion; | ||
267 | command.SuppressLayout = this.SuppressLayout; | ||
268 | command.IntermediateFolder = this.IntermediateFolder; | ||
269 | command.Execute(); | ||
271 | 270 | ||
272 | if (wixMergeTuples.Any()) | 271 | fileFacades.AddRange(command.MergeModulesFileFacades); |
272 | } | ||
273 | } | ||
274 | else if (SectionType.Patch == section.Type) | ||
273 | { | 275 | { |
274 | containsMergeModules = true; | 276 | // Merge transform data into the output object. |
277 | //IEnumerable<FileFacade> filesFromTransform = this.CopyFromTransformData(this.Output); | ||
278 | |||
279 | //var command = new CopyTransformDataCommand(this.Messaging, /*output*/this.SubStorages, tableDefinitions, copyOutFileRows: true); | ||
280 | //command.Output = output; | ||
281 | //command.TableDefinitions = this.TableDefinitions; | ||
282 | //command.CopyOutFileRows = true; | ||
283 | var command = new GetFileFacadesFromTransforms(this.Messaging, this.SubStorages, tableDefinitions); | ||
284 | command.Execute(); | ||
285 | var filesFromTransforms = command.FileFacades; | ||
275 | 286 | ||
276 | var command = new ExtractMergeModuleFilesCommand(this.Messaging, section, wixMergeTuples); | 287 | fileFacades.AddRange(filesFromTransforms); |
288 | } | ||
289 | |||
290 | // stop processing if an error previously occurred | ||
291 | if (this.Messaging.EncounteredError) | ||
292 | { | ||
293 | return; | ||
294 | } | ||
295 | |||
296 | // Gather information about files that do not come from merge modules. | ||
297 | { | ||
298 | var command = new UpdateFileFacadesCommand(this.Messaging, section); | ||
277 | command.FileFacades = fileFacades; | 299 | command.FileFacades = fileFacades; |
278 | command.OutputInstallerVersion = installerVersion; | 300 | command.UpdateFileFacades = fileFacades.Where(f => !f.FromModule); |
279 | command.SuppressLayout = this.SuppressLayout; | 301 | command.OverwriteHash = true; |
280 | command.IntermediateFolder = this.IntermediateFolder; | 302 | command.TableDefinitions = tableDefinitions; |
303 | command.VariableCache = variableCache; | ||
281 | command.Execute(); | 304 | command.Execute(); |
282 | |||
283 | fileFacades.AddRange(command.MergeModulesFileFacades); | ||
284 | } | 305 | } |
285 | } | ||
286 | #if TODO_FINISH_PATCH | ||
287 | else if (OutputType.Patch == this.Output.Type) | ||
288 | { | ||
289 | // Merge transform data into the output object. | ||
290 | IEnumerable<FileFacade> filesFromTransform = this.CopyFromTransformData(this.Output); | ||
291 | 306 | ||
292 | fileFacades.AddRange(filesFromTransform); | 307 | // Assign files to media. |
293 | } | 308 | Dictionary<int, MediaTuple> assignedMediaRows; |
294 | #endif | 309 | Dictionary<MediaTuple, IEnumerable<FileFacade>> filesByCabinetMedia; |
310 | IEnumerable<FileFacade> uncompressedFiles; | ||
311 | { | ||
312 | var command = new AssignMediaCommand(section, this.Messaging); | ||
313 | command.FileFacades = fileFacades; | ||
314 | command.FilesCompressed = compressed; | ||
315 | command.Execute(); | ||
295 | 316 | ||
296 | // stop processing if an error previously occurred | 317 | assignedMediaRows = command.MediaRows; |
297 | if (this.Messaging.EncounteredError) | 318 | filesByCabinetMedia = command.FileFacadesByCabinetMedia; |
298 | { | 319 | uncompressedFiles = command.UncompressedFileFacades; |
299 | return; | 320 | } |
300 | } | ||
301 | 321 | ||
302 | // Assign files to media. | 322 | // stop processing if an error previously occurred |
303 | Dictionary<int, MediaTuple> assignedMediaRows; | 323 | if (this.Messaging.EncounteredError) |
304 | Dictionary<MediaTuple, IEnumerable<FileFacade>> filesByCabinetMedia; | 324 | { |
305 | IEnumerable<FileFacade> uncompressedFiles; | 325 | return; |
306 | { | 326 | } |
307 | var command = new AssignMediaCommand(section, this.Messaging); | ||
308 | command.FileFacades = fileFacades; | ||
309 | command.FilesCompressed = compressed; | ||
310 | command.Execute(); | ||
311 | 327 | ||
312 | assignedMediaRows = command.MediaRows; | 328 | // Now that the variable cache is populated, resolve any delayed fields. |
313 | filesByCabinetMedia = command.FileFacadesByCabinetMedia; | 329 | if (this.DelayedFields.Any()) |
314 | uncompressedFiles = command.UncompressedFileFacades; | 330 | { |
315 | } | 331 | var command = new ResolveDelayedFieldsCommand(this.Messaging, this.DelayedFields, variableCache); |
332 | command.Execute(); | ||
333 | } | ||
316 | 334 | ||
317 | // stop processing if an error previously occurred | 335 | // Set generated component guids. |
318 | if (this.Messaging.EncounteredError) | 336 | { |
319 | { | 337 | var command = new CalculateComponentGuids(this.Messaging, this.BackendHelper, this.PathResolver, section); |
320 | return; | 338 | command.Execute(); |
321 | } | 339 | } |
322 | 340 | ||
323 | // Time to create the output object. Try to put as much above here as possible, updating the IR is better. | 341 | // stop processing if an error previously occurred |
324 | WindowsInstallerData output; | 342 | if (this.Messaging.EncounteredError) |
325 | { | 343 | { |
326 | var command = new CreateOutputFromIRCommand(this.Messaging, section, tableDefinitions, this.BackendExtensions); | 344 | return; |
327 | command.Execute(); | 345 | } |
328 | 346 | ||
329 | output = command.Output; | 347 | // Time to create the output object. Try to put as much above here as possible, updating the IR is better. |
330 | } | 348 | { |
349 | var command = new CreateOutputFromIRCommand(this.Messaging, section, tableDefinitions, this.BackendExtensions); | ||
350 | command.Execute(); | ||
331 | 351 | ||
332 | // Update file sequence. | 352 | output = command.Output; |
333 | { | 353 | } |
334 | var command = new UpdateMediaSequencesCommand(output, fileFacades); | ||
335 | command.Execute(); | ||
336 | } | ||
337 | 354 | ||
338 | // Modularize identifiers. | 355 | // Update file sequence. |
339 | if (OutputType.Module == output.Type) | 356 | { |
340 | { | 357 | var command = new UpdateMediaSequencesCommand(output, fileFacades); |
341 | var command = new ModularizeCommand(output, modularizationGuid, section.Tuples.OfType<WixSuppressModularizationTuple>()); | 358 | command.Execute(); |
342 | command.Execute(); | 359 | } |
343 | } | 360 | |
344 | else // we can create instance transforms since Component Guids are set. | 361 | // Modularize identifiers. |
345 | { | 362 | if (OutputType.Module == output.Type) |
363 | { | ||
364 | var command = new ModularizeCommand(output, modularizationGuid, section.Tuples.OfType<WixSuppressModularizationTuple>()); | ||
365 | command.Execute(); | ||
366 | } | ||
367 | else if (output.Type == OutputType.Patch) | ||
368 | { | ||
369 | foreach (var storage in this.SubStorages) | ||
370 | { | ||
371 | output.SubStorages.Add(storage); | ||
372 | } | ||
373 | } | ||
374 | else // we can create instance transforms since Component Guids are set. | ||
375 | { | ||
346 | #if TODO_FIX_INSTANCE_TRANSFORM | 376 | #if TODO_FIX_INSTANCE_TRANSFORM |
347 | this.CreateInstanceTransforms(this.Output); | 377 | this.CreateInstanceTransforms(this.Output); |
348 | #endif | 378 | #endif |
349 | } | 379 | } |
350 | 380 | ||
351 | #if TODO_FINISH_UPDATE | 381 | #if TODO_FINISH_UPDATE |
352 | // Extended binder extensions can be called now that fields are resolved. | 382 | // Extended binder extensions can be called now that fields are resolved. |
@@ -384,116 +414,121 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
384 | } | 414 | } |
385 | #endif | 415 | #endif |
386 | 416 | ||
387 | // Stop processing if an error previously occurred. | 417 | this.ValidateComponentGuids(output); |
388 | if (this.Messaging.EncounteredError) | ||
389 | { | ||
390 | return; | ||
391 | } | ||
392 | 418 | ||
393 | // Ensure the intermediate folder is created since delta patches will be | 419 | // Stop processing if an error previously occurred. |
394 | // created there. | 420 | if (this.Messaging.EncounteredError) |
395 | Directory.CreateDirectory(this.IntermediateFolder); | 421 | { |
422 | return; | ||
423 | } | ||
396 | 424 | ||
397 | if (SectionType.Patch == section.Type && this.DeltaBinaryPatch) | 425 | // Ensure the intermediate folder is created since delta patches will be |
398 | { | 426 | // created there. |
399 | var command = new CreateDeltaPatchesCommand(fileFacades, this.IntermediateFolder, section.Tuples.OfType<WixPatchIdTuple>().FirstOrDefault()); | 427 | Directory.CreateDirectory(this.IntermediateFolder); |
400 | command.Execute(); | ||
401 | } | ||
402 | 428 | ||
403 | // create cabinet files and process uncompressed files | 429 | if (SectionType.Patch == section.Type && this.DeltaBinaryPatch) |
404 | var layoutDirectory = Path.GetDirectoryName(this.OutputPath); | 430 | { |
405 | if (!this.SuppressLayout || OutputType.Module == output.Type) | 431 | var command = new CreateDeltaPatchesCommand(fileFacades, this.IntermediateFolder, section.Tuples.OfType<WixPatchIdTuple>().FirstOrDefault()); |
406 | { | 432 | command.Execute(); |
407 | this.Messaging.Write(VerboseMessages.CreatingCabinetFiles()); | 433 | } |
408 | |||
409 | var command = new CreateCabinetsCommand(this.ServiceProvider, this.BackendHelper); | ||
410 | command.CabbingThreadCount = this.CabbingThreadCount; | ||
411 | command.CabCachePath = this.CabCachePath; | ||
412 | command.DefaultCompressionLevel = this.DefaultCompressionLevel; | ||
413 | command.Output = output; | ||
414 | command.Messaging = this.Messaging; | ||
415 | command.BackendExtensions = this.BackendExtensions; | ||
416 | command.LayoutDirectory = layoutDirectory; | ||
417 | command.Compressed = compressed; | ||
418 | command.FileRowsByCabinet = filesByCabinetMedia; | ||
419 | command.ResolveMedia = this.ResolveMedia; | ||
420 | command.TableDefinitions = tableDefinitions; | ||
421 | command.TempFilesLocation = this.IntermediateFolder; | ||
422 | command.Execute(); | ||
423 | 434 | ||
424 | fileTransfers.AddRange(command.FileTransfers); | 435 | // create cabinet files and process uncompressed files |
425 | trackedFiles.AddRange(command.TrackedFiles); | 436 | var layoutDirectory = Path.GetDirectoryName(this.OutputPath); |
426 | } | 437 | if (!this.SuppressLayout || OutputType.Module == output.Type) |
438 | { | ||
439 | this.Messaging.Write(VerboseMessages.CreatingCabinetFiles()); | ||
440 | |||
441 | var command = new CreateCabinetsCommand(this.ServiceProvider, this.BackendHelper); | ||
442 | command.CabbingThreadCount = this.CabbingThreadCount; | ||
443 | command.CabCachePath = this.CabCachePath; | ||
444 | command.DefaultCompressionLevel = this.DefaultCompressionLevel; | ||
445 | command.Output = output; | ||
446 | command.Messaging = this.Messaging; | ||
447 | command.BackendExtensions = this.BackendExtensions; | ||
448 | command.LayoutDirectory = layoutDirectory; | ||
449 | command.Compressed = compressed; | ||
450 | command.FileRowsByCabinet = filesByCabinetMedia; | ||
451 | command.ResolveMedia = this.ResolveMedia; | ||
452 | command.TableDefinitions = tableDefinitions; | ||
453 | command.TempFilesLocation = this.IntermediateFolder; | ||
454 | command.Execute(); | ||
427 | 455 | ||
428 | #if TODO_FINISH_PATCH | 456 | fileTransfers.AddRange(command.FileTransfers); |
429 | if (OutputType.Patch == this.Output.Type) | 457 | trackedFiles.AddRange(command.TrackedFiles); |
430 | { | 458 | } |
431 | // copy output data back into the transforms | ||
432 | this.CopyToTransformData(this.Output); | ||
433 | } | ||
434 | #endif | ||
435 | 459 | ||
436 | this.ValidateComponentGuids(output); | 460 | #if DELETE |
461 | if (OutputType.Patch == output.Type) | ||
462 | { | ||
463 | // Copy output data back into the transforms. | ||
464 | #if TODO_PATCHING | ||
465 | var command = new CopyTransformDataCommand(this.Messaging, output, tableDefinitions, copyOutFileRows: false); | ||
466 | command.Execute(); | ||
437 | 467 | ||
438 | // stop processing if an error previously occurred | 468 | this.CopyToTransformData(this.Output); |
439 | if (this.Messaging.EncounteredError) | 469 | #endif |
440 | { | 470 | } |
441 | return; | 471 | #endif |
442 | } | ||
443 | 472 | ||
444 | // Generate database file. | 473 | // stop processing if an error previously occurred |
445 | this.Messaging.Write(VerboseMessages.GeneratingDatabase()); | 474 | if (this.Messaging.EncounteredError) |
475 | { | ||
476 | return; | ||
477 | } | ||
446 | 478 | ||
447 | { | 479 | // Generate database file. |
448 | var trackMsi = this.BackendHelper.TrackFile(this.OutputPath, TrackedFileType.Final); | 480 | this.Messaging.Write(VerboseMessages.GeneratingDatabase()); |
449 | trackedFiles.Add(trackMsi); | ||
450 | 481 | ||
451 | var temporaryFiles = this.GenerateDatabase(output, tableDefinitions, trackMsi.Path, false, false); | 482 | { |
452 | trackedFiles.AddRange(temporaryFiles); | 483 | var trackMsi = this.BackendHelper.TrackFile(this.OutputPath, TrackedFileType.Final); |
453 | } | 484 | trackedFiles.Add(trackMsi); |
454 | 485 | ||
455 | // Stop processing if an error previously occurred. | 486 | var temporaryFiles = this.GenerateDatabase(output, tableDefinitions, trackMsi.Path, false, false); |
456 | if (this.Messaging.EncounteredError) | 487 | trackedFiles.AddRange(temporaryFiles); |
457 | { | 488 | } |
458 | return; | ||
459 | } | ||
460 | 489 | ||
461 | // Merge modules. | 490 | // Stop processing if an error previously occurred. |
462 | if (containsMergeModules) | 491 | if (this.Messaging.EncounteredError) |
463 | { | 492 | { |
464 | this.Messaging.Write(VerboseMessages.MergingModules()); | 493 | return; |
494 | } | ||
465 | 495 | ||
466 | // Add back possibly suppressed sequence tables since all sequence tables must be present | 496 | // Merge modules. |
467 | // for the merge process to work. We'll drop the suppressed sequence tables again as | 497 | if (containsMergeModules) |
468 | // necessary. | ||
469 | foreach (SequenceTable sequence in Enum.GetValues(typeof(SequenceTable))) | ||
470 | { | 498 | { |
471 | var sequenceTableName = sequence.ToString(); | 499 | this.Messaging.Write(VerboseMessages.MergingModules()); |
472 | var sequenceTable = output.Tables[sequenceTableName]; | ||
473 | 500 | ||
474 | if (null == sequenceTable) | 501 | // Add back possibly suppressed sequence tables since all sequence tables must be present |
502 | // for the merge process to work. We'll drop the suppressed sequence tables again as | ||
503 | // necessary. | ||
504 | foreach (SequenceTable sequence in Enum.GetValues(typeof(SequenceTable))) | ||
475 | { | 505 | { |
476 | sequenceTable = output.EnsureTable(tableDefinitions[sequenceTableName]); | 506 | var sequenceTableName = sequence.ToString(); |
477 | } | 507 | var sequenceTable = output.Tables[sequenceTableName]; |
478 | 508 | ||
479 | if (0 == sequenceTable.Rows.Count) | 509 | if (null == sequenceTable) |
480 | { | 510 | { |
481 | suppressedTableNames.Add(sequenceTableName); | 511 | sequenceTable = output.EnsureTable(tableDefinitions[sequenceTableName]); |
512 | } | ||
513 | |||
514 | if (0 == sequenceTable.Rows.Count) | ||
515 | { | ||
516 | suppressedTableNames.Add(sequenceTableName); | ||
517 | } | ||
482 | } | 518 | } |
483 | } | ||
484 | 519 | ||
485 | var command = new MergeModulesCommand(); | 520 | var command = new MergeModulesCommand(); |
486 | command.FileFacades = fileFacades; | 521 | command.FileFacades = fileFacades; |
487 | command.Output = output; | 522 | command.Output = output; |
488 | command.OutputPath = this.OutputPath; | 523 | command.OutputPath = this.OutputPath; |
489 | command.SuppressedTableNames = suppressedTableNames; | 524 | command.SuppressedTableNames = suppressedTableNames; |
490 | command.Execute(); | 525 | command.Execute(); |
491 | } | 526 | } |
492 | 527 | ||
493 | if (this.Messaging.EncounteredError) | 528 | if (this.Messaging.EncounteredError) |
494 | { | 529 | { |
495 | return; | 530 | return; |
496 | } | 531 | } |
497 | 532 | ||
498 | #if TODO_FINISH_VALIDATION | 533 | #if TODO_FINISH_VALIDATION |
499 | // Validate the output if there is an MSI validator. | 534 | // Validate the output if there is an MSI validator. |
@@ -519,27 +554,29 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
519 | } | 554 | } |
520 | #endif | 555 | #endif |
521 | 556 | ||
522 | // Process uncompressed files. | 557 | // Process uncompressed files. |
523 | if (!this.Messaging.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any()) | 558 | if (!this.Messaging.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any()) |
524 | { | 559 | { |
525 | var command = new ProcessUncompressedFilesCommand(section, this.BackendHelper, this.PathResolver); | 560 | var command = new ProcessUncompressedFilesCommand(section, this.BackendHelper, this.PathResolver); |
526 | command.Compressed = compressed; | 561 | command.Compressed = compressed; |
527 | command.FileFacades = uncompressedFiles; | 562 | command.FileFacades = uncompressedFiles; |
528 | command.LayoutDirectory = layoutDirectory; | 563 | command.LayoutDirectory = layoutDirectory; |
529 | command.LongNamesInImage = longNames; | 564 | command.LongNamesInImage = longNames; |
530 | command.ResolveMedia = this.ResolveMedia; | 565 | command.ResolveMedia = this.ResolveMedia; |
531 | command.DatabasePath = this.OutputPath; | 566 | command.DatabasePath = this.OutputPath; |
532 | command.Execute(); | 567 | command.Execute(); |
533 | 568 | ||
534 | fileTransfers.AddRange(command.FileTransfers); | 569 | fileTransfers.AddRange(command.FileTransfers); |
535 | trackedFiles.AddRange(command.TrackedFiles); | 570 | trackedFiles.AddRange(command.TrackedFiles); |
571 | } | ||
572 | |||
573 | // TODO: this is not sufficient to collect all Input files (for example, it misses Binary and Icon tables). | ||
574 | trackedFiles.AddRange(fileFacades.Select(f => this.BackendHelper.TrackFile(f.SourcePath, TrackedFileType.Input, f.SourceLineNumber))); | ||
536 | } | 575 | } |
537 | 576 | ||
538 | this.Wixout = this.CreateWixout(trackedFiles, this.Intermediate, output); | 577 | this.Wixout = this.CreateWixout(trackedFiles, this.Intermediate, output); |
539 | 578 | ||
540 | this.FileTransfers = fileTransfers; | 579 | this.FileTransfers = fileTransfers; |
541 | // TODO: this is not sufficient to collect all Input files (for example, it misses Binary and Icon tables). | ||
542 | trackedFiles.AddRange(fileFacades.Select(f => this.BackendHelper.TrackFile(f.File.Source.Path, TrackedFileType.Input, f.File.SourceLineNumbers))); | ||
543 | this.TrackedFiles = trackedFiles; | 580 | this.TrackedFiles = trackedFiles; |
544 | } | 581 | } |
545 | 582 | ||
@@ -566,7 +603,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
566 | return wixout; | 603 | return wixout; |
567 | } | 604 | } |
568 | 605 | ||
569 | #if TODO_FINISH_PATCH | 606 | #if TODO_PATCHING |
570 | /// <summary> | 607 | /// <summary> |
571 | /// Copy file data between transform substorages and the patch output object | 608 | /// Copy file data between transform substorages and the patch output object |
572 | /// </summary> | 609 | /// </summary> |
@@ -936,28 +973,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
936 | /// <param name="useSubdirectory">Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files.</param> | 973 | /// <param name="useSubdirectory">Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files.</param> |
937 | private IEnumerable<ITrackedFile> GenerateDatabase(WindowsInstallerData output, TableDefinitionCollection tableDefinitions, string databaseFile, bool keepAddedColumns, bool useSubdirectory) | 974 | private IEnumerable<ITrackedFile> GenerateDatabase(WindowsInstallerData output, TableDefinitionCollection tableDefinitions, string databaseFile, bool keepAddedColumns, bool useSubdirectory) |
938 | { | 975 | { |
939 | var command = new GenerateDatabaseCommand(); | 976 | var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystemExtensions, output, databaseFile, tableDefinitions, this.IntermediateFolder, this.Codepage, keepAddedColumns, this.SuppressAddingValidationRows, useSubdirectory); |
940 | command.BackendHelper = this.BackendHelper; | ||
941 | command.Extensions = this.FileSystemExtensions; | ||
942 | command.Output = output; | ||
943 | command.OutputPath = databaseFile; | ||
944 | command.KeepAddedColumns = keepAddedColumns; | ||
945 | command.UseSubDirectory = useSubdirectory; | ||
946 | command.SuppressAddingValidationRows = this.SuppressAddingValidationRows; | ||
947 | command.TableDefinitions = tableDefinitions; | ||
948 | command.IntermediateFolder = this.IntermediateFolder; | ||
949 | command.Codepage = this.Codepage; | ||
950 | command.Execute(); | 977 | command.Execute(); |
951 | 978 | ||
952 | return command.GeneratedTemporaryFiles; | 979 | return command.GeneratedTemporaryFiles; |
953 | } | 980 | } |
954 | 981 | ||
955 | #region IDisposable Support | 982 | #region IDisposable Support |
956 | 983 | ||
957 | public void Dispose() | 984 | public void Dispose() => this.Dispose(true); |
958 | { | ||
959 | this.Dispose(true); | ||
960 | } | ||
961 | 985 | ||
962 | protected virtual void Dispose(bool disposing) | 986 | protected virtual void Dispose(bool disposing) |
963 | { | 987 | { |
@@ -972,6 +996,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
972 | } | 996 | } |
973 | } | 997 | } |
974 | 998 | ||
975 | #endregion | 999 | #endregion |
976 | } | 1000 | } |
977 | } | 1001 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs index 8757024e..ea6e4f31 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs | |||
@@ -15,24 +15,37 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
15 | 15 | ||
16 | internal class BindTransformCommand | 16 | internal class BindTransformCommand |
17 | { | 17 | { |
18 | public IEnumerable<IFileSystemExtension> Extensions { private get; set; } | 18 | public BindTransformCommand(IMessaging messaging, IBackendHelper backendHelper, IEnumerable<IFileSystemExtension> extensions, string intermediateFolder, WindowsInstallerData transform, string outputPath, TableDefinitionCollection tableDefinitions) |
19 | { | ||
20 | this.Messaging = messaging; | ||
21 | this.BackendHelper = backendHelper; | ||
22 | this.Extensions = extensions; | ||
23 | this.IntermediateFolder = intermediateFolder; | ||
24 | this.Transform = transform; | ||
25 | this.OutputPath = outputPath; | ||
26 | this.TableDefinitions = tableDefinitions; | ||
27 | } | ||
28 | |||
29 | private IMessaging Messaging { get; } | ||
19 | 30 | ||
20 | public TableDefinitionCollection TableDefinitions { private get; set; } | 31 | private IBackendHelper BackendHelper { get; } |
21 | 32 | ||
22 | public string TempFilesLocation { private get; set; } | 33 | private IEnumerable<IFileSystemExtension> Extensions { get; } |
23 | 34 | ||
24 | public WindowsInstallerData Transform { private get; set; } | 35 | private TableDefinitionCollection TableDefinitions { get; } |
25 | 36 | ||
26 | public IMessaging Messaging { private get; set; } | 37 | private string IntermediateFolder { get; } |
27 | 38 | ||
28 | public string OutputPath { private get; set; } | 39 | private WindowsInstallerData Transform { get; } |
40 | |||
41 | private string OutputPath { get; } | ||
29 | 42 | ||
30 | public void Execute() | 43 | public void Execute() |
31 | { | 44 | { |
32 | int transformFlags = 0; | 45 | var transformFlags = 0; |
33 | 46 | ||
34 | WindowsInstallerData targetOutput = new WindowsInstallerData(null); | 47 | var targetOutput = new WindowsInstallerData(null); |
35 | WindowsInstallerData updatedOutput = new WindowsInstallerData(null); | 48 | var updatedOutput = new WindowsInstallerData(null); |
36 | 49 | ||
37 | // TODO: handle added columns | 50 | // TODO: handle added columns |
38 | 51 | ||
@@ -49,8 +62,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
49 | string targetUpgradeCode = null; | 62 | string targetUpgradeCode = null; |
50 | string updatedUpgradeCode = null; | 63 | string updatedUpgradeCode = null; |
51 | 64 | ||
52 | Table propertyTable = this.Transform.Tables["Property"]; | 65 | if (this.Transform.TryGetTable("Property", out var propertyTable)) |
53 | if (null != propertyTable) | ||
54 | { | 66 | { |
55 | for (int i = propertyTable.Rows.Count - 1; i >= 0; i--) | 67 | for (int i = propertyTable.Rows.Count - 1; i >= 0; i--) |
56 | { | 68 | { |
@@ -68,18 +80,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
68 | } | 80 | } |
69 | } | 81 | } |
70 | 82 | ||
71 | Table targetSummaryInfo = targetOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]); | 83 | var targetSummaryInfo = targetOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]); |
72 | Table updatedSummaryInfo = updatedOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]); | 84 | var updatedSummaryInfo = updatedOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]); |
73 | Table targetPropertyTable = targetOutput.EnsureTable(this.TableDefinitions["Property"]); | 85 | var targetPropertyTable = targetOutput.EnsureTable(this.TableDefinitions["Property"]); |
74 | Table updatedPropertyTable = updatedOutput.EnsureTable(this.TableDefinitions["Property"]); | 86 | var updatedPropertyTable = updatedOutput.EnsureTable(this.TableDefinitions["Property"]); |
75 | 87 | ||
76 | // process special summary information values | 88 | // process special summary information values |
77 | foreach (Row row in this.Transform.Tables["_SummaryInformation"].Rows) | 89 | foreach (var row in this.Transform.Tables["_SummaryInformation"].Rows) |
78 | { | 90 | { |
79 | if ((int)SummaryInformation.Transform.CodePage == (int)row[0]) | 91 | var summaryId = row.FieldAsInteger(0); |
92 | var summaryData = row.FieldAsString(1); | ||
93 | |||
94 | if ((int)SummaryInformation.Transform.CodePage == summaryId) | ||
80 | { | 95 | { |
81 | // convert from a web name if provided | 96 | // convert from a web name if provided |
82 | string codePage = (string)row.Fields[1].Data; | 97 | var codePage = summaryData; |
83 | if (null == codePage) | 98 | if (null == codePage) |
84 | { | 99 | { |
85 | codePage = "0"; | 100 | codePage = "0"; |
@@ -89,7 +104,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
89 | codePage = Common.GetValidCodePage(codePage).ToString(CultureInfo.InvariantCulture); | 104 | codePage = Common.GetValidCodePage(codePage).ToString(CultureInfo.InvariantCulture); |
90 | } | 105 | } |
91 | 106 | ||
92 | string previousCodePage = (string)row.Fields[1].PreviousData; | 107 | var previousCodePage = row.Fields[1].PreviousData; |
93 | if (null == previousCodePage) | 108 | if (null == previousCodePage) |
94 | { | 109 | { |
95 | previousCodePage = "0"; | 110 | previousCodePage = "0"; |
@@ -99,50 +114,50 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
99 | previousCodePage = Common.GetValidCodePage(previousCodePage).ToString(CultureInfo.InvariantCulture); | 114 | previousCodePage = Common.GetValidCodePage(previousCodePage).ToString(CultureInfo.InvariantCulture); |
100 | } | 115 | } |
101 | 116 | ||
102 | Row targetCodePageRow = targetSummaryInfo.CreateRow(null); | 117 | var targetCodePageRow = targetSummaryInfo.CreateRow(null); |
103 | targetCodePageRow[0] = 1; // PID_CODEPAGE | 118 | targetCodePageRow[0] = 1; // PID_CODEPAGE |
104 | targetCodePageRow[1] = previousCodePage; | 119 | targetCodePageRow[1] = previousCodePage; |
105 | 120 | ||
106 | Row updatedCodePageRow = updatedSummaryInfo.CreateRow(null); | 121 | var updatedCodePageRow = updatedSummaryInfo.CreateRow(null); |
107 | updatedCodePageRow[0] = 1; // PID_CODEPAGE | 122 | updatedCodePageRow[0] = 1; // PID_CODEPAGE |
108 | updatedCodePageRow[1] = codePage; | 123 | updatedCodePageRow[1] = codePage; |
109 | } | 124 | } |
110 | else if ((int)SummaryInformation.Transform.TargetPlatformAndLanguage == (int)row[0] || | 125 | else if ((int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId || |
111 | (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == (int)row[0]) | 126 | (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == summaryId) |
112 | { | 127 | { |
113 | // the target language | 128 | // the target language |
114 | string[] propertyData = ((string)row[1]).Split(';'); | 129 | var propertyData = summaryData.Split(';'); |
115 | string lang = 2 == propertyData.Length ? propertyData[1] : "0"; | 130 | var lang = 2 == propertyData.Length ? propertyData[1] : "0"; |
116 | 131 | ||
117 | Table tempSummaryInfo = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == (int)row[0] ? targetSummaryInfo : updatedSummaryInfo; | 132 | var tempSummaryInfo = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId ? targetSummaryInfo : updatedSummaryInfo; |
118 | Table tempPropertyTable = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == (int)row[0] ? targetPropertyTable : updatedPropertyTable; | 133 | var tempPropertyTable = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId ? targetPropertyTable : updatedPropertyTable; |
119 | 134 | ||
120 | Row productLanguageRow = tempPropertyTable.CreateRow(null); | 135 | var productLanguageRow = tempPropertyTable.CreateRow(null); |
121 | productLanguageRow[0] = "ProductLanguage"; | 136 | productLanguageRow[0] = "ProductLanguage"; |
122 | productLanguageRow[1] = lang; | 137 | productLanguageRow[1] = lang; |
123 | 138 | ||
124 | // set the platform;language on the MSI to be generated | 139 | // set the platform;language on the MSI to be generated |
125 | Row templateRow = tempSummaryInfo.CreateRow(null); | 140 | var templateRow = tempSummaryInfo.CreateRow(null); |
126 | templateRow[0] = 7; // PID_TEMPLATE | 141 | templateRow[0] = 7; // PID_TEMPLATE |
127 | templateRow[1] = (string)row[1]; | 142 | templateRow[1] = summaryData; |
128 | } | 143 | } |
129 | else if ((int)SummaryInformation.Transform.ProductCodes == (int)row[0]) | 144 | else if ((int)SummaryInformation.Transform.ProductCodes == summaryId) |
130 | { | 145 | { |
131 | string[] propertyData = ((string)row[1]).Split(';'); | 146 | var propertyData = summaryData.Split(';'); |
132 | 147 | ||
133 | Row targetProductCodeRow = targetPropertyTable.CreateRow(null); | 148 | var targetProductCodeRow = targetPropertyTable.CreateRow(null); |
134 | targetProductCodeRow[0] = "ProductCode"; | 149 | targetProductCodeRow[0] = "ProductCode"; |
135 | targetProductCodeRow[1] = propertyData[0].Substring(0, 38); | 150 | targetProductCodeRow[1] = propertyData[0].Substring(0, 38); |
136 | 151 | ||
137 | Row targetProductVersionRow = targetPropertyTable.CreateRow(null); | 152 | var targetProductVersionRow = targetPropertyTable.CreateRow(null); |
138 | targetProductVersionRow[0] = "ProductVersion"; | 153 | targetProductVersionRow[0] = "ProductVersion"; |
139 | targetProductVersionRow[1] = propertyData[0].Substring(38); | 154 | targetProductVersionRow[1] = propertyData[0].Substring(38); |
140 | 155 | ||
141 | Row updatedProductCodeRow = updatedPropertyTable.CreateRow(null); | 156 | var updatedProductCodeRow = updatedPropertyTable.CreateRow(null); |
142 | updatedProductCodeRow[0] = "ProductCode"; | 157 | updatedProductCodeRow[0] = "ProductCode"; |
143 | updatedProductCodeRow[1] = propertyData[1].Substring(0, 38); | 158 | updatedProductCodeRow[1] = propertyData[1].Substring(0, 38); |
144 | 159 | ||
145 | Row updatedProductVersionRow = updatedPropertyTable.CreateRow(null); | 160 | var updatedProductVersionRow = updatedPropertyTable.CreateRow(null); |
146 | updatedProductVersionRow[0] = "ProductVersion"; | 161 | updatedProductVersionRow[0] = "ProductVersion"; |
147 | updatedProductVersionRow[1] = propertyData[1].Substring(38); | 162 | updatedProductVersionRow[1] = propertyData[1].Substring(38); |
148 | 163 | ||
@@ -153,7 +168,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
153 | targetUpgradeCode = propertyData[2]; | 168 | targetUpgradeCode = propertyData[2]; |
154 | if (!String.IsNullOrEmpty(targetUpgradeCode)) | 169 | if (!String.IsNullOrEmpty(targetUpgradeCode)) |
155 | { | 170 | { |
156 | Row targetUpgradeCodeRow = targetPropertyTable.CreateRow(null); | 171 | var targetUpgradeCodeRow = targetPropertyTable.CreateRow(null); |
157 | targetUpgradeCodeRow[0] = "UpgradeCode"; | 172 | targetUpgradeCodeRow[0] = "UpgradeCode"; |
158 | targetUpgradeCodeRow[1] = targetUpgradeCode; | 173 | targetUpgradeCodeRow[1] = targetUpgradeCode; |
159 | 174 | ||
@@ -167,16 +182,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
167 | 182 | ||
168 | if (!String.IsNullOrEmpty(updatedUpgradeCode)) | 183 | if (!String.IsNullOrEmpty(updatedUpgradeCode)) |
169 | { | 184 | { |
170 | Row updatedUpgradeCodeRow = updatedPropertyTable.CreateRow(null); | 185 | var updatedUpgradeCodeRow = updatedPropertyTable.CreateRow(null); |
171 | updatedUpgradeCodeRow[0] = "UpgradeCode"; | 186 | updatedUpgradeCodeRow[0] = "UpgradeCode"; |
172 | updatedUpgradeCodeRow[1] = updatedUpgradeCode; | 187 | updatedUpgradeCodeRow[1] = updatedUpgradeCode; |
173 | } | 188 | } |
174 | } | 189 | } |
175 | else if ((int)SummaryInformation.Transform.ValidationFlags == (int)row[0]) | 190 | else if ((int)SummaryInformation.Transform.ValidationFlags == summaryId) |
176 | { | 191 | { |
177 | transformFlags = Convert.ToInt32(row[1], CultureInfo.InvariantCulture); | 192 | transformFlags = Convert.ToInt32(summaryData, CultureInfo.InvariantCulture); |
178 | } | 193 | } |
179 | else if ((int)SummaryInformation.Transform.Reserved11 == (int)row[0]) | 194 | else if ((int)SummaryInformation.Transform.Reserved11 == summaryId) |
180 | { | 195 | { |
181 | // PID_LASTPRINTED should be null for transforms | 196 | // PID_LASTPRINTED should be null for transforms |
182 | row.Operation = RowOperation.None; | 197 | row.Operation = RowOperation.None; |
@@ -184,11 +199,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
184 | else | 199 | else |
185 | { | 200 | { |
186 | // add everything else as is | 201 | // add everything else as is |
187 | Row targetRow = targetSummaryInfo.CreateRow(null); | 202 | var targetRow = targetSummaryInfo.CreateRow(null); |
188 | targetRow[0] = row[0]; | 203 | targetRow[0] = row[0]; |
189 | targetRow[1] = row[1]; | 204 | targetRow[1] = row[1]; |
190 | 205 | ||
191 | Row updatedRow = updatedSummaryInfo.CreateRow(null); | 206 | var updatedRow = updatedSummaryInfo.CreateRow(null); |
192 | updatedRow[0] = row[0]; | 207 | updatedRow[0] = row[0]; |
193 | updatedRow[1] = row[1]; | 208 | updatedRow[1] = row[1]; |
194 | } | 209 | } |
@@ -205,7 +220,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
205 | 220 | ||
206 | string emptyFile = null; | 221 | string emptyFile = null; |
207 | 222 | ||
208 | foreach (Table table in this.Transform.Tables) | 223 | foreach (var table in this.Transform.Tables) |
209 | { | 224 | { |
210 | // Ignore unreal tables when building transforms except the _Stream table. | 225 | // Ignore unreal tables when building transforms except the _Stream table. |
211 | // These tables are ignored when generating the database so there is no reason | 226 | // These tables are ignored when generating the database so there is no reason |
@@ -231,20 +246,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
231 | } | 246 | } |
232 | 247 | ||
233 | // process row operations | 248 | // process row operations |
234 | foreach (Row row in table.Rows) | 249 | foreach (var row in table.Rows) |
235 | { | 250 | { |
236 | switch (row.Operation) | 251 | switch (row.Operation) |
237 | { | 252 | { |
238 | case RowOperation.Add: | 253 | case RowOperation.Add: |
239 | Table updatedTable = updatedOutput.EnsureTable(table.Definition); | 254 | var updatedTable = updatedOutput.EnsureTable(table.Definition); |
240 | updatedTable.Rows.Add(row); | 255 | updatedTable.Rows.Add(row); |
241 | continue; | 256 | continue; |
257 | |||
242 | case RowOperation.Delete: | 258 | case RowOperation.Delete: |
243 | Table targetTable = targetOutput.EnsureTable(table.Definition); | 259 | var targetTable = targetOutput.EnsureTable(table.Definition); |
244 | targetTable.Rows.Add(row); | 260 | targetTable.Rows.Add(row); |
245 | 261 | ||
246 | // fill-in non-primary key values | 262 | // fill-in non-primary key values |
247 | foreach (Field field in row.Fields) | 263 | foreach (var field in row.Fields) |
248 | { | 264 | { |
249 | if (!field.Column.PrimaryKey) | 265 | if (!field.Column.PrimaryKey) |
250 | { | 266 | { |
@@ -256,7 +272,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
256 | { | 272 | { |
257 | if (null == emptyFile) | 273 | if (null == emptyFile) |
258 | { | 274 | { |
259 | emptyFile = Path.Combine(this.TempFilesLocation, "empty"); | 275 | emptyFile = Path.Combine(this.IntermediateFolder, "empty"); |
260 | } | 276 | } |
261 | 277 | ||
262 | field.Data = emptyFile; | 278 | field.Data = emptyFile; |
@@ -273,7 +289,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
273 | // Assure that the file table's sequence is populated | 289 | // Assure that the file table's sequence is populated |
274 | if ("File" == table.Name) | 290 | if ("File" == table.Name) |
275 | { | 291 | { |
276 | foreach (Row fileRow in table.Rows) | 292 | foreach (var fileRow in table.Rows) |
277 | { | 293 | { |
278 | if (null == fileRow[7]) | 294 | if (null == fileRow[7]) |
279 | { | 295 | { |
@@ -290,60 +306,48 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
290 | } | 306 | } |
291 | 307 | ||
292 | // process modified and unmodified rows | 308 | // process modified and unmodified rows |
293 | bool modifiedRow = false; | 309 | var modifiedRow = false; |
294 | Row targetRow = new Row(null, table.Definition); | 310 | var targetRow = new Row(null, table.Definition); |
295 | Row updatedRow = row; | 311 | var updatedRow = row; |
296 | for (int i = 0; i < row.Fields.Length; i++) | 312 | for (var i = 0; i < row.Fields.Length; i++) |
297 | { | 313 | { |
298 | Field updatedField = row.Fields[i]; | 314 | var updatedField = row.Fields[i]; |
299 | 315 | ||
300 | if (updatedField.Modified) | 316 | if (updatedField.Modified) |
301 | { | 317 | { |
302 | // set a different value in the target row to ensure this value will be modified during transform generation | 318 | // set a different value in the target row to ensure this value will be modified during transform generation |
303 | if (ColumnType.Number == updatedField.Column.Type && !updatedField.Column.IsLocalizable) | 319 | if (ColumnType.Number == updatedField.Column.Type && !updatedField.Column.IsLocalizable) |
304 | { | 320 | { |
305 | if (null == updatedField.Data || 1 != (int)updatedField.Data) | 321 | var data = updatedField.AsNullableInteger(); |
306 | { | 322 | targetRow[i] = (data == 1) ? 2 : 1; |
307 | targetRow[i] = 1; | ||
308 | } | ||
309 | else | ||
310 | { | ||
311 | targetRow[i] = 2; | ||
312 | } | ||
313 | } | 323 | } |
314 | else if (ColumnType.Object == updatedField.Column.Type) | 324 | else if (ColumnType.Object == updatedField.Column.Type) |
315 | { | 325 | { |
316 | if (null == emptyFile) | 326 | if (null == emptyFile) |
317 | { | 327 | { |
318 | emptyFile = Path.Combine(this.TempFilesLocation, "empty"); | 328 | emptyFile = Path.Combine(this.IntermediateFolder, "empty"); |
319 | } | 329 | } |
320 | 330 | ||
321 | targetRow[i] = emptyFile; | 331 | targetRow[i] = emptyFile; |
322 | } | 332 | } |
323 | else | 333 | else |
324 | { | 334 | { |
325 | if ("0" != (string)updatedField.Data) | 335 | var data = updatedField.AsString(); |
326 | { | 336 | targetRow[i] = (data == "0") ? "1" : "0"; |
327 | targetRow[i] = "0"; | ||
328 | } | ||
329 | else | ||
330 | { | ||
331 | targetRow[i] = "1"; | ||
332 | } | ||
333 | } | 337 | } |
334 | 338 | ||
335 | modifiedRow = true; | 339 | modifiedRow = true; |
336 | } | 340 | } |
337 | else if (ColumnType.Object == updatedField.Column.Type) | 341 | else if (ColumnType.Object == updatedField.Column.Type) |
338 | { | 342 | { |
339 | ObjectField objectField = (ObjectField)updatedField; | 343 | var objectField = (ObjectField)updatedField; |
340 | 344 | ||
341 | // create an empty file for comparing against | 345 | // create an empty file for comparing against |
342 | if (null == objectField.PreviousData) | 346 | if (null == objectField.PreviousData) |
343 | { | 347 | { |
344 | if (null == emptyFile) | 348 | if (null == emptyFile) |
345 | { | 349 | { |
346 | emptyFile = Path.Combine(this.TempFilesLocation, "empty"); | 350 | emptyFile = Path.Combine(this.IntermediateFolder, "empty"); |
347 | } | 351 | } |
348 | 352 | ||
349 | targetRow[i] = emptyFile; | 353 | targetRow[i] = emptyFile; |
@@ -372,10 +376,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
372 | "ProductVersion" == (string)row[0] || | 376 | "ProductVersion" == (string)row[0] || |
373 | "UpgradeCode" == (string)row[0]))) | 377 | "UpgradeCode" == (string)row[0]))) |
374 | { | 378 | { |
375 | Table targetTable = targetOutput.EnsureTable(table.Definition); | 379 | var targetTable = targetOutput.EnsureTable(table.Definition); |
376 | targetTable.Rows.Add(targetRow); | 380 | targetTable.Rows.Add(targetRow); |
377 | 381 | ||
378 | Table updatedTable = updatedOutput.EnsureTable(table.Definition); | 382 | var updatedTable = updatedOutput.EnsureTable(table.Definition); |
379 | updatedTable.Rows.Add(updatedRow); | 383 | updatedTable.Rows.Add(updatedRow); |
380 | } | 384 | } |
381 | } | 385 | } |
@@ -392,38 +396,36 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
392 | return; | 396 | return; |
393 | } | 397 | } |
394 | 398 | ||
395 | string transformFileName = Path.GetFileNameWithoutExtension(this.OutputPath); | 399 | var transformFileName = Path.GetFileNameWithoutExtension(this.OutputPath); |
396 | string targetDatabaseFile = Path.Combine(this.TempFilesLocation, String.Concat(transformFileName, "_target.msi")); | 400 | var targetDatabaseFile = Path.Combine(this.IntermediateFolder, String.Concat(transformFileName, "_target.msi")); |
397 | string updatedDatabaseFile = Path.Combine(this.TempFilesLocation, String.Concat(transformFileName, "_updated.msi")); | 401 | var updatedDatabaseFile = Path.Combine(this.IntermediateFolder, String.Concat(transformFileName, "_updated.msi")); |
398 | 402 | ||
399 | try | 403 | try |
400 | { | 404 | { |
401 | if (!String.IsNullOrEmpty(emptyFile)) | 405 | if (!String.IsNullOrEmpty(emptyFile)) |
402 | { | 406 | { |
403 | using (FileStream fileStream = File.Create(emptyFile)) | 407 | using (var fileStream = File.Create(emptyFile)) |
404 | { | 408 | { |
405 | } | 409 | } |
406 | } | 410 | } |
407 | 411 | ||
408 | this.GenerateDatabase(targetOutput, targetDatabaseFile, false); | 412 | this.GenerateDatabase(targetOutput, targetDatabaseFile, keepAddedColumns: false); |
409 | this.GenerateDatabase(updatedOutput, updatedDatabaseFile, true); | 413 | this.GenerateDatabase(updatedOutput, updatedDatabaseFile, keepAddedColumns: true); |
410 | 414 | ||
411 | // make sure the directory exists | 415 | // make sure the directory exists |
412 | Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); | 416 | Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); |
413 | 417 | ||
414 | // create the transform file | 418 | // create the transform file |
415 | using (Database targetDatabase = new Database(targetDatabaseFile, OpenDatabase.ReadOnly)) | 419 | using (var targetDatabase = new Database(targetDatabaseFile, OpenDatabase.ReadOnly)) |
420 | using (var updatedDatabase = new Database(updatedDatabaseFile, OpenDatabase.ReadOnly)) | ||
416 | { | 421 | { |
417 | using (Database updatedDatabase = new Database(updatedDatabaseFile, OpenDatabase.ReadOnly)) | 422 | if (updatedDatabase.GenerateTransform(targetDatabase, this.OutputPath)) |
418 | { | 423 | { |
419 | if (updatedDatabase.GenerateTransform(targetDatabase, this.OutputPath)) | 424 | updatedDatabase.CreateTransformSummaryInfo(targetDatabase, this.OutputPath, (TransformErrorConditions)(transformFlags & 0xFFFF), (TransformValidations)((transformFlags >> 16) & 0xFFFF)); |
420 | { | 425 | } |
421 | updatedDatabase.CreateTransformSummaryInfo(targetDatabase, this.OutputPath, (TransformErrorConditions)(transformFlags & 0xFFFF), (TransformValidations)((transformFlags >> 16) & 0xFFFF)); | 426 | else |
422 | } | 427 | { |
423 | else | 428 | this.Messaging.Write(ErrorMessages.NoDifferencesInTransform(this.Transform.SourceLineNumbers)); |
424 | { | ||
425 | this.Messaging.Write(ErrorMessages.NoDifferencesInTransform(this.Transform.SourceLineNumbers)); | ||
426 | } | ||
427 | } | 429 | } |
428 | } | 430 | } |
429 | } | 431 | } |
@@ -458,16 +460,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
458 | 460 | ||
459 | private void GenerateDatabase(WindowsInstallerData output, string outputPath, bool keepAddedColumns) | 461 | private void GenerateDatabase(WindowsInstallerData output, string outputPath, bool keepAddedColumns) |
460 | { | 462 | { |
461 | var command = new GenerateDatabaseCommand(); | 463 | var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.Extensions, output, outputPath, this.TableDefinitions, this.IntermediateFolder, codepage: -1, keepAddedColumns, suppressAddingValidationRows: true, useSubdirectory: true ); |
462 | command.Codepage = output.Codepage; | ||
463 | command.Extensions = this.Extensions; | ||
464 | command.KeepAddedColumns = keepAddedColumns; | ||
465 | command.Output = output; | ||
466 | command.OutputPath = outputPath; | ||
467 | command.TableDefinitions = this.TableDefinitions; | ||
468 | command.IntermediateFolder = this.TempFilesLocation; | ||
469 | command.SuppressAddingValidationRows = true; | ||
470 | command.UseSubDirectory = true; | ||
471 | command.Execute(); | 464 | command.Execute(); |
472 | } | 465 | } |
473 | } | 466 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs index bf1140d8..ec4e0818 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs | |||
@@ -152,7 +152,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
152 | 152 | ||
153 | foreach (FileFacade facade in cabinetWorkItem.FileFacades) // No other easy way than looping to get the only row | 153 | foreach (FileFacade facade in cabinetWorkItem.FileFacades) // No other easy way than looping to get the only row |
154 | { | 154 | { |
155 | if ((ulong)facade.File.FileSize >= maxPreCompressedSizeInBytes) | 155 | if ((ulong)facade.FileSize >= maxPreCompressedSizeInBytes) |
156 | { | 156 | { |
157 | // If file is larger than MaximumUncompressedFileSize set Maximum Cabinet Size for Cabinet Splitting | 157 | // If file is larger than MaximumUncompressedFileSize set Maximum Cabinet Size for Cabinet Splitting |
158 | maxCabinetSize = this.MaximumCabinetSizeForLargeFileSplitting; | 158 | maxCabinetSize = this.MaximumCabinetSizeForLargeFileSplitting; |
@@ -166,8 +166,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
166 | 166 | ||
167 | var files = cabinetWorkItem.FileFacades | 167 | var files = cabinetWorkItem.FileFacades |
168 | .Select(facade => facade.Hash == null ? | 168 | .Select(facade => facade.Hash == null ? |
169 | new CabinetCompressFile(facade.File.Source.Path, facade.File.Id.Id) : | 169 | new CabinetCompressFile(facade.SourcePath, facade.Id) : |
170 | new CabinetCompressFile(facade.File.Source.Path, facade.File.Id.Id, facade.Hash.HashPart1, facade.Hash.HashPart2, facade.Hash.HashPart3, facade.Hash.HashPart4)) | 170 | new CabinetCompressFile(facade.SourcePath, facade.Id, facade.Hash.HashPart1, facade.Hash.HashPart2, facade.Hash.HashPart3, facade.Hash.HashPart4)) |
171 | .ToList(); | 171 | .ToList(); |
172 | 172 | ||
173 | var cab = new Cabinet(cabinetPath); | 173 | var cab = new Cabinet(cabinetPath); |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CabinetResolver.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetResolver.cs index 3fa3f3a0..79b1c619 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CabinetResolver.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetResolver.cs | |||
@@ -112,8 +112,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
112 | private IBindFileWithPath CreateBindFileWithPath(FileFacade facade) | 112 | private IBindFileWithPath CreateBindFileWithPath(FileFacade facade) |
113 | { | 113 | { |
114 | var result = this.ServiceProvider.GetService<IBindFileWithPath>(); | 114 | var result = this.ServiceProvider.GetService<IBindFileWithPath>(); |
115 | result.Id = facade.File.Id.Id; | 115 | result.Id = facade.Id; |
116 | result.Path = facade.File.Source.Path; | 116 | result.Path = facade.SourcePath; |
117 | 117 | ||
118 | return result; | 118 | return result; |
119 | } | 119 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs index 107f3208..0dcce61b 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs | |||
@@ -1,12 +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. | 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 | 2 | ||
3 | #if DELETE | ||
4 | |||
3 | namespace WixToolset.Core.WindowsInstaller.Bind | 5 | namespace WixToolset.Core.WindowsInstaller.Bind |
4 | { | 6 | { |
5 | using System; | 7 | using System; |
6 | using System.Collections.Generic; | 8 | using System.Collections.Generic; |
7 | using System.Diagnostics; | 9 | using System.Diagnostics; |
10 | using System.IO; | ||
11 | using System.Linq; | ||
8 | using WixToolset.Core.Bind; | 12 | using WixToolset.Core.Bind; |
9 | using WixToolset.Core.Native; | ||
10 | using WixToolset.Data; | 13 | using WixToolset.Data; |
11 | using WixToolset.Data.Tuples; | 14 | using WixToolset.Data.Tuples; |
12 | using WixToolset.Data.WindowsInstaller; | 15 | using WixToolset.Data.WindowsInstaller; |
@@ -16,15 +19,23 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
16 | 19 | ||
17 | internal class CopyTransformDataCommand | 20 | internal class CopyTransformDataCommand |
18 | { | 21 | { |
19 | public bool CopyOutFileRows { private get; set; } | 22 | public CopyTransformDataCommand(IMessaging messaging, WindowsInstallerData output, TableDefinitionCollection tableDefinitions, bool copyOutFileRows) |
23 | { | ||
24 | this.Messaging = messaging; | ||
25 | this.Output = output; | ||
26 | this.TableDefinitions = tableDefinitions; | ||
27 | this.CopyOutFileRows = copyOutFileRows; | ||
28 | } | ||
20 | 29 | ||
21 | public IEnumerable<IFileSystemExtension> Extensions { private get; set; } | 30 | private bool CopyOutFileRows { get; } |
22 | 31 | ||
23 | public IMessaging Messaging { private get; set; } | 32 | public IEnumerable<IFileSystemExtension> Extensions { get; } |
24 | 33 | ||
25 | public WindowsInstallerData Output { private get; set; } | 34 | private IMessaging Messaging { get; } |
26 | 35 | ||
27 | public TableDefinitionCollection TableDefinitions { private get; set; } | 36 | private WindowsInstallerData Output { get; } |
37 | |||
38 | private TableDefinitionCollection TableDefinitions { get; } | ||
28 | 39 | ||
29 | public IEnumerable<FileFacade> FileFacades { get; private set; } | 40 | public IEnumerable<FileFacade> FileFacades { get; private set; } |
30 | 41 | ||
@@ -32,18 +43,17 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
32 | { | 43 | { |
33 | Debug.Assert(OutputType.Patch != this.Output.Type); | 44 | Debug.Assert(OutputType.Patch != this.Output.Type); |
34 | 45 | ||
35 | List<FileFacade> allFileRows = this.CopyOutFileRows ? new List<FileFacade>() : null; | 46 | var allFileRows = this.CopyOutFileRows ? new List<FileFacade>() : null; |
36 | 47 | ||
37 | #if REVISIT_FOR_PATCHING // TODO: Fix this patching related code to work correctly with FileFacades. | 48 | var copyToPatch = (allFileRows != null); |
38 | bool copyToPatch = (allFileRows != null); | 49 | var copyFromPatch = !copyToPatch; |
39 | bool copyFromPatch = !copyToPatch; | ||
40 | 50 | ||
41 | RowDictionary<MediaRow> patchMediaRows = new RowDictionary<MediaRow>(); | 51 | var patchMediaRows = new RowDictionary<MediaRow>(); |
42 | 52 | ||
43 | Dictionary<int, RowDictionary<WixFileRow>> patchMediaFileRows = new Dictionary<int, RowDictionary<WixFileRow>>(); | 53 | var patchMediaFileRows = new Dictionary<int, RowDictionary<WixFileRow>>(); |
44 | 54 | ||
45 | Table patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]); | 55 | var patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]); |
46 | Table patchFileTable = this.Output.EnsureTable(this.TableDefinitions["WixFile"]); | 56 | var patchFileTable = this.Output.EnsureTable(this.TableDefinitions["WixFile"]); |
47 | 57 | ||
48 | if (copyFromPatch) | 58 | if (copyFromPatch) |
49 | { | 59 | { |
@@ -51,8 +61,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
51 | foreach (WixFileRow patchFileRow in patchFileTable.Rows) | 61 | foreach (WixFileRow patchFileRow in patchFileTable.Rows) |
52 | { | 62 | { |
53 | int diskId = patchFileRow.DiskId; | 63 | int diskId = patchFileRow.DiskId; |
54 | RowDictionary<WixFileRow> mediaFileRows; | 64 | if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows)) |
55 | if (!patchMediaFileRows.TryGetValue(diskId, out mediaFileRows)) | ||
56 | { | 65 | { |
57 | mediaFileRows = new RowDictionary<WixFileRow>(); | 66 | mediaFileRows = new RowDictionary<WixFileRow>(); |
58 | patchMediaFileRows.Add(diskId, mediaFileRows); | 67 | patchMediaFileRows.Add(diskId, mediaFileRows); |
@@ -61,24 +70,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
61 | mediaFileRows.Add(patchFileRow); | 70 | mediaFileRows.Add(patchFileRow); |
62 | } | 71 | } |
63 | 72 | ||
64 | Table patchMediaTable = this.Output.EnsureTable(this.TableDefinitions["Media"]); | 73 | var patchMediaTable = this.Output.EnsureTable(this.TableDefinitions["Media"]); |
65 | patchMediaRows = new RowDictionary<MediaRow>(patchMediaTable); | 74 | patchMediaRows = new RowDictionary<MediaRow>(patchMediaTable); |
66 | } | 75 | } |
67 | 76 | ||
68 | // index paired transforms | 77 | // Index paired transforms by name without the "#" prefix. |
69 | Dictionary<string, Output> pairedTransforms = new Dictionary<string, Output>(); | 78 | var pairedTransforms = this.Output.SubStorages.Where(s => s.Name.StartsWith("#")).ToDictionary(s => s.Name.Substring(1), s => s.Data); |
70 | foreach (SubStorage substorage in this.Output.SubStorages) | 79 | //Dictionary<string, Output> pairedTransforms = new Dictionary<string, Output>(); |
71 | { | 80 | //foreach (SubStorage substorage in this.Output.SubStorages) |
72 | if (substorage.Name.StartsWith("#")) | 81 | //{ |
73 | { | 82 | // if (substorage.Name.StartsWith("#")) |
74 | pairedTransforms.Add(substorage.Name.Substring(1), substorage.Data); | 83 | // { |
75 | } | 84 | // pairedTransforms.Add(substorage.Name.Substring(1), substorage.Data); |
76 | } | 85 | // } |
86 | //} | ||
77 | 87 | ||
78 | try | 88 | try |
79 | { | 89 | { |
80 | // copy File bind data into substorages | 90 | // Copy File bind data into substorages |
81 | foreach (SubStorage substorage in this.Output.SubStorages) | 91 | foreach (var substorage in this.Output.SubStorages) |
82 | { | 92 | { |
83 | if (substorage.Name.StartsWith("#")) | 93 | if (substorage.Name.StartsWith("#")) |
84 | { | 94 | { |
@@ -86,25 +96,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
86 | continue; | 96 | continue; |
87 | } | 97 | } |
88 | 98 | ||
89 | Output mainTransform = substorage.Data; | 99 | var mainTransform = substorage.Data; |
90 | Table mainWixFileTable = mainTransform.Tables["WixFile"]; | 100 | var mainWixFileTable = mainTransform.Tables["WixFile"]; |
91 | Table mainMsiFileHashTable = mainTransform.Tables["MsiFileHash"]; | 101 | var mainMsiFileHashTable = mainTransform.Tables["MsiFileHash"]; |
92 | 102 | ||
93 | this.FileManagerCore.ActiveSubStorage = substorage; | 103 | this.FileManagerCore.ActiveSubStorage = substorage; |
94 | 104 | ||
95 | RowDictionary<WixFileRow> mainWixFiles = new RowDictionary<WixFileRow>(mainWixFileTable); | 105 | var mainWixFiles = new RowDictionary<WixFileRow>(mainWixFileTable); |
96 | RowDictionary<Row> mainMsiFileHashIndex = new RowDictionary<Row>(); | 106 | var mainMsiFileHashIndex = new RowDictionary<Row>(); |
97 | 107 | ||
98 | Table mainFileTable = mainTransform.Tables["File"]; | 108 | var mainFileTable = mainTransform.Tables["File"]; |
99 | Output pairedTransform = (Output)pairedTransforms[substorage.Name]; | 109 | var pairedTransform = pairedTransforms[substorage.Name]; |
100 | 110 | ||
101 | // copy Media.LastSequence and index the MsiFileHash table if it exists. | 111 | // copy Media.LastSequence and index the MsiFileHash table if it exists. |
102 | if (copyFromPatch) | 112 | if (copyFromPatch) |
103 | { | 113 | { |
104 | Table pairedMediaTable = pairedTransform.Tables["Media"]; | 114 | var pairedMediaTable = pairedTransform.Tables["Media"]; |
105 | foreach (MediaRow pairedMediaRow in pairedMediaTable.Rows) | 115 | foreach (MediaRow pairedMediaRow in pairedMediaTable.Rows) |
106 | { | 116 | { |
107 | MediaRow patchMediaRow = patchMediaRows.Get(pairedMediaRow.DiskId); | 117 | var patchMediaRow = patchMediaRows.Get(pairedMediaRow.DiskId); |
108 | pairedMediaRow.Fields[1] = patchMediaRow.Fields[1]; | 118 | pairedMediaRow.Fields[1] = patchMediaRow.Fields[1]; |
109 | } | 119 | } |
110 | 120 | ||
@@ -118,8 +128,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
118 | } | 128 | } |
119 | 129 | ||
120 | // Index File table of pairedTransform | 130 | // Index File table of pairedTransform |
121 | Table pairedFileTable = pairedTransform.Tables["File"]; | 131 | var pairedFileTable = pairedTransform.Tables["File"]; |
122 | RowDictionary<FileRow> pairedFileRows = new RowDictionary<FileRow>(pairedFileTable); | 132 | var pairedFileRows = new RowDictionary<FileRow>(pairedFileTable); |
123 | 133 | ||
124 | if (null != mainFileTable) | 134 | if (null != mainFileTable) |
125 | { | 135 | { |
@@ -140,12 +150,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
140 | continue; | 150 | continue; |
141 | } | 151 | } |
142 | 152 | ||
143 | WixFileRow mainWixFileRow = mainWixFiles.Get(mainFileRow.File); | 153 | var mainWixFileRow = mainWixFiles.Get(mainFileRow.File); |
144 | 154 | ||
145 | if (copyToPatch) // when copying to the patch, we need compare the underlying files and include all file changes. | 155 | if (copyToPatch) // when copying to the patch, we need compare the underlying files and include all file changes. |
146 | { | 156 | { |
147 | ObjectField objectField = (ObjectField)mainWixFileRow.Fields[6]; | 157 | var objectField = (ObjectField)mainWixFileRow.Fields[6]; |
148 | FileRow pairedFileRow = pairedFileRows.Get(mainFileRow.File); | 158 | var pairedFileRow = pairedFileRows.Get(mainFileRow.File); |
149 | 159 | ||
150 | // If the file is new, we always need to add it to the patch. | 160 | // If the file is new, we always need to add it to the patch. |
151 | if (mainFileRow.Operation != RowOperation.Add) | 161 | if (mainFileRow.Operation != RowOperation.Add) |
@@ -169,8 +179,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
169 | if (null != pairedFileRow) | 179 | if (null != pairedFileRow) |
170 | { | 180 | { |
171 | // Always patch-added, but never non-compressed. | 181 | // Always patch-added, but never non-compressed. |
172 | pairedFileRow.Attributes |= MsiInterop.MsidbFileAttributesPatchAdded; | 182 | pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded; |
173 | pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; | 183 | pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed; |
174 | pairedFileRow.Fields[6].Modified = true; | 184 | pairedFileRow.Fields[6].Modified = true; |
175 | pairedFileRow.Operation = RowOperation.Modify; | 185 | pairedFileRow.Operation = RowOperation.Modify; |
176 | } | 186 | } |
@@ -179,14 +189,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
179 | { | 189 | { |
180 | // The File is same. We need mark all the attributes as unchanged. | 190 | // The File is same. We need mark all the attributes as unchanged. |
181 | mainFileRow.Operation = RowOperation.None; | 191 | mainFileRow.Operation = RowOperation.None; |
182 | foreach (Field field in mainFileRow.Fields) | 192 | foreach (var field in mainFileRow.Fields) |
183 | { | 193 | { |
184 | field.Modified = false; | 194 | field.Modified = false; |
185 | } | 195 | } |
186 | 196 | ||
187 | if (null != pairedFileRow) | 197 | if (null != pairedFileRow) |
188 | { | 198 | { |
189 | pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesPatchAdded; | 199 | pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesPatchAdded; |
190 | pairedFileRow.Fields[6].Modified = false; | 200 | pairedFileRow.Fields[6].Modified = false; |
191 | pairedFileRow.Operation = RowOperation.None; | 201 | pairedFileRow.Operation = RowOperation.None; |
192 | } | 202 | } |
@@ -197,8 +207,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
197 | else if (null != pairedFileRow) // RowOperation.Add | 207 | else if (null != pairedFileRow) // RowOperation.Add |
198 | { | 208 | { |
199 | // Always patch-added, but never non-compressed. | 209 | // Always patch-added, but never non-compressed. |
200 | pairedFileRow.Attributes |= MsiInterop.MsidbFileAttributesPatchAdded; | 210 | pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded; |
201 | pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; | 211 | pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed; |
202 | pairedFileRow.Fields[6].Modified = true; | 212 | pairedFileRow.Fields[6].Modified = true; |
203 | pairedFileRow.Operation = RowOperation.Add; | 213 | pairedFileRow.Operation = RowOperation.Add; |
204 | } | 214 | } |
@@ -207,20 +217,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
207 | // index patch files by diskId+fileId | 217 | // index patch files by diskId+fileId |
208 | int diskId = mainWixFileRow.DiskId; | 218 | int diskId = mainWixFileRow.DiskId; |
209 | 219 | ||
210 | RowDictionary<WixFileRow> mediaFileRows; | 220 | if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows)) |
211 | if (!patchMediaFileRows.TryGetValue(diskId, out mediaFileRows)) | ||
212 | { | 221 | { |
213 | mediaFileRows = new RowDictionary<WixFileRow>(); | 222 | mediaFileRows = new RowDictionary<WixFileRow>(); |
214 | patchMediaFileRows.Add(diskId, mediaFileRows); | 223 | patchMediaFileRows.Add(diskId, mediaFileRows); |
215 | } | 224 | } |
216 | 225 | ||
217 | string fileId = mainFileRow.File; | 226 | var fileId = mainFileRow.File; |
218 | WixFileRow patchFileRow = mediaFileRows.Get(fileId); | 227 | var patchFileRow = mediaFileRows.Get(fileId); |
219 | if (copyToPatch) | 228 | if (copyToPatch) |
220 | { | 229 | { |
221 | if (null == patchFileRow) | 230 | if (null == patchFileRow) |
222 | { | 231 | { |
223 | FileRow patchActualFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); | 232 | var patchActualFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); |
224 | patchActualFileRow.CopyFrom(mainFileRow); | 233 | patchActualFileRow.CopyFrom(mainFileRow); |
225 | 234 | ||
226 | patchFileRow = (WixFileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); | 235 | patchFileRow = (WixFileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); |
@@ -237,7 +246,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
237 | // make sure Source is same. Otherwise we are silently ignoring a file. | 246 | // make sure Source is same. Otherwise we are silently ignoring a file. |
238 | if (0 != String.Compare(patchFileRow.Source, mainWixFileRow.Source, StringComparison.OrdinalIgnoreCase)) | 247 | if (0 != String.Compare(patchFileRow.Source, mainWixFileRow.Source, StringComparison.OrdinalIgnoreCase)) |
239 | { | 248 | { |
240 | Messaging.Instance.OnMessage(WixErrors.SameFileIdDifferentSource(mainFileRow.SourceLineNumbers, fileId, patchFileRow.Source, mainWixFileRow.Source)); | 249 | this.Messaging.Write(ErrorMessages.SameFileIdDifferentSource(mainFileRow.SourceLineNumbers, fileId, patchFileRow.Source, mainWixFileRow.Source)); |
241 | } | 250 | } |
242 | 251 | ||
243 | // capture the previous file versions (and associated data) from this targeted instance of the baseline into the current filerow. | 252 | // capture the previous file versions (and associated data) from this targeted instance of the baseline into the current filerow. |
@@ -249,11 +258,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
249 | // copy data from the patch back to the transform | 258 | // copy data from the patch back to the transform |
250 | if (null != patchFileRow) | 259 | if (null != patchFileRow) |
251 | { | 260 | { |
252 | FileRow pairedFileRow = (FileRow)pairedFileRows.Get(fileId); | 261 | var pairedFileRow = pairedFileRows.Get(fileId); |
253 | for (int i = 0; i < patchFileRow.Fields.Length; i++) | 262 | for (var i = 0; i < patchFileRow.Fields.Length; i++) |
254 | { | 263 | { |
255 | string patchValue = patchFileRow[i] == null ? "" : patchFileRow[i].ToString(); | 264 | var patchValue = patchFileRow[i] == null ? String.Empty : patchFileRow.FieldAsString(i); |
256 | string mainValue = mainFileRow[i] == null ? "" : mainFileRow[i].ToString(); | 265 | var mainValue = mainFileRow[i] == null ? String.Empty : mainFileRow.FieldAsString(i); |
257 | 266 | ||
258 | if (1 == i) | 267 | if (1 == i) |
259 | { | 268 | { |
@@ -298,17 +307,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
298 | } | 307 | } |
299 | 308 | ||
300 | // copy MsiFileHash row for this File | 309 | // copy MsiFileHash row for this File |
301 | Row patchHashRow; | 310 | if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out var patchHashRow)) |
302 | if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out patchHashRow)) | ||
303 | { | 311 | { |
304 | patchHashRow = patchFileRow.Hash; | 312 | patchHashRow = patchFileRow.Hash; |
305 | } | 313 | } |
306 | 314 | ||
307 | if (null != patchHashRow) | 315 | if (null != patchHashRow) |
308 | { | 316 | { |
309 | Table mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]); | 317 | var mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]); |
310 | Row mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers); | 318 | var mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers); |
311 | for (int i = 0; i < patchHashRow.Fields.Length; i++) | 319 | for (var i = 0; i < patchHashRow.Fields.Length; i++) |
312 | { | 320 | { |
313 | mainHashRow[i] = patchHashRow[i]; | 321 | mainHashRow[i] = patchHashRow[i]; |
314 | if (i > 1) | 322 | if (i > 1) |
@@ -326,12 +334,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
326 | List<Row> patchAssemblyNameRows = patchFileRow.AssemblyNames; | 334 | List<Row> patchAssemblyNameRows = patchFileRow.AssemblyNames; |
327 | if (null != patchAssemblyNameRows) | 335 | if (null != patchAssemblyNameRows) |
328 | { | 336 | { |
329 | Table mainAssemblyNameTable = mainTransform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); | 337 | var mainAssemblyNameTable = mainTransform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); |
330 | foreach (Row patchAssemblyNameRow in patchAssemblyNameRows) | 338 | foreach (var patchAssemblyNameRow in patchAssemblyNameRows) |
331 | { | 339 | { |
332 | // Copy if there isn't an identical modified/added row already in the transform. | 340 | // Copy if there isn't an identical modified/added row already in the transform. |
333 | bool foundMatchingModifiedRow = false; | 341 | var foundMatchingModifiedRow = false; |
334 | foreach (Row mainAssemblyNameRow in mainAssemblyNameTable.Rows) | 342 | foreach (var mainAssemblyNameRow in mainAssemblyNameTable.Rows) |
335 | { | 343 | { |
336 | if (RowOperation.None != mainAssemblyNameRow.Operation && mainAssemblyNameRow.GetPrimaryKey('/').Equals(patchAssemblyNameRow.GetPrimaryKey('/'))) | 344 | if (RowOperation.None != mainAssemblyNameRow.Operation && mainAssemblyNameRow.GetPrimaryKey('/').Equals(patchAssemblyNameRow.GetPrimaryKey('/'))) |
337 | { | 345 | { |
@@ -342,8 +350,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
342 | 350 | ||
343 | if (!foundMatchingModifiedRow) | 351 | if (!foundMatchingModifiedRow) |
344 | { | 352 | { |
345 | Row mainAssemblyNameRow = mainAssemblyNameTable.CreateRow(mainFileRow.SourceLineNumbers); | 353 | var mainAssemblyNameRow = mainAssemblyNameTable.CreateRow(mainFileRow.SourceLineNumbers); |
346 | for (int i = 0; i < patchAssemblyNameRow.Fields.Length; i++) | 354 | for (var i = 0; i < patchAssemblyNameRow.Fields.Length; i++) |
347 | { | 355 | { |
348 | mainAssemblyNameRow[i] = patchAssemblyNameRow[i]; | 356 | mainAssemblyNameRow[i] = patchAssemblyNameRow[i]; |
349 | } | 357 | } |
@@ -359,34 +367,36 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
359 | if (null != patchFileRow.Patch) | 367 | if (null != patchFileRow.Patch) |
360 | { | 368 | { |
361 | // Add the PatchFiles action automatically to the AdminExecuteSequence and InstallExecuteSequence tables. | 369 | // Add the PatchFiles action automatically to the AdminExecuteSequence and InstallExecuteSequence tables. |
362 | AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow); | 370 | this.AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow); |
363 | AddPatchFilesActionToSequenceTable(SequenceTable.InstallExecuteSequence, mainTransform, pairedTransform, mainFileRow); | 371 | this.AddPatchFilesActionToSequenceTable(SequenceTable.InstallExecuteSequence, mainTransform, pairedTransform, mainFileRow); |
364 | 372 | ||
365 | // Add to Patch table | 373 | // Add to Patch table |
366 | Table patchTable = pairedTransform.EnsureTable(this.TableDefinitions["Patch"]); | 374 | var patchTable = pairedTransform.EnsureTable(this.TableDefinitions["Patch"]); |
367 | if (0 == patchTable.Rows.Count) | 375 | if (0 == patchTable.Rows.Count) |
368 | { | 376 | { |
369 | patchTable.Operation = TableOperation.Add; | 377 | patchTable.Operation = TableOperation.Add; |
370 | } | 378 | } |
371 | 379 | ||
372 | Row patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers); | 380 | var patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers); |
373 | patchRow[0] = patchFileRow.File; | 381 | patchRow[0] = patchFileRow.File; |
374 | patchRow[1] = patchFileRow.Sequence; | 382 | patchRow[1] = patchFileRow.Sequence; |
375 | 383 | ||
376 | FileInfo patchFile = new FileInfo(patchFileRow.Source); | 384 | var patchFile = new FileInfo(patchFileRow.Source); |
377 | patchRow[2] = (int)patchFile.Length; | 385 | patchRow[2] = (int)patchFile.Length; |
378 | patchRow[3] = 0 == (PatchAttributeType.AllowIgnoreOnError & patchFileRow.PatchAttributes) ? 0 : 1; | 386 | patchRow[3] = 0 == (PatchAttributeType.AllowIgnoreOnError & patchFileRow.PatchAttributes) ? 0 : 1; |
379 | 387 | ||
380 | string streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1]; | 388 | var streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1]; |
381 | if (MsiInterop.MsiMaxStreamNameLength < streamName.Length) | 389 | if (Msi.MsiInterop.MsiMaxStreamNameLength < streamName.Length) |
382 | { | 390 | { |
383 | streamName = "_" + Guid.NewGuid().ToString("D").ToUpperInvariant().Replace('-', '_'); | 391 | streamName = "_" + Guid.NewGuid().ToString("D").ToUpperInvariant().Replace('-', '_'); |
384 | Table patchHeadersTable = pairedTransform.EnsureTable(this.TableDefinitions["MsiPatchHeaders"]); | 392 | |
393 | var patchHeadersTable = pairedTransform.EnsureTable(this.TableDefinitions["MsiPatchHeaders"]); | ||
385 | if (0 == patchHeadersTable.Rows.Count) | 394 | if (0 == patchHeadersTable.Rows.Count) |
386 | { | 395 | { |
387 | patchHeadersTable.Operation = TableOperation.Add; | 396 | patchHeadersTable.Operation = TableOperation.Add; |
388 | } | 397 | } |
389 | Row patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers); | 398 | |
399 | var patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
390 | patchHeadersRow[0] = streamName; | 400 | patchHeadersRow[0] = streamName; |
391 | patchHeadersRow[1] = patchFileRow.Patch; | 401 | patchHeadersRow[1] = patchFileRow.Patch; |
392 | patchRow[5] = streamName; | 402 | patchRow[5] = streamName; |
@@ -420,7 +430,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
420 | { | 430 | { |
421 | this.FileManagerCore.ActiveSubStorage = null; | 431 | this.FileManagerCore.ActiveSubStorage = null; |
422 | } | 432 | } |
423 | #endif | 433 | |
424 | this.FileFacades = allFileRows; | 434 | this.FileFacades = allFileRows; |
425 | } | 435 | } |
426 | 436 | ||
@@ -509,17 +519,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
509 | foreach (var row in sequenceTable.Rows) | 519 | foreach (var row in sequenceTable.Rows) |
510 | { | 520 | { |
511 | var actionName = row.FieldAsString(0); | 521 | var actionName = row.FieldAsString(0); |
512 | if (String.Equals("PatchFiles", actionName, StringComparison.Ordinal)) | 522 | switch (actionName) |
513 | { | ||
514 | hasPatchFilesAction = true; | ||
515 | } | ||
516 | else if (String.Equals("InstallFiles", actionName, StringComparison.Ordinal)) | ||
517 | { | 523 | { |
518 | installFilesSequence = row.FieldAsInteger(2); | 524 | case "PatchFiles": |
519 | } | 525 | hasPatchFilesAction = true; |
520 | else if (String.Equals("DuplicateFiles", actionName, StringComparison.Ordinal)) | 526 | break; |
521 | { | 527 | |
522 | duplicateFilesSequence = row.FieldAsInteger(2); | 528 | case "InstallFiles": |
529 | installFilesSequence = row.FieldAsInteger(2); | ||
530 | break; | ||
531 | |||
532 | case "DuplicateFiles": | ||
533 | duplicateFilesSequence = row.FieldAsInteger(2); | ||
534 | break; | ||
523 | } | 535 | } |
524 | } | 536 | } |
525 | } | 537 | } |
@@ -531,8 +543,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
531 | /// <param name="output">The output to validate.</param> | 543 | /// <param name="output">The output to validate.</param> |
532 | private void ValidateFileRowChanges(WindowsInstallerData transform) | 544 | private void ValidateFileRowChanges(WindowsInstallerData transform) |
533 | { | 545 | { |
534 | Table componentTable = transform.Tables["Component"]; | 546 | var componentTable = transform.Tables["Component"]; |
535 | Table fileTable = transform.Tables["File"]; | 547 | var fileTable = transform.Tables["File"]; |
536 | 548 | ||
537 | // There's no sense validating keypaths if the transform has no component or file table | 549 | // There's no sense validating keypaths if the transform has no component or file table |
538 | if (componentTable == null || fileTable == null) | 550 | if (componentTable == null || fileTable == null) |
@@ -540,31 +552,31 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
540 | return; | 552 | return; |
541 | } | 553 | } |
542 | 554 | ||
543 | Dictionary<string, string> componentKeyPath = new Dictionary<string, string>(componentTable.Rows.Count); | 555 | var componentKeyPath = new Dictionary<string, string>(componentTable.Rows.Count); |
544 | 556 | ||
545 | // Index the Component table for non-directory & non-registry key paths. | 557 | // Index the Component table for non-directory & non-registry key paths. |
546 | foreach (Row row in componentTable.Rows) | 558 | foreach (var row in componentTable.Rows) |
547 | { | 559 | { |
548 | if (null != row.Fields[5].Data && | 560 | var keyPath = row.FieldAsString(5); |
549 | 0 != ((int)row.Fields[3].Data & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath)) | 561 | if (keyPath != null && 0 != (row.FieldAsInteger(3) & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath)) |
550 | { | 562 | { |
551 | componentKeyPath.Add(row.Fields[0].Data.ToString(), row.Fields[5].Data.ToString()); | 563 | componentKeyPath.Add(row.FieldAsString(0), keyPath); |
552 | } | 564 | } |
553 | } | 565 | } |
554 | 566 | ||
555 | Dictionary<string, string> componentWithChangedKeyPath = new Dictionary<string, string>(); | 567 | var componentWithChangedKeyPath = new Dictionary<string, string>(); |
556 | Dictionary<string, string> componentWithNonKeyPathChanged = new Dictionary<string, string>(); | 568 | var componentWithNonKeyPathChanged = new Dictionary<string, string>(); |
557 | // Verify changes in the file table, now that file diffing has occurred | 569 | // Verify changes in the file table, now that file diffing has occurred |
558 | foreach (FileRow row in fileTable.Rows) | 570 | foreach (FileRow row in fileTable.Rows) |
559 | { | 571 | { |
560 | string fileId = row.Fields[0].Data.ToString(); | ||
561 | string componentId = row.Fields[1].Data.ToString(); | ||
562 | |||
563 | if (RowOperation.Modify != row.Operation) | 572 | if (RowOperation.Modify != row.Operation) |
564 | { | 573 | { |
565 | continue; | 574 | continue; |
566 | } | 575 | } |
567 | 576 | ||
577 | var fileId = row.FieldAsString(0); | ||
578 | var componentId = row.FieldAsString(1); | ||
579 | |||
568 | // If this file is the keypath of a component | 580 | // If this file is the keypath of a component |
569 | if (componentKeyPath.ContainsValue(fileId)) | 581 | if (componentKeyPath.ContainsValue(fileId)) |
570 | { | 582 | { |
@@ -582,12 +594,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
582 | } | 594 | } |
583 | } | 595 | } |
584 | 596 | ||
585 | foreach (KeyValuePair<string, string> componentFile in componentWithNonKeyPathChanged) | 597 | foreach (var componentFile in componentWithNonKeyPathChanged) |
586 | { | 598 | { |
587 | // Make sure all changes to non keypath files also had a change in the keypath. | 599 | // Make sure all changes to non keypath files also had a change in the keypath. |
588 | if (!componentWithChangedKeyPath.ContainsKey(componentFile.Key) && componentKeyPath.ContainsKey(componentFile.Key)) | 600 | if (!componentWithChangedKeyPath.ContainsKey(componentFile.Key) && componentKeyPath.TryGetValue(componentFile.Key, out var keyPath)) |
589 | { | 601 | { |
590 | this.Messaging.Write(WarningMessages.UpdateOfNonKeyPathFile((string)componentFile.Value, (string)componentFile.Key, (string)componentKeyPath[componentFile.Key])); | 602 | this.Messaging.Write(WarningMessages.UpdateOfNonKeyPathFile(componentFile.Value, componentFile.Key, keyPath)); |
591 | } | 603 | } |
592 | } | 604 | } |
593 | } | 605 | } |
@@ -614,3 +626,5 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
614 | } | 626 | } |
615 | } | 627 | } |
616 | } | 628 | } |
629 | |||
630 | #endif | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs index 19f7b9e5..f5ac00e6 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs | |||
@@ -33,7 +33,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
33 | var optimizePatchSizeForLargeFiles = this.WixPatchId?.OptimizePatchSizeForLargeFiles ?? false; | 33 | var optimizePatchSizeForLargeFiles = this.WixPatchId?.OptimizePatchSizeForLargeFiles ?? false; |
34 | var apiPatchingSymbolFlags = (PatchSymbolFlagsType)(this.WixPatchId?.ApiPatchingSymbolFlags ?? 0); | 34 | var apiPatchingSymbolFlags = (PatchSymbolFlagsType)(this.WixPatchId?.ApiPatchingSymbolFlags ?? 0); |
35 | 35 | ||
36 | #if REVISIT_FOR_PATCHING | 36 | #if TODO_PATCHING |
37 | foreach (FileFacade facade in this.FileFacades) | 37 | foreach (FileFacade facade in this.FileFacades) |
38 | { | 38 | { |
39 | if (RowOperation.Modify == facade.File.Operation && | 39 | if (RowOperation.Modify == facade.File.Operation && |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs index 6b1dead5..f09a2e47 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs | |||
@@ -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 | 2 | ||
3 | namespace WixToolset.Core.WindowsInstaller.Bind | 3 | namespace WixToolset.Core.WindowsInstaller.Bind |
4 | { | 4 | { |
@@ -122,13 +122,18 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
122 | tableString.Append(definition.Name); | 122 | tableString.Append(definition.Name); |
123 | foreach (var column in definition.Columns) | 123 | foreach (var column in definition.Columns) |
124 | { | 124 | { |
125 | // conditionally keep columns added in a transform; otherwise, | 125 | // Conditionally keep columns added in a transform; otherwise, |
126 | // break because columns can only be added at the end | 126 | // break because columns can only be added at the end. |
127 | if (column.Added && !keepAddedColumns) | 127 | if (column.Added && !keepAddedColumns) |
128 | { | 128 | { |
129 | break; | 129 | break; |
130 | } | 130 | } |
131 | 131 | ||
132 | if (column.Unreal) | ||
133 | { | ||
134 | continue; | ||
135 | } | ||
136 | |||
132 | if (!first) | 137 | if (!first) |
133 | { | 138 | { |
134 | columnString.Append('\t'); | 139 | columnString.Append('\t'); |
@@ -168,6 +173,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
168 | break; | 173 | break; |
169 | } | 174 | } |
170 | 175 | ||
176 | if (field.Column.Unreal) | ||
177 | { | ||
178 | continue; | ||
179 | } | ||
180 | |||
171 | if (first) | 181 | if (first) |
172 | { | 182 | { |
173 | first = false; | 183 | first = false; |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs index 31d0b3a6..5707f7ce 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs | |||
@@ -40,193 +40,193 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
40 | 40 | ||
41 | public void Execute() | 41 | public void Execute() |
42 | { | 42 | { |
43 | var output = new WindowsInstallerData(this.Section.Tuples.First().SourceLineNumbers); | 43 | this.Output = new WindowsInstallerData(this.Section.Tuples.First().SourceLineNumbers) |
44 | output.Codepage = this.Section.Codepage; | 44 | { |
45 | output.Type = SectionTypeToOutputType(this.Section.Type); | 45 | Codepage = this.Section.Codepage, |
46 | 46 | Type = SectionTypeToOutputType(this.Section.Type) | |
47 | this.AddSectionToOutput(this.Section, output); | 47 | }; |
48 | 48 | ||
49 | this.Output = output; | 49 | this.AddSectionToOutput(); |
50 | } | 50 | } |
51 | 51 | ||
52 | private void AddSectionToOutput(IntermediateSection section, WindowsInstallerData output) | 52 | private void AddSectionToOutput() |
53 | { | 53 | { |
54 | foreach (var tuple in section.Tuples) | 54 | foreach (var tuple in this.Section.Tuples) |
55 | { | 55 | { |
56 | switch (tuple.Definition.Type) | 56 | switch (tuple.Definition.Type) |
57 | { | 57 | { |
58 | case TupleDefinitionType.AppSearch: | 58 | case TupleDefinitionType.AppSearch: |
59 | this.AddTupleDefaultly(tuple, output); | 59 | this.AddTupleDefaultly(tuple); |
60 | output.EnsureTable(this.TableDefinitions["Signature"]); | 60 | this.Output.EnsureTable(this.TableDefinitions["Signature"]); |
61 | break; | 61 | break; |
62 | 62 | ||
63 | case TupleDefinitionType.Assembly: | 63 | case TupleDefinitionType.Assembly: |
64 | this.AddAssemblyTuple((AssemblyTuple)tuple, output); | 64 | this.AddAssemblyTuple((AssemblyTuple)tuple); |
65 | break; | 65 | break; |
66 | 66 | ||
67 | case TupleDefinitionType.Binary: | 67 | case TupleDefinitionType.Binary: |
68 | this.AddTupleDefaultly(tuple, output, idIsPrimaryKey: true); | 68 | this.AddTupleDefaultly(tuple, idIsPrimaryKey: true); |
69 | break; | 69 | break; |
70 | 70 | ||
71 | case TupleDefinitionType.BBControl: | 71 | case TupleDefinitionType.BBControl: |
72 | this.AddBBControlTuple((BBControlTuple)tuple, output); | 72 | this.AddBBControlTuple((BBControlTuple)tuple); |
73 | break; | 73 | break; |
74 | 74 | ||
75 | case TupleDefinitionType.Class: | 75 | case TupleDefinitionType.Class: |
76 | this.AddClassTuple((ClassTuple)tuple, output); | 76 | this.AddClassTuple((ClassTuple)tuple); |
77 | break; | 77 | break; |
78 | 78 | ||
79 | case TupleDefinitionType.Control: | 79 | case TupleDefinitionType.Control: |
80 | this.AddControlTuple((ControlTuple)tuple, output); | 80 | this.AddControlTuple((ControlTuple)tuple); |
81 | break; | 81 | break; |
82 | 82 | ||
83 | case TupleDefinitionType.Component: | 83 | case TupleDefinitionType.Component: |
84 | this.AddComponentTuple((ComponentTuple)tuple, output); | 84 | this.AddComponentTuple((ComponentTuple)tuple); |
85 | break; | 85 | break; |
86 | 86 | ||
87 | case TupleDefinitionType.CustomAction: | 87 | case TupleDefinitionType.CustomAction: |
88 | this.AddCustomActionTuple((CustomActionTuple)tuple, output); | 88 | this.AddCustomActionTuple((CustomActionTuple)tuple); |
89 | break; | 89 | break; |
90 | 90 | ||
91 | case TupleDefinitionType.Dialog: | 91 | case TupleDefinitionType.Dialog: |
92 | this.AddDialogTuple((DialogTuple)tuple, output); | 92 | this.AddDialogTuple((DialogTuple)tuple); |
93 | break; | 93 | break; |
94 | 94 | ||
95 | case TupleDefinitionType.Directory: | 95 | case TupleDefinitionType.Directory: |
96 | this.AddDirectoryTuple((DirectoryTuple)tuple, output); | 96 | this.AddDirectoryTuple((DirectoryTuple)tuple); |
97 | break; | 97 | break; |
98 | 98 | ||
99 | case TupleDefinitionType.Environment: | 99 | case TupleDefinitionType.Environment: |
100 | this.AddEnvironmentTuple((EnvironmentTuple)tuple, output); | 100 | this.AddEnvironmentTuple((EnvironmentTuple)tuple); |
101 | break; | 101 | break; |
102 | 102 | ||
103 | case TupleDefinitionType.Error: | 103 | case TupleDefinitionType.Error: |
104 | this.AddErrorTuple((ErrorTuple)tuple, output); | 104 | this.AddErrorTuple((ErrorTuple)tuple); |
105 | break; | 105 | break; |
106 | 106 | ||
107 | case TupleDefinitionType.Feature: | 107 | case TupleDefinitionType.Feature: |
108 | this.AddFeatureTuple((FeatureTuple)tuple, output); | 108 | this.AddFeatureTuple((FeatureTuple)tuple); |
109 | break; | 109 | break; |
110 | 110 | ||
111 | case TupleDefinitionType.File: | 111 | case TupleDefinitionType.File: |
112 | this.AddFileTuple((FileTuple)tuple, output); | 112 | this.AddFileTuple((FileTuple)tuple); |
113 | break; | 113 | break; |
114 | 114 | ||
115 | case TupleDefinitionType.Icon: | 115 | case TupleDefinitionType.Icon: |
116 | this.AddTupleDefaultly(tuple, output, idIsPrimaryKey: true); | 116 | this.AddTupleDefaultly(tuple, idIsPrimaryKey: true); |
117 | break; | 117 | break; |
118 | 118 | ||
119 | case TupleDefinitionType.IniFile: | 119 | case TupleDefinitionType.IniFile: |
120 | this.AddIniFileTuple((IniFileTuple)tuple, output); | 120 | this.AddIniFileTuple((IniFileTuple)tuple); |
121 | break; | 121 | break; |
122 | 122 | ||
123 | case TupleDefinitionType.Media: | 123 | case TupleDefinitionType.Media: |
124 | this.AddMediaTuple((MediaTuple)tuple, output); | 124 | this.AddMediaTuple((MediaTuple)tuple); |
125 | break; | 125 | break; |
126 | 126 | ||
127 | case TupleDefinitionType.ModuleConfiguration: | 127 | case TupleDefinitionType.ModuleConfiguration: |
128 | this.AddModuleConfigurationTuple((ModuleConfigurationTuple)tuple, output); | 128 | this.AddModuleConfigurationTuple((ModuleConfigurationTuple)tuple); |
129 | break; | 129 | break; |
130 | 130 | ||
131 | case TupleDefinitionType.MsiEmbeddedUI: | 131 | case TupleDefinitionType.MsiEmbeddedUI: |
132 | this.AddMsiEmbeddedUITuple((MsiEmbeddedUITuple)tuple, output); | 132 | this.AddMsiEmbeddedUITuple((MsiEmbeddedUITuple)tuple); |
133 | break; | 133 | break; |
134 | 134 | ||
135 | case TupleDefinitionType.MsiFileHash: | 135 | case TupleDefinitionType.MsiFileHash: |
136 | this.AddMsiFileHashTuple((MsiFileHashTuple)tuple, output); | 136 | this.AddMsiFileHashTuple((MsiFileHashTuple)tuple); |
137 | break; | 137 | break; |
138 | 138 | ||
139 | case TupleDefinitionType.MsiServiceConfig: | 139 | case TupleDefinitionType.MsiServiceConfig: |
140 | this.AddMsiServiceConfigTuple((MsiServiceConfigTuple)tuple, output); | 140 | this.AddMsiServiceConfigTuple((MsiServiceConfigTuple)tuple); |
141 | break; | 141 | break; |
142 | 142 | ||
143 | case TupleDefinitionType.MsiServiceConfigFailureActions: | 143 | case TupleDefinitionType.MsiServiceConfigFailureActions: |
144 | this.AddMsiServiceConfigFailureActionsTuple((MsiServiceConfigFailureActionsTuple)tuple, output); | 144 | this.AddMsiServiceConfigFailureActionsTuple((MsiServiceConfigFailureActionsTuple)tuple); |
145 | break; | 145 | break; |
146 | 146 | ||
147 | case TupleDefinitionType.MsiShortcutProperty: | 147 | case TupleDefinitionType.MsiShortcutProperty: |
148 | this.AddTupleDefaultly(tuple, output, idIsPrimaryKey: true); | 148 | this.AddTupleDefaultly(tuple, idIsPrimaryKey: true); |
149 | break; | 149 | break; |
150 | 150 | ||
151 | case TupleDefinitionType.MoveFile: | 151 | case TupleDefinitionType.MoveFile: |
152 | this.AddMoveFileTuple((MoveFileTuple)tuple, output); | 152 | this.AddMoveFileTuple((MoveFileTuple)tuple); |
153 | break; | 153 | break; |
154 | 154 | ||
155 | case TupleDefinitionType.ProgId: | 155 | case TupleDefinitionType.ProgId: |
156 | this.AddTupleDefaultly(tuple, output); | 156 | this.AddTupleDefaultly(tuple); |
157 | output.EnsureTable(this.TableDefinitions["Extension"]); | 157 | this.Output.EnsureTable(this.TableDefinitions["Extension"]); |
158 | break; | 158 | break; |
159 | 159 | ||
160 | case TupleDefinitionType.Property: | 160 | case TupleDefinitionType.Property: |
161 | this.AddPropertyTuple((PropertyTuple)tuple, output); | 161 | this.AddPropertyTuple((PropertyTuple)tuple); |
162 | break; | 162 | break; |
163 | 163 | ||
164 | case TupleDefinitionType.RemoveFile: | 164 | case TupleDefinitionType.RemoveFile: |
165 | this.AddRemoveFileTuple((RemoveFileTuple)tuple, output); | 165 | this.AddRemoveFileTuple((RemoveFileTuple)tuple); |
166 | break; | 166 | break; |
167 | 167 | ||
168 | case TupleDefinitionType.Registry: | 168 | case TupleDefinitionType.Registry: |
169 | this.AddRegistryTuple((RegistryTuple)tuple, output); | 169 | this.AddRegistryTuple((RegistryTuple)tuple); |
170 | break; | 170 | break; |
171 | 171 | ||
172 | case TupleDefinitionType.RegLocator: | 172 | case TupleDefinitionType.RegLocator: |
173 | this.AddRegLocatorTuple((RegLocatorTuple)tuple, output); | 173 | this.AddRegLocatorTuple((RegLocatorTuple)tuple); |
174 | break; | 174 | break; |
175 | 175 | ||
176 | case TupleDefinitionType.RemoveRegistry: | 176 | case TupleDefinitionType.RemoveRegistry: |
177 | this.AddRemoveRegistryTuple((RemoveRegistryTuple)tuple, output); | 177 | this.AddRemoveRegistryTuple((RemoveRegistryTuple)tuple); |
178 | break; | 178 | break; |
179 | 179 | ||
180 | case TupleDefinitionType.ReserveCost: | 180 | case TupleDefinitionType.ReserveCost: |
181 | this.AddTupleDefaultly(tuple, output, idIsPrimaryKey: true); | 181 | this.AddTupleDefaultly(tuple, idIsPrimaryKey: true); |
182 | break; | 182 | break; |
183 | 183 | ||
184 | case TupleDefinitionType.ServiceControl: | 184 | case TupleDefinitionType.ServiceControl: |
185 | this.AddServiceControlTuple((ServiceControlTuple)tuple, output); | 185 | this.AddServiceControlTuple((ServiceControlTuple)tuple); |
186 | break; | 186 | break; |
187 | 187 | ||
188 | case TupleDefinitionType.ServiceInstall: | 188 | case TupleDefinitionType.ServiceInstall: |
189 | this.AddServiceInstallTuple((ServiceInstallTuple)tuple, output); | 189 | this.AddServiceInstallTuple((ServiceInstallTuple)tuple); |
190 | break; | 190 | break; |
191 | 191 | ||
192 | case TupleDefinitionType.Shortcut: | 192 | case TupleDefinitionType.Shortcut: |
193 | this.AddShortcutTuple((ShortcutTuple)tuple, output); | 193 | this.AddShortcutTuple((ShortcutTuple)tuple); |
194 | break; | 194 | break; |
195 | 195 | ||
196 | case TupleDefinitionType.Signature: | 196 | case TupleDefinitionType.Signature: |
197 | this.AddTupleDefaultly(tuple, output, idIsPrimaryKey: true); | 197 | this.AddTupleDefaultly(tuple, idIsPrimaryKey: true); |
198 | break; | 198 | break; |
199 | 199 | ||
200 | case TupleDefinitionType.SummaryInformation: | 200 | case TupleDefinitionType.SummaryInformation: |
201 | this.AddTupleDefaultly(tuple, output, tableName: "_SummaryInformation"); | 201 | this.AddTupleDefaultly(tuple, tableName: "_SummaryInformation"); |
202 | break; | 202 | break; |
203 | 203 | ||
204 | case TupleDefinitionType.TextStyle: | 204 | case TupleDefinitionType.TextStyle: |
205 | this.AddTextStyleTuple((TextStyleTuple)tuple, output); | 205 | this.AddTextStyleTuple((TextStyleTuple)tuple); |
206 | break; | 206 | break; |
207 | 207 | ||
208 | case TupleDefinitionType.Upgrade: | 208 | case TupleDefinitionType.Upgrade: |
209 | this.AddUpgradeTuple((UpgradeTuple)tuple, output); | 209 | this.AddUpgradeTuple((UpgradeTuple)tuple); |
210 | break; | 210 | break; |
211 | 211 | ||
212 | case TupleDefinitionType.WixAction: | 212 | case TupleDefinitionType.WixAction: |
213 | this.AddWixActionTuple((WixActionTuple)tuple, output); | 213 | this.AddWixActionTuple((WixActionTuple)tuple); |
214 | break; | 214 | break; |
215 | 215 | ||
216 | case TupleDefinitionType.WixMediaTemplate: | 216 | case TupleDefinitionType.WixMediaTemplate: |
217 | this.AddWixMediaTemplateTuple((WixMediaTemplateTuple)tuple, output); | 217 | this.AddWixMediaTemplateTuple((WixMediaTemplateTuple)tuple); |
218 | break; | 218 | break; |
219 | 219 | ||
220 | case TupleDefinitionType.MustBeFromAnExtension: | 220 | case TupleDefinitionType.MustBeFromAnExtension: |
221 | this.AddTupleFromExtension(tuple, output); | 221 | this.AddTupleFromExtension(tuple); |
222 | break; | 222 | break; |
223 | 223 | ||
224 | case TupleDefinitionType.WixCustomRow: | 224 | case TupleDefinitionType.WixCustomRow: |
225 | this.AddWixCustomRowTuple((WixCustomRowTuple)tuple, output); | 225 | this.AddWixCustomRowTuple((WixCustomRowTuple)tuple); |
226 | break; | 226 | break; |
227 | 227 | ||
228 | case TupleDefinitionType.WixEnsureTable: | 228 | case TupleDefinitionType.WixEnsureTable: |
229 | this.AddWixEnsureTableTuple((WixEnsureTableTuple)tuple, output); | 229 | this.AddWixEnsureTableTuple((WixEnsureTableTuple)tuple); |
230 | break; | 230 | break; |
231 | 231 | ||
232 | // ignored. | 232 | // ignored. |
@@ -234,25 +234,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
234 | case TupleDefinitionType.WixComponentGroup: | 234 | case TupleDefinitionType.WixComponentGroup: |
235 | case TupleDefinitionType.WixDeltaPatchFile: | 235 | case TupleDefinitionType.WixDeltaPatchFile: |
236 | case TupleDefinitionType.WixFeatureGroup: | 236 | case TupleDefinitionType.WixFeatureGroup: |
237 | break; | 237 | case TupleDefinitionType.WixPatchBaseline: |
238 | break; | ||
238 | 239 | ||
239 | // Already processed. | 240 | // Already processed. |
240 | case TupleDefinitionType.WixCustomTable: | 241 | case TupleDefinitionType.WixCustomTable: |
241 | break; | 242 | break; |
242 | 243 | ||
243 | default: | 244 | default: |
244 | this.AddTupleDefaultly(tuple, output); | 245 | this.AddTupleDefaultly(tuple); |
245 | break; | 246 | break; |
246 | } | 247 | } |
247 | } | 248 | } |
248 | } | 249 | } |
249 | 250 | ||
250 | private void AddAssemblyTuple(AssemblyTuple tuple, WindowsInstallerData output) | 251 | private void AddAssemblyTuple(AssemblyTuple tuple) |
251 | { | 252 | { |
252 | var attributes = tuple.Type == AssemblyType.Win32Assembly ? 1 : (int?)null; | 253 | var attributes = tuple.Type == AssemblyType.Win32Assembly ? 1 : (int?)null; |
253 | 254 | ||
254 | var table = output.EnsureTable(this.TableDefinitions["MsiAssembly"]); | 255 | var row = this.CreateRow(tuple, "MsiAssembly"); |
255 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
256 | row[0] = tuple.ComponentRef; | 256 | row[0] = tuple.ComponentRef; |
257 | row[1] = tuple.FeatureRef; | 257 | row[1] = tuple.FeatureRef; |
258 | row[2] = tuple.ManifestFileRef; | 258 | row[2] = tuple.ManifestFileRef; |
@@ -260,7 +260,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
260 | row[4] = attributes; | 260 | row[4] = attributes; |
261 | } | 261 | } |
262 | 262 | ||
263 | private void AddBBControlTuple(BBControlTuple tuple, WindowsInstallerData output) | 263 | private void AddBBControlTuple(BBControlTuple tuple) |
264 | { | 264 | { |
265 | var attributes = tuple.Attributes; | 265 | var attributes = tuple.Attributes; |
266 | attributes |= tuple.Enabled ? WindowsInstallerConstants.MsidbControlAttributesEnabled : 0; | 266 | attributes |= tuple.Enabled ? WindowsInstallerConstants.MsidbControlAttributesEnabled : 0; |
@@ -272,8 +272,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
272 | attributes |= tuple.Sunken ? WindowsInstallerConstants.MsidbControlAttributesSunken : 0; | 272 | attributes |= tuple.Sunken ? WindowsInstallerConstants.MsidbControlAttributesSunken : 0; |
273 | attributes |= tuple.Visible ? WindowsInstallerConstants.MsidbControlAttributesVisible : 0; | 273 | attributes |= tuple.Visible ? WindowsInstallerConstants.MsidbControlAttributesVisible : 0; |
274 | 274 | ||
275 | var table = output.EnsureTable(this.TableDefinitions["BBControl"]); | 275 | var row = this.CreateRow(tuple, "BBControl"); |
276 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
277 | row[0] = tuple.BillboardRef; | 276 | row[0] = tuple.BillboardRef; |
278 | row[1] = tuple.BBControl; | 277 | row[1] = tuple.BBControl; |
279 | row[2] = tuple.Type; | 278 | row[2] = tuple.Type; |
@@ -285,10 +284,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
285 | row[8] = tuple.Text; | 284 | row[8] = tuple.Text; |
286 | } | 285 | } |
287 | 286 | ||
288 | private void AddClassTuple(ClassTuple tuple, WindowsInstallerData output) | 287 | private void AddClassTuple(ClassTuple tuple) |
289 | { | 288 | { |
290 | var table = output.EnsureTable(this.TableDefinitions["Class"]); | 289 | var row = this.CreateRow(tuple, "Class"); |
291 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
292 | row[0] = tuple.CLSID; | 290 | row[0] = tuple.CLSID; |
293 | row[1] = tuple.Context; | 291 | row[1] = tuple.Context; |
294 | row[2] = tuple.ComponentRef; | 292 | row[2] = tuple.ComponentRef; |
@@ -304,7 +302,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
304 | row[12] = tuple.RelativePath ? (int?)1 : null; | 302 | row[12] = tuple.RelativePath ? (int?)1 : null; |
305 | } | 303 | } |
306 | 304 | ||
307 | private void AddControlTuple(ControlTuple tuple, WindowsInstallerData output) | 305 | private void AddControlTuple(ControlTuple tuple) |
308 | { | 306 | { |
309 | var text = tuple.Text; | 307 | var text = tuple.Text; |
310 | var attributes = tuple.Attributes; | 308 | var attributes = tuple.Attributes; |
@@ -329,8 +327,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
329 | text = String.Concat(text, " "); | 327 | text = String.Concat(text, " "); |
330 | } | 328 | } |
331 | 329 | ||
332 | var table = output.EnsureTable(this.TableDefinitions["Control"]); | 330 | var row = this.CreateRow(tuple, "Control"); |
333 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
334 | row[0] = tuple.DialogRef; | 331 | row[0] = tuple.DialogRef; |
335 | row[1] = tuple.Control; | 332 | row[1] = tuple.Control; |
336 | row[2] = tuple.Type; | 333 | row[2] = tuple.Type; |
@@ -344,7 +341,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
344 | row[10] = tuple.Help; | 341 | row[10] = tuple.Help; |
345 | } | 342 | } |
346 | 343 | ||
347 | private void AddComponentTuple(ComponentTuple tuple, WindowsInstallerData output) | 344 | private void AddComponentTuple(ComponentTuple tuple) |
348 | { | 345 | { |
349 | var attributes = ComponentLocation.Either == tuple.Location ? WindowsInstallerConstants.MsidbComponentAttributesOptional : 0; | 346 | var attributes = ComponentLocation.Either == tuple.Location ? WindowsInstallerConstants.MsidbComponentAttributesOptional : 0; |
350 | attributes |= ComponentLocation.SourceOnly == tuple.Location ? WindowsInstallerConstants.MsidbComponentAttributesSourceOnly : 0; | 347 | attributes |= ComponentLocation.SourceOnly == tuple.Location ? WindowsInstallerConstants.MsidbComponentAttributesSourceOnly : 0; |
@@ -359,8 +356,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
359 | attributes |= tuple.UninstallWhenSuperseded ? WindowsInstallerConstants.MsidbComponentAttributesUninstallOnSupersedence : 0; | 356 | attributes |= tuple.UninstallWhenSuperseded ? WindowsInstallerConstants.MsidbComponentAttributesUninstallOnSupersedence : 0; |
360 | attributes |= tuple.Win64 ? WindowsInstallerConstants.MsidbComponentAttributes64bit : 0; | 357 | attributes |= tuple.Win64 ? WindowsInstallerConstants.MsidbComponentAttributes64bit : 0; |
361 | 358 | ||
362 | var table = output.EnsureTable(this.TableDefinitions["Component"]); | 359 | var row = this.CreateRow(tuple, "Component"); |
363 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
364 | row[0] = tuple.Id.Id; | 360 | row[0] = tuple.Id.Id; |
365 | row[1] = tuple.ComponentId; | 361 | row[1] = tuple.ComponentId; |
366 | row[2] = tuple.DirectoryRef; | 362 | row[2] = tuple.DirectoryRef; |
@@ -369,7 +365,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
369 | row[5] = tuple.KeyPath; | 365 | row[5] = tuple.KeyPath; |
370 | } | 366 | } |
371 | 367 | ||
372 | private void AddCustomActionTuple(CustomActionTuple tuple, WindowsInstallerData output) | 368 | private void AddCustomActionTuple(CustomActionTuple tuple) |
373 | { | 369 | { |
374 | var type = tuple.Win64 ? WindowsInstallerConstants.MsidbCustomActionType64BitScript : 0; | 370 | var type = tuple.Win64 ? WindowsInstallerConstants.MsidbCustomActionType64BitScript : 0; |
375 | type |= tuple.IgnoreResult ? WindowsInstallerConstants.MsidbCustomActionTypeContinue : 0; | 371 | type |= tuple.IgnoreResult ? WindowsInstallerConstants.MsidbCustomActionTypeContinue : 0; |
@@ -396,8 +392,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
396 | type |= tuple.TSAware ? WindowsInstallerConstants.MsidbCustomActionTypeTSAware : 0; | 392 | type |= tuple.TSAware ? WindowsInstallerConstants.MsidbCustomActionTypeTSAware : 0; |
397 | } | 393 | } |
398 | 394 | ||
399 | var table = output.EnsureTable(this.TableDefinitions["CustomAction"]); | 395 | var row = this.CreateRow(tuple, "CustomAction"); |
400 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
401 | row[0] = tuple.Id.Id; | 396 | row[0] = tuple.Id.Id; |
402 | row[1] = type; | 397 | row[1] = type; |
403 | row[2] = tuple.Source; | 398 | row[2] = tuple.Source; |
@@ -405,7 +400,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
405 | row[4] = tuple.PatchUninstall ? (int?)WindowsInstallerConstants.MsidbCustomActionTypePatchUninstall : null; | 400 | row[4] = tuple.PatchUninstall ? (int?)WindowsInstallerConstants.MsidbCustomActionTypePatchUninstall : null; |
406 | } | 401 | } |
407 | 402 | ||
408 | private void AddDialogTuple(DialogTuple tuple, WindowsInstallerData output) | 403 | private void AddDialogTuple(DialogTuple tuple) |
409 | { | 404 | { |
410 | var attributes = tuple.Visible ? WindowsInstallerConstants.MsidbDialogAttributesVisible : 0; | 405 | var attributes = tuple.Visible ? WindowsInstallerConstants.MsidbDialogAttributesVisible : 0; |
411 | attributes|= tuple.Modal ? WindowsInstallerConstants.MsidbDialogAttributesModal : 0; | 406 | attributes|= tuple.Modal ? WindowsInstallerConstants.MsidbDialogAttributesModal : 0; |
@@ -419,8 +414,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
419 | attributes|= tuple.SystemModal ? WindowsInstallerConstants.MsidbDialogAttributesSysModal : 0; | 414 | attributes|= tuple.SystemModal ? WindowsInstallerConstants.MsidbDialogAttributesSysModal : 0; |
420 | attributes|= tuple.TrackDiskSpace ? WindowsInstallerConstants.MsidbDialogAttributesTrackDiskSpace : 0; | 415 | attributes|= tuple.TrackDiskSpace ? WindowsInstallerConstants.MsidbDialogAttributesTrackDiskSpace : 0; |
421 | 416 | ||
422 | var table = output.EnsureTable(this.TableDefinitions["Dialog"]); | 417 | var row = this.CreateRow(tuple, "Dialog"); |
423 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
424 | row[0] = tuple.Id.Id; | 418 | row[0] = tuple.Id.Id; |
425 | row[1] = tuple.HCentering; | 419 | row[1] = tuple.HCentering; |
426 | row[2] = tuple.VCentering; | 420 | row[2] = tuple.VCentering; |
@@ -432,10 +426,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
432 | row[8] = tuple.DefaultControlRef; | 426 | row[8] = tuple.DefaultControlRef; |
433 | row[9] = tuple.CancelControlRef; | 427 | row[9] = tuple.CancelControlRef; |
434 | 428 | ||
435 | output.EnsureTable(this.TableDefinitions["ListBox"]); | 429 | this.Output.EnsureTable(this.TableDefinitions["ListBox"]); |
436 | } | 430 | } |
437 | 431 | ||
438 | private void AddDirectoryTuple(DirectoryTuple tuple, WindowsInstallerData output) | 432 | private void AddDirectoryTuple(DirectoryTuple tuple) |
439 | { | 433 | { |
440 | var sourceName = GetMsiFilenameValue(tuple.SourceShortName, tuple.SourceName); | 434 | var sourceName = GetMsiFilenameValue(tuple.SourceShortName, tuple.SourceName); |
441 | var targetName = GetMsiFilenameValue(tuple.ShortName, tuple.Name); | 435 | var targetName = GetMsiFilenameValue(tuple.ShortName, tuple.Name); |
@@ -447,14 +441,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
447 | 441 | ||
448 | var defaultDir = String.IsNullOrEmpty(sourceName) ? targetName : targetName + ":" + sourceName ; | 442 | var defaultDir = String.IsNullOrEmpty(sourceName) ? targetName : targetName + ":" + sourceName ; |
449 | 443 | ||
450 | var table = output.EnsureTable(this.TableDefinitions["Directory"]); | 444 | var row = this.CreateRow(tuple, "Directory"); |
451 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
452 | row[0] = tuple.Id.Id; | 445 | row[0] = tuple.Id.Id; |
453 | row[1] = tuple.ParentDirectoryRef; | 446 | row[1] = tuple.ParentDirectoryRef; |
454 | row[2] = defaultDir; | 447 | row[2] = defaultDir; |
455 | } | 448 | } |
456 | 449 | ||
457 | private void AddEnvironmentTuple(EnvironmentTuple tuple, WindowsInstallerData output) | 450 | private void AddEnvironmentTuple(EnvironmentTuple tuple) |
458 | { | 451 | { |
459 | var action = String.Empty; | 452 | var action = String.Empty; |
460 | var system = tuple.System ? "*" : String.Empty; | 453 | var system = tuple.System ? "*" : String.Empty; |
@@ -484,23 +477,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
484 | break; | 477 | break; |
485 | } | 478 | } |
486 | 479 | ||
487 | var table = output.EnsureTable(this.TableDefinitions["Environment"]); | 480 | var row = this.CreateRow(tuple, "Environment"); |
488 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
489 | row[0] = tuple.Id.Id; | 481 | row[0] = tuple.Id.Id; |
490 | row[1] = String.Concat(action, uninstall, system, tuple.Name); | 482 | row[1] = String.Concat(action, uninstall, system, tuple.Name); |
491 | row[2] = value; | 483 | row[2] = value; |
492 | row[3] = tuple.ComponentRef; | 484 | row[3] = tuple.ComponentRef; |
493 | } | 485 | } |
494 | 486 | ||
495 | private void AddErrorTuple(ErrorTuple tuple, WindowsInstallerData output) | 487 | private void AddErrorTuple(ErrorTuple tuple) |
496 | { | 488 | { |
497 | var table = output.EnsureTable(this.TableDefinitions["Error"]); | 489 | var row = this.CreateRow(tuple, "Error"); |
498 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
499 | row[0] = Convert.ToInt32(tuple.Id.Id); | 490 | row[0] = Convert.ToInt32(tuple.Id.Id); |
500 | row[1] = tuple.Message; | 491 | row[1] = tuple.Message; |
501 | } | 492 | } |
502 | 493 | ||
503 | private void AddFeatureTuple(FeatureTuple tuple, WindowsInstallerData output) | 494 | private void AddFeatureTuple(FeatureTuple tuple) |
504 | { | 495 | { |
505 | var attributes = tuple.DisallowAbsent ? WindowsInstallerConstants.MsidbFeatureAttributesUIDisallowAbsent : 0; | 496 | var attributes = tuple.DisallowAbsent ? WindowsInstallerConstants.MsidbFeatureAttributesUIDisallowAbsent : 0; |
506 | attributes |= tuple.DisallowAdvertise ? WindowsInstallerConstants.MsidbFeatureAttributesDisallowAdvertise : 0; | 497 | attributes |= tuple.DisallowAdvertise ? WindowsInstallerConstants.MsidbFeatureAttributesDisallowAdvertise : 0; |
@@ -508,8 +499,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
508 | attributes |= FeatureInstallDefault.Source == tuple.InstallDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFavorSource : 0; | 499 | attributes |= FeatureInstallDefault.Source == tuple.InstallDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFavorSource : 0; |
509 | attributes |= FeatureTypicalDefault.Advertise == tuple.TypicalDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFavorAdvertise : 0; | 500 | attributes |= FeatureTypicalDefault.Advertise == tuple.TypicalDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFavorAdvertise : 0; |
510 | 501 | ||
511 | var table = output.EnsureTable(this.TableDefinitions["Feature"]); | 502 | var row = this.CreateRow(tuple, "Feature"); |
512 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
513 | row[0] = tuple.Id.Id; | 503 | row[0] = tuple.Id.Id; |
514 | row[1] = tuple.ParentFeatureRef; | 504 | row[1] = tuple.ParentFeatureRef; |
515 | row[2] = tuple.Title; | 505 | row[2] = tuple.Title; |
@@ -520,16 +510,17 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
520 | row[7] = attributes; | 510 | row[7] = attributes; |
521 | } | 511 | } |
522 | 512 | ||
523 | private void AddFileTuple(FileTuple tuple, WindowsInstallerData output) | 513 | private void AddFileTuple(FileTuple tuple) |
524 | { | 514 | { |
525 | var table = output.EnsureTable(this.TableDefinitions["File"]); | 515 | var row = (FileRow)this.CreateRow(tuple, "File"); |
526 | var row = (FileRow)table.CreateRow(tuple.SourceLineNumbers); | ||
527 | row.File = tuple.Id.Id; | 516 | row.File = tuple.Id.Id; |
528 | row.Component = tuple.ComponentRef; | 517 | row.Component = tuple.ComponentRef; |
529 | row.FileName = GetMsiFilenameValue(tuple.ShortName, tuple.Name); | 518 | row.FileName = GetMsiFilenameValue(tuple.ShortName, tuple.Name); |
530 | row.FileSize = tuple.FileSize; | 519 | row.FileSize = tuple.FileSize; |
531 | row.Version = tuple.Version; | 520 | row.Version = tuple.Version; |
532 | row.Language = tuple.Language; | 521 | row.Language = tuple.Language; |
522 | row.DiskId = tuple.DiskId ?? 1; // TODO: is 0 the correct thing to default here | ||
523 | row.Source = tuple.Source.Path; | ||
533 | 524 | ||
534 | var attributes = (tuple.Attributes & FileTupleAttributes.Checksum) == FileTupleAttributes.Checksum ? WindowsInstallerConstants.MsidbFileAttributesChecksum : 0; | 525 | var attributes = (tuple.Attributes & FileTupleAttributes.Checksum) == FileTupleAttributes.Checksum ? WindowsInstallerConstants.MsidbFileAttributesChecksum : 0; |
535 | attributes |= (tuple.Attributes & FileTupleAttributes.Compressed) == FileTupleAttributes.Compressed ? WindowsInstallerConstants.MsidbFileAttributesCompressed : 0; | 526 | attributes |= (tuple.Attributes & FileTupleAttributes.Compressed) == FileTupleAttributes.Compressed ? WindowsInstallerConstants.MsidbFileAttributesCompressed : 0; |
@@ -542,19 +533,17 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
542 | 533 | ||
543 | if (!String.IsNullOrEmpty(tuple.FontTitle)) | 534 | if (!String.IsNullOrEmpty(tuple.FontTitle)) |
544 | { | 535 | { |
545 | var fontTable = output.EnsureTable(this.TableDefinitions["Font"]); | 536 | var fontRow = this.CreateRow(tuple, "Font"); |
546 | var fontRow = fontTable.CreateRow(tuple.SourceLineNumbers); | ||
547 | fontRow[0] = tuple.Id.Id; | 537 | fontRow[0] = tuple.Id.Id; |
548 | fontRow[1] = tuple.FontTitle; | 538 | fontRow[1] = tuple.FontTitle; |
549 | } | 539 | } |
550 | } | 540 | } |
551 | 541 | ||
552 | private void AddIniFileTuple(IniFileTuple tuple, WindowsInstallerData output) | 542 | private void AddIniFileTuple(IniFileTuple tuple) |
553 | { | 543 | { |
554 | var tableName = (InifFileActionType.AddLine == tuple.Action || InifFileActionType.AddTag == tuple.Action || InifFileActionType.CreateLine == tuple.Action) ? "IniFile" : "RemoveIniFile"; | 544 | var tableName = (InifFileActionType.AddLine == tuple.Action || InifFileActionType.AddTag == tuple.Action || InifFileActionType.CreateLine == tuple.Action) ? "IniFile" : "RemoveIniFile"; |
555 | 545 | ||
556 | var table = output.EnsureTable(this.TableDefinitions[tableName]); | 546 | var row = this.CreateRow(tuple, tableName); |
557 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
558 | row[0] = tuple.Id.Id; | 547 | row[0] = tuple.Id.Id; |
559 | row[1] = tuple.FileName; | 548 | row[1] = tuple.FileName; |
560 | row[2] = tuple.DirProperty; | 549 | row[2] = tuple.DirProperty; |
@@ -565,12 +554,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
565 | row[7] = tuple.ComponentRef; | 554 | row[7] = tuple.ComponentRef; |
566 | } | 555 | } |
567 | 556 | ||
568 | private void AddMediaTuple(MediaTuple tuple, WindowsInstallerData output) | 557 | private void AddMediaTuple(MediaTuple tuple) |
569 | { | 558 | { |
570 | if (this.Section.Type != SectionType.Module) | 559 | if (this.Section.Type != SectionType.Module) |
571 | { | 560 | { |
572 | var table = output.EnsureTable(this.TableDefinitions["Media"]); | 561 | var row = (MediaRow)this.CreateRow(tuple, "Media"); |
573 | var row = (MediaRow)table.CreateRow(tuple.SourceLineNumbers); | ||
574 | row.DiskId = tuple.DiskId; | 562 | row.DiskId = tuple.DiskId; |
575 | row.LastSequence = tuple.LastSequence ?? 0; | 563 | row.LastSequence = tuple.LastSequence ?? 0; |
576 | row.DiskPrompt = tuple.DiskPrompt; | 564 | row.DiskPrompt = tuple.DiskPrompt; |
@@ -580,10 +568,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
580 | } | 568 | } |
581 | } | 569 | } |
582 | 570 | ||
583 | private void AddModuleConfigurationTuple(ModuleConfigurationTuple tuple, WindowsInstallerData output) | 571 | private void AddModuleConfigurationTuple(ModuleConfigurationTuple tuple) |
584 | { | 572 | { |
585 | var table = output.EnsureTable(this.TableDefinitions["ModuleConfiguration"]); | 573 | var row = this.CreateRow(tuple, "ModuleConfiguration"); |
586 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
587 | row[0] = tuple.Id.Id; | 574 | row[0] = tuple.Id.Id; |
588 | row[1] = tuple.Format; | 575 | row[1] = tuple.Format; |
589 | row[2] = tuple.Type; | 576 | row[2] = tuple.Type; |
@@ -597,13 +584,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
597 | row[9] = tuple.HelpKeyword; | 584 | row[9] = tuple.HelpKeyword; |
598 | } | 585 | } |
599 | 586 | ||
600 | private void AddMsiEmbeddedUITuple(MsiEmbeddedUITuple tuple, WindowsInstallerData output) | 587 | private void AddMsiEmbeddedUITuple(MsiEmbeddedUITuple tuple) |
601 | { | 588 | { |
602 | var attributes = tuple.EntryPoint ? WindowsInstallerConstants.MsidbEmbeddedUI : 0; | 589 | var attributes = tuple.EntryPoint ? WindowsInstallerConstants.MsidbEmbeddedUI : 0; |
603 | attributes |= tuple.SupportsBasicUI ? WindowsInstallerConstants.MsidbEmbeddedHandlesBasic : 0; | 590 | attributes |= tuple.SupportsBasicUI ? WindowsInstallerConstants.MsidbEmbeddedHandlesBasic : 0; |
604 | 591 | ||
605 | var table = output.EnsureTable(this.TableDefinitions["MsiEmbeddedUI"]); | 592 | var row = this.CreateRow(tuple, "MsiEmbeddedUI"); |
606 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
607 | row[0] = tuple.Id.Id; | 593 | row[0] = tuple.Id.Id; |
608 | row[1] = tuple.FileName; | 594 | row[1] = tuple.FileName; |
609 | row[2] = attributes; | 595 | row[2] = attributes; |
@@ -611,10 +597,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
611 | row[4] = tuple.Source; | 597 | row[4] = tuple.Source; |
612 | } | 598 | } |
613 | 599 | ||
614 | private void AddMsiFileHashTuple(MsiFileHashTuple tuple, WindowsInstallerData output) | 600 | private void AddMsiFileHashTuple(MsiFileHashTuple tuple) |
615 | { | 601 | { |
616 | var table = output.EnsureTable(this.TableDefinitions["MsiFileHash"]); | 602 | var row = this.CreateRow(tuple, "MsiFileHash"); |
617 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
618 | row[0] = tuple.Id.Id; | 603 | row[0] = tuple.Id.Id; |
619 | row[1] = tuple.Options; | 604 | row[1] = tuple.Options; |
620 | row[2] = tuple.HashPart1; | 605 | row[2] = tuple.HashPart1; |
@@ -623,14 +608,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
623 | row[5] = tuple.HashPart4; | 608 | row[5] = tuple.HashPart4; |
624 | } | 609 | } |
625 | 610 | ||
626 | private void AddMsiServiceConfigTuple(MsiServiceConfigTuple tuple, WindowsInstallerData output) | 611 | private void AddMsiServiceConfigTuple(MsiServiceConfigTuple tuple) |
627 | { | 612 | { |
628 | var events = tuple.OnInstall ? WindowsInstallerConstants.MsidbServiceConfigEventInstall : 0; | 613 | var events = tuple.OnInstall ? WindowsInstallerConstants.MsidbServiceConfigEventInstall : 0; |
629 | events |= tuple.OnReinstall ? WindowsInstallerConstants.MsidbServiceConfigEventReinstall : 0; | 614 | events |= tuple.OnReinstall ? WindowsInstallerConstants.MsidbServiceConfigEventReinstall : 0; |
630 | events |= tuple.OnUninstall ? WindowsInstallerConstants.MsidbServiceConfigEventUninstall : 0; | 615 | events |= tuple.OnUninstall ? WindowsInstallerConstants.MsidbServiceConfigEventUninstall : 0; |
631 | 616 | ||
632 | var table = output.EnsureTable(this.TableDefinitions["MsiServiceConfigFailureActions"]); | 617 | var row = this.CreateRow(tuple, "MsiServiceConfigFailureActions"); |
633 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
634 | row[0] = tuple.Id.Id; | 618 | row[0] = tuple.Id.Id; |
635 | row[1] = tuple.Name; | 619 | row[1] = tuple.Name; |
636 | row[2] = events; | 620 | row[2] = events; |
@@ -639,14 +623,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
639 | row[5] = tuple.ComponentRef; | 623 | row[5] = tuple.ComponentRef; |
640 | } | 624 | } |
641 | 625 | ||
642 | private void AddMsiServiceConfigFailureActionsTuple(MsiServiceConfigFailureActionsTuple tuple, WindowsInstallerData output) | 626 | private void AddMsiServiceConfigFailureActionsTuple(MsiServiceConfigFailureActionsTuple tuple) |
643 | { | 627 | { |
644 | var events = tuple.OnInstall ? WindowsInstallerConstants.MsidbServiceConfigEventInstall : 0; | 628 | var events = tuple.OnInstall ? WindowsInstallerConstants.MsidbServiceConfigEventInstall : 0; |
645 | events |= tuple.OnReinstall ? WindowsInstallerConstants.MsidbServiceConfigEventReinstall : 0; | 629 | events |= tuple.OnReinstall ? WindowsInstallerConstants.MsidbServiceConfigEventReinstall : 0; |
646 | events |= tuple.OnUninstall ? WindowsInstallerConstants.MsidbServiceConfigEventUninstall : 0; | 630 | events |= tuple.OnUninstall ? WindowsInstallerConstants.MsidbServiceConfigEventUninstall : 0; |
647 | 631 | ||
648 | var table = output.EnsureTable(this.TableDefinitions["MsiServiceConfig"]); | 632 | var row = this.CreateRow(tuple, "MsiServiceConfig"); |
649 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
650 | row[0] = tuple.Id.Id; | 633 | row[0] = tuple.Id.Id; |
651 | row[1] = tuple.Name; | 634 | row[1] = tuple.Name; |
652 | row[2] = events; | 635 | row[2] = events; |
@@ -658,10 +641,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
658 | row[8] = tuple.ComponentRef; | 641 | row[8] = tuple.ComponentRef; |
659 | } | 642 | } |
660 | 643 | ||
661 | private void AddMoveFileTuple(MoveFileTuple tuple, WindowsInstallerData output) | 644 | private void AddMoveFileTuple(MoveFileTuple tuple) |
662 | { | 645 | { |
663 | var table = output.EnsureTable(this.TableDefinitions["MoveFile"]); | 646 | var row = this.CreateRow(tuple, "MoveFile"); |
664 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
665 | row[0] = tuple.Id.Id; | 647 | row[0] = tuple.Id.Id; |
666 | row[1] = tuple.ComponentRef; | 648 | row[1] = tuple.ComponentRef; |
667 | row[2] = tuple.SourceName; | 649 | row[2] = tuple.SourceName; |
@@ -671,26 +653,24 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
671 | row[6] = tuple.Delete ? WindowsInstallerConstants.MsidbMoveFileOptionsMove : 0; | 653 | row[6] = tuple.Delete ? WindowsInstallerConstants.MsidbMoveFileOptionsMove : 0; |
672 | } | 654 | } |
673 | 655 | ||
674 | private void AddPropertyTuple(PropertyTuple tuple, WindowsInstallerData output) | 656 | private void AddPropertyTuple(PropertyTuple tuple) |
675 | { | 657 | { |
676 | if (String.IsNullOrEmpty(tuple.Value)) | 658 | if (String.IsNullOrEmpty(tuple.Value)) |
677 | { | 659 | { |
678 | return; | 660 | return; |
679 | } | 661 | } |
680 | 662 | ||
681 | var table = output.EnsureTable(this.TableDefinitions["Property"]); | 663 | var row = (PropertyRow)this.CreateRow(tuple, "Property"); |
682 | var row = (PropertyRow)table.CreateRow(tuple.SourceLineNumbers); | ||
683 | row.Property = tuple.Id.Id; | 664 | row.Property = tuple.Id.Id; |
684 | row.Value = tuple.Value; | 665 | row.Value = tuple.Value; |
685 | } | 666 | } |
686 | 667 | ||
687 | private void AddRemoveFileTuple(RemoveFileTuple tuple, WindowsInstallerData output) | 668 | private void AddRemoveFileTuple(RemoveFileTuple tuple) |
688 | { | 669 | { |
689 | var installMode = tuple.OnInstall == true ? WindowsInstallerConstants.MsidbRemoveFileInstallModeOnInstall : 0; | 670 | var installMode = tuple.OnInstall == true ? WindowsInstallerConstants.MsidbRemoveFileInstallModeOnInstall : 0; |
690 | installMode |= tuple.OnUninstall == true ? WindowsInstallerConstants.MsidbRemoveFileInstallModeOnRemove : 0; | 671 | installMode |= tuple.OnUninstall == true ? WindowsInstallerConstants.MsidbRemoveFileInstallModeOnRemove : 0; |
691 | 672 | ||
692 | var table = output.EnsureTable(this.TableDefinitions["RemoveFile"]); | 673 | var row = this.CreateRow(tuple, "RemoveFile"); |
693 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
694 | row[0] = tuple.Id.Id; | 674 | row[0] = tuple.Id.Id; |
695 | row[1] = tuple.ComponentRef; | 675 | row[1] = tuple.ComponentRef; |
696 | row[2] = tuple.FileName; | 676 | row[2] = tuple.FileName; |
@@ -698,7 +678,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
698 | row[4] = installMode; | 678 | row[4] = installMode; |
699 | } | 679 | } |
700 | 680 | ||
701 | private void AddRegistryTuple(RegistryTuple tuple, WindowsInstallerData output) | 681 | private void AddRegistryTuple(RegistryTuple tuple) |
702 | { | 682 | { |
703 | var value = tuple.Value; | 683 | var value = tuple.Value; |
704 | 684 | ||
@@ -740,8 +720,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
740 | break; | 720 | break; |
741 | } | 721 | } |
742 | 722 | ||
743 | var table = output.EnsureTable(this.TableDefinitions["Registry"]); | 723 | var row = this.CreateRow(tuple, "Registry"); |
744 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
745 | row[0] = tuple.Id.Id; | 724 | row[0] = tuple.Id.Id; |
746 | row[1] = tuple.Root; | 725 | row[1] = tuple.Root; |
747 | row[2] = tuple.Key; | 726 | row[2] = tuple.Key; |
@@ -750,13 +729,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
750 | row[5] = tuple.ComponentRef; | 729 | row[5] = tuple.ComponentRef; |
751 | } | 730 | } |
752 | 731 | ||
753 | private void AddRegLocatorTuple(RegLocatorTuple tuple, WindowsInstallerData output) | 732 | private void AddRegLocatorTuple(RegLocatorTuple tuple) |
754 | { | 733 | { |
755 | var type = (int)tuple.Type; | 734 | var type = (int)tuple.Type; |
756 | type |= tuple.Win64 ? WindowsInstallerConstants.MsidbLocatorType64bit : 0; | 735 | type |= tuple.Win64 ? WindowsInstallerConstants.MsidbLocatorType64bit : 0; |
757 | 736 | ||
758 | var table = output.EnsureTable(this.TableDefinitions["RegLocator"]); | 737 | var row = this.CreateRow(tuple, "RegLocator"); |
759 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
760 | row[0] = tuple.Id.Id; | 738 | row[0] = tuple.Id.Id; |
761 | row[1] = tuple.Root; | 739 | row[1] = tuple.Root; |
762 | row[2] = tuple.Key; | 740 | row[2] = tuple.Key; |
@@ -764,12 +742,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
764 | row[4] = type; | 742 | row[4] = type; |
765 | } | 743 | } |
766 | 744 | ||
767 | private void AddRemoveRegistryTuple(RemoveRegistryTuple tuple, WindowsInstallerData output) | 745 | private void AddRemoveRegistryTuple(RemoveRegistryTuple tuple) |
768 | { | 746 | { |
769 | if (tuple.Action == RemoveRegistryActionType.RemoveOnInstall) | 747 | if (tuple.Action == RemoveRegistryActionType.RemoveOnInstall) |
770 | { | 748 | { |
771 | var table = output.EnsureTable(this.TableDefinitions["RemoveRegistry"]); | 749 | var row = this.CreateRow(tuple, "RemoveRegistry"); |
772 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
773 | row[0] = tuple.Id.Id; | 750 | row[0] = tuple.Id.Id; |
774 | row[1] = tuple.Root; | 751 | row[1] = tuple.Root; |
775 | row[2] = tuple.Key; | 752 | row[2] = tuple.Key; |
@@ -778,8 +755,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
778 | } | 755 | } |
779 | else // Registry table is used to remove registry keys on uninstall. | 756 | else // Registry table is used to remove registry keys on uninstall. |
780 | { | 757 | { |
781 | var table = output.EnsureTable(this.TableDefinitions["Registry"]); | 758 | var row = this.CreateRow(tuple, "Registry"); |
782 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
783 | row[0] = tuple.Id.Id; | 759 | row[0] = tuple.Id.Id; |
784 | row[1] = tuple.Root; | 760 | row[1] = tuple.Root; |
785 | row[2] = tuple.Key; | 761 | row[2] = tuple.Key; |
@@ -788,7 +764,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
788 | } | 764 | } |
789 | } | 765 | } |
790 | 766 | ||
791 | private void AddServiceControlTuple(ServiceControlTuple tuple, WindowsInstallerData output) | 767 | private void AddServiceControlTuple(ServiceControlTuple tuple) |
792 | { | 768 | { |
793 | var events = tuple.InstallRemove ? WindowsInstallerConstants.MsidbServiceControlEventDelete : 0; | 769 | var events = tuple.InstallRemove ? WindowsInstallerConstants.MsidbServiceControlEventDelete : 0; |
794 | events |= tuple.UninstallRemove ? WindowsInstallerConstants.MsidbServiceControlEventUninstallDelete : 0; | 770 | events |= tuple.UninstallRemove ? WindowsInstallerConstants.MsidbServiceControlEventUninstallDelete : 0; |
@@ -797,8 +773,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
797 | events |= tuple.InstallStop ? WindowsInstallerConstants.MsidbServiceControlEventStop : 0; | 773 | events |= tuple.InstallStop ? WindowsInstallerConstants.MsidbServiceControlEventStop : 0; |
798 | events |= tuple.UninstallStop ? WindowsInstallerConstants.MsidbServiceControlEventUninstallStop : 0; | 774 | events |= tuple.UninstallStop ? WindowsInstallerConstants.MsidbServiceControlEventUninstallStop : 0; |
799 | 775 | ||
800 | var table = output.EnsureTable(this.TableDefinitions["ServiceControl"]); | 776 | var row = this.CreateRow(tuple, "ServiceControl"); |
801 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
802 | row[0] = tuple.Id.Id; | 777 | row[0] = tuple.Id.Id; |
803 | row[1] = tuple.Name; | 778 | row[1] = tuple.Name; |
804 | row[2] = events; | 779 | row[2] = events; |
@@ -810,7 +785,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
810 | row[5] = tuple.ComponentRef; | 785 | row[5] = tuple.ComponentRef; |
811 | } | 786 | } |
812 | 787 | ||
813 | private void AddServiceInstallTuple(ServiceInstallTuple tuple, WindowsInstallerData output) | 788 | private void AddServiceInstallTuple(ServiceInstallTuple tuple) |
814 | { | 789 | { |
815 | var errorControl = (int)tuple.ErrorControl; | 790 | var errorControl = (int)tuple.ErrorControl; |
816 | errorControl |= tuple.Vital ? WindowsInstallerConstants.MsidbServiceInstallErrorControlVital : 0; | 791 | errorControl |= tuple.Vital ? WindowsInstallerConstants.MsidbServiceInstallErrorControlVital : 0; |
@@ -818,8 +793,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
818 | var serviceType = (int)tuple.ServiceType; | 793 | var serviceType = (int)tuple.ServiceType; |
819 | serviceType |= tuple.Interactive ? WindowsInstallerConstants.MsidbServiceInstallInteractive : 0; | 794 | serviceType |= tuple.Interactive ? WindowsInstallerConstants.MsidbServiceInstallInteractive : 0; |
820 | 795 | ||
821 | var table = output.EnsureTable(this.TableDefinitions["ServiceInstall"]); | 796 | var row = this.CreateRow(tuple, "ServiceInstall"); |
822 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
823 | row[0] = tuple.Id.Id; | 797 | row[0] = tuple.Id.Id; |
824 | row[1] = tuple.Name; | 798 | row[1] = tuple.Name; |
825 | row[2] = tuple.DisplayName; | 799 | row[2] = tuple.DisplayName; |
@@ -835,10 +809,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
835 | row[12] = tuple.Description; | 809 | row[12] = tuple.Description; |
836 | } | 810 | } |
837 | 811 | ||
838 | private void AddShortcutTuple(ShortcutTuple tuple, WindowsInstallerData output) | 812 | private void AddShortcutTuple(ShortcutTuple tuple) |
839 | { | 813 | { |
840 | var table = output.EnsureTable(this.TableDefinitions["Shortcut"]); | 814 | var row = this.CreateRow(tuple, "Shortcut"); |
841 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
842 | row[0] = tuple.Id.Id; | 815 | row[0] = tuple.Id.Id; |
843 | row[1] = tuple.DirectoryRef; | 816 | row[1] = tuple.DirectoryRef; |
844 | row[2] = GetMsiFilenameValue(tuple.ShortName, tuple.Name); | 817 | row[2] = GetMsiFilenameValue(tuple.ShortName, tuple.Name); |
@@ -857,7 +830,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
857 | row[15] = tuple.DescriptionResourceId; | 830 | row[15] = tuple.DescriptionResourceId; |
858 | } | 831 | } |
859 | 832 | ||
860 | private void AddTextStyleTuple(TextStyleTuple tuple, WindowsInstallerData output) | 833 | private void AddTextStyleTuple(TextStyleTuple tuple) |
861 | { | 834 | { |
862 | var styleBits = tuple.Bold ? WindowsInstallerConstants.MsidbTextStyleStyleBitsBold : 0; | 835 | var styleBits = tuple.Bold ? WindowsInstallerConstants.MsidbTextStyleStyleBitsBold : 0; |
863 | styleBits |= tuple.Italic ? WindowsInstallerConstants.MsidbTextStyleStyleBitsItalic : 0; | 836 | styleBits |= tuple.Italic ? WindowsInstallerConstants.MsidbTextStyleStyleBitsItalic : 0; |
@@ -873,8 +846,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
873 | color += (long)(tuple.Blue ?? 0) * 65536; | 846 | color += (long)(tuple.Blue ?? 0) * 65536; |
874 | } | 847 | } |
875 | 848 | ||
876 | var table = output.EnsureTable(this.TableDefinitions["TextStyle"]); | 849 | var row = this.CreateRow(tuple, "TextStyle"); |
877 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
878 | row[0] = tuple.Id.Id; | 850 | row[0] = tuple.Id.Id; |
879 | row[1] = tuple.FaceName; | 851 | row[1] = tuple.FaceName; |
880 | row[2] = tuple.Size; | 852 | row[2] = tuple.Size; |
@@ -882,10 +854,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
882 | row[4] = styleBits == 0 ? null : (int?)styleBits; | 854 | row[4] = styleBits == 0 ? null : (int?)styleBits; |
883 | } | 855 | } |
884 | 856 | ||
885 | private void AddUpgradeTuple(UpgradeTuple tuple, WindowsInstallerData output) | 857 | private void AddUpgradeTuple(UpgradeTuple tuple) |
886 | { | 858 | { |
887 | var table = output.EnsureTable(this.TableDefinitions["Upgrade"]); | 859 | var row = (UpgradeRow)this.CreateRow(tuple, "Upgrade"); |
888 | var row = (UpgradeRow)table.CreateRow(tuple.SourceLineNumbers); | ||
889 | row.UpgradeCode = tuple.UpgradeCode; | 860 | row.UpgradeCode = tuple.UpgradeCode; |
890 | row.VersionMin = tuple.VersionMin; | 861 | row.VersionMin = tuple.VersionMin; |
891 | row.VersionMax = tuple.VersionMax; | 862 | row.VersionMax = tuple.VersionMax; |
@@ -902,72 +873,71 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
902 | row.Attributes = attributes; | 873 | row.Attributes = attributes; |
903 | } | 874 | } |
904 | 875 | ||
905 | private void AddWixActionTuple(WixActionTuple tuple, WindowsInstallerData output) | 876 | private void AddWixActionTuple(WixActionTuple tuple) |
906 | { | 877 | { |
907 | // Get the table definition for the action (and ensure the proper table exists for a module). | 878 | // Get the table definition for the action (and ensure the proper table exists for a module). |
908 | TableDefinition sequenceTableDefinition = null; | 879 | string sequenceTableName = null; |
909 | switch (tuple.SequenceTable) | 880 | switch (tuple.SequenceTable) |
910 | { | 881 | { |
911 | case SequenceTable.AdminExecuteSequence: | 882 | case SequenceTable.AdminExecuteSequence: |
912 | if (OutputType.Module == output.Type) | 883 | if (OutputType.Module == this.Output.Type) |
913 | { | 884 | { |
914 | output.EnsureTable(this.TableDefinitions["AdminExecuteSequence"]); | 885 | this.Output.EnsureTable(this.TableDefinitions["AdminExecuteSequence"]); |
915 | sequenceTableDefinition = this.TableDefinitions["ModuleAdminExecuteSequence"]; | 886 | sequenceTableName = "ModuleAdminExecuteSequence"; |
916 | } | 887 | } |
917 | else | 888 | else |
918 | { | 889 | { |
919 | sequenceTableDefinition = this.TableDefinitions["AdminExecuteSequence"]; | 890 | sequenceTableName = "AdminExecuteSequence"; |
920 | } | 891 | } |
921 | break; | 892 | break; |
922 | case SequenceTable.AdminUISequence: | 893 | case SequenceTable.AdminUISequence: |
923 | if (OutputType.Module == output.Type) | 894 | if (OutputType.Module == this.Output.Type) |
924 | { | 895 | { |
925 | output.EnsureTable(this.TableDefinitions["AdminUISequence"]); | 896 | this.Output.EnsureTable(this.TableDefinitions["AdminUISequence"]); |
926 | sequenceTableDefinition = this.TableDefinitions["ModuleAdminUISequence"]; | 897 | sequenceTableName = "ModuleAdminUISequence"; |
927 | } | 898 | } |
928 | else | 899 | else |
929 | { | 900 | { |
930 | sequenceTableDefinition = this.TableDefinitions["AdminUISequence"]; | 901 | sequenceTableName = "AdminUISequence"; |
931 | } | 902 | } |
932 | break; | 903 | break; |
933 | case SequenceTable.AdvertiseExecuteSequence: | 904 | case SequenceTable.AdvertiseExecuteSequence: |
934 | if (OutputType.Module == output.Type) | 905 | if (OutputType.Module == this.Output.Type) |
935 | { | 906 | { |
936 | output.EnsureTable(this.TableDefinitions["AdvtExecuteSequence"]); | 907 | this.Output.EnsureTable(this.TableDefinitions["AdvtExecuteSequence"]); |
937 | sequenceTableDefinition = this.TableDefinitions["ModuleAdvtExecuteSequence"]; | 908 | sequenceTableName = "ModuleAdvtExecuteSequence"; |
938 | } | 909 | } |
939 | else | 910 | else |
940 | { | 911 | { |
941 | sequenceTableDefinition = this.TableDefinitions["AdvtExecuteSequence"]; | 912 | sequenceTableName = "AdvtExecuteSequence"; |
942 | } | 913 | } |
943 | break; | 914 | break; |
944 | case SequenceTable.InstallExecuteSequence: | 915 | case SequenceTable.InstallExecuteSequence: |
945 | if (OutputType.Module == output.Type) | 916 | if (OutputType.Module == this.Output.Type) |
946 | { | 917 | { |
947 | output.EnsureTable(this.TableDefinitions["InstallExecuteSequence"]); | 918 | this.Output.EnsureTable(this.TableDefinitions["InstallExecuteSequence"]); |
948 | sequenceTableDefinition = this.TableDefinitions["ModuleInstallExecuteSequence"]; | 919 | sequenceTableName = "ModuleInstallExecuteSequence"; |
949 | } | 920 | } |
950 | else | 921 | else |
951 | { | 922 | { |
952 | sequenceTableDefinition = this.TableDefinitions["InstallExecuteSequence"]; | 923 | sequenceTableName = "InstallExecuteSequence"; |
953 | } | 924 | } |
954 | break; | 925 | break; |
955 | case SequenceTable.InstallUISequence: | 926 | case SequenceTable.InstallUISequence: |
956 | if (OutputType.Module == output.Type) | 927 | if (OutputType.Module == this.Output.Type) |
957 | { | 928 | { |
958 | output.EnsureTable(this.TableDefinitions["InstallUISequence"]); | 929 | this.Output.EnsureTable(this.TableDefinitions["InstallUISequence"]); |
959 | sequenceTableDefinition = this.TableDefinitions["ModuleInstallUISequence"]; | 930 | sequenceTableName = "ModuleInstallUISequence"; |
960 | } | 931 | } |
961 | else | 932 | else |
962 | { | 933 | { |
963 | sequenceTableDefinition = this.TableDefinitions["InstallUISequence"]; | 934 | sequenceTableName = "InstallUISequence"; |
964 | } | 935 | } |
965 | break; | 936 | break; |
966 | } | 937 | } |
967 | 938 | ||
968 | // create the action sequence row in the output | 939 | // create the action sequence row in the output |
969 | var sequenceTable = output.EnsureTable(sequenceTableDefinition); | 940 | var row = this.CreateRow(tuple, sequenceTableName); |
970 | var row = sequenceTable.CreateRow(tuple.SourceLineNumbers); | ||
971 | 941 | ||
972 | if (SectionType.Module == this.Section.Type) | 942 | if (SectionType.Module == this.Section.Type) |
973 | { | 943 | { |
@@ -992,7 +962,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
992 | } | 962 | } |
993 | } | 963 | } |
994 | 964 | ||
995 | private void AddWixCustomRowTuple(WixCustomRowTuple tuple, WindowsInstallerData output) | 965 | private void AddWixCustomRowTuple(WixCustomRowTuple tuple) |
996 | { | 966 | { |
997 | var customTableDefinition = this.TableDefinitions[tuple.Table]; | 967 | var customTableDefinition = this.TableDefinitions[tuple.Table]; |
998 | 968 | ||
@@ -1002,8 +972,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
1002 | return; | 972 | return; |
1003 | } | 973 | } |
1004 | 974 | ||
1005 | var customTable = output.EnsureTable(customTableDefinition); | 975 | var customRow = this.CreateRow(tuple, customTableDefinition); |
1006 | var customRow = customTable.CreateRow(tuple.SourceLineNumbers); | ||
1007 | 976 | ||
1008 | #if TODO // SectionId seems like a good thing to preserve. | 977 | #if TODO // SectionId seems like a good thing to preserve. |
1009 | customRow.SectionId = tuple.SectionId; | 978 | customRow.SectionId = tuple.SectionId; |
@@ -1073,16 +1042,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
1073 | } | 1042 | } |
1074 | } | 1043 | } |
1075 | 1044 | ||
1076 | private void AddWixEnsureTableTuple(WixEnsureTableTuple tuple, WindowsInstallerData output) | 1045 | private void AddWixEnsureTableTuple(WixEnsureTableTuple tuple) |
1077 | { | 1046 | { |
1078 | var tableDefinition = this.TableDefinitions[tuple.Table]; | 1047 | var tableDefinition = this.TableDefinitions[tuple.Table]; |
1079 | output.EnsureTable(tableDefinition); | 1048 | this.Output.EnsureTable(tableDefinition); |
1080 | } | 1049 | } |
1081 | 1050 | ||
1082 | private void AddWixMediaTemplateTuple(WixMediaTemplateTuple tuple, WindowsInstallerData output) | 1051 | private void AddWixMediaTemplateTuple(WixMediaTemplateTuple tuple) |
1083 | { | 1052 | { |
1084 | var table = output.EnsureTable(this.TableDefinitions["WixMediaTemplate"]); | 1053 | var row = (WixMediaTemplateRow)this.CreateRow(tuple, "WixMediaTemplate"); |
1085 | var row = (WixMediaTemplateRow)table.CreateRow(tuple.SourceLineNumbers); | ||
1086 | row.CabinetTemplate = tuple.CabinetTemplate; | 1054 | row.CabinetTemplate = tuple.CabinetTemplate; |
1087 | row.CompressionLevel = tuple.CompressionLevel; | 1055 | row.CompressionLevel = tuple.CompressionLevel; |
1088 | row.DiskPrompt = tuple.DiskPrompt; | 1056 | row.DiskPrompt = tuple.DiskPrompt; |
@@ -1091,26 +1059,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
1091 | row.MaximumCabinetSizeForLargeFileSplitting = tuple.MaximumCabinetSizeForLargeFileSplitting ?? MaxValueOfMaxCabSizeForLargeFileSplitting; | 1059 | row.MaximumCabinetSizeForLargeFileSplitting = tuple.MaximumCabinetSizeForLargeFileSplitting ?? MaxValueOfMaxCabSizeForLargeFileSplitting; |
1092 | } | 1060 | } |
1093 | 1061 | ||
1094 | private void AddTupleFromExtension(IntermediateTuple tuple, WindowsInstallerData output) | 1062 | private void AddTupleFromExtension(IntermediateTuple tuple) |
1095 | { | 1063 | { |
1096 | foreach (var extension in this.BackendExtensions) | 1064 | foreach (var extension in this.BackendExtensions) |
1097 | { | 1065 | { |
1098 | if (extension.TryAddTupleToOutput(tuple, output)) | 1066 | if (extension.TryAddTupleToOutput(tuple, this.Output)) |
1099 | { | 1067 | { |
1100 | break; | 1068 | break; |
1101 | } | 1069 | } |
1102 | } | 1070 | } |
1103 | } | 1071 | } |
1104 | 1072 | ||
1105 | private void AddTupleDefaultly(IntermediateTuple tuple, WindowsInstallerData output, bool idIsPrimaryKey = false, string tableName = null) | 1073 | private void AddTupleDefaultly(IntermediateTuple tuple, bool idIsPrimaryKey = false, string tableName = null) |
1106 | { | 1074 | { |
1107 | if (!this.TableDefinitions.TryGet(tableName ?? tuple.Definition.Name, out var tableDefinition)) | 1075 | if (!this.TableDefinitions.TryGet(tableName ?? tuple.Definition.Name, out var tableDefinition)) |
1108 | { | 1076 | { |
1109 | return; | 1077 | return; |
1110 | } | 1078 | } |
1111 | 1079 | ||
1112 | var table = output.EnsureTable(tableDefinition); | 1080 | var row = this.CreateRow(tuple, tableDefinition); |
1113 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
1114 | var rowOffset = 0; | 1081 | var rowOffset = 0; |
1115 | 1082 | ||
1116 | if (idIsPrimaryKey) | 1083 | if (idIsPrimaryKey) |
@@ -1159,6 +1126,18 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
1159 | } | 1126 | } |
1160 | } | 1127 | } |
1161 | 1128 | ||
1129 | private Row CreateRow(IntermediateTuple tuple, string tableDefinitionName) => this.CreateRow(tuple, this.TableDefinitions[tableDefinitionName]); | ||
1130 | |||
1131 | private Row CreateRow(IntermediateTuple tuple, TableDefinition tableDefinition) | ||
1132 | { | ||
1133 | var table = this.Output.EnsureTable(tableDefinition); | ||
1134 | |||
1135 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
1136 | row.SectionId = this.Section.Id; | ||
1137 | |||
1138 | return row; | ||
1139 | } | ||
1140 | |||
1162 | private static string GetMsiFilenameValue(string shortName, string longName) | 1141 | private static string GetMsiFilenameValue(string shortName, string longName) |
1163 | { | 1142 | { |
1164 | if (String.IsNullOrEmpty(shortName) || String.Equals(shortName, longName, StringComparison.OrdinalIgnoreCase)) | 1143 | if (String.IsNullOrEmpty(shortName) || String.Equals(shortName, longName, StringComparison.OrdinalIgnoreCase)) |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs new file mode 100644 index 00000000..854d973e --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs | |||
@@ -0,0 +1,90 @@ | |||
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 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.IO; | ||
8 | using System.Linq; | ||
9 | using WixToolset.Core.WindowsInstaller.Msi; | ||
10 | using WixToolset.Core.WindowsInstaller.Unbind; | ||
11 | using WixToolset.Data; | ||
12 | using WixToolset.Data.Tuples; | ||
13 | using WixToolset.Data.WindowsInstaller; | ||
14 | using WixToolset.Extensibility.Services; | ||
15 | |||
16 | internal class CreatePatchTransformsCommand | ||
17 | { | ||
18 | public CreatePatchTransformsCommand(IMessaging messaging, Intermediate intermediate, string intermediateFolder) | ||
19 | { | ||
20 | this.Messaging = messaging; | ||
21 | this.Intermediate = intermediate; | ||
22 | this.IntermediateFolder = intermediateFolder; | ||
23 | } | ||
24 | |||
25 | private IMessaging Messaging { get; } | ||
26 | |||
27 | private Intermediate Intermediate { get; } | ||
28 | |||
29 | private string IntermediateFolder { get; } | ||
30 | |||
31 | public IEnumerable<PatchTransform> PatchTransforms { get; private set; } | ||
32 | |||
33 | public IEnumerable<PatchTransform> Execute() | ||
34 | { | ||
35 | var patchTransforms = new List<PatchTransform>(); | ||
36 | |||
37 | var tuples = this.Intermediate.Sections.SelectMany(s => s.Tuples).OfType<WixPatchBaselineTuple>(); | ||
38 | |||
39 | foreach (var tuple in tuples) | ||
40 | { | ||
41 | WindowsInstallerData transform; | ||
42 | |||
43 | if (tuple.TransformFile is null) | ||
44 | { | ||
45 | var baselineData = this.GetData(tuple.BaselineFile.Path); | ||
46 | var updateData = this.GetData(tuple.UpdateFile.Path); | ||
47 | |||
48 | var command = new GenerateTransformCommand(this.Messaging, baselineData, updateData, false); | ||
49 | transform = command.Execute(); | ||
50 | } | ||
51 | else | ||
52 | { | ||
53 | var exportBasePath = Path.Combine(this.IntermediateFolder, "_trans"); // TODO: come up with a better path. | ||
54 | |||
55 | var command = new UnbindTransformCommand(this.Messaging, tuple.TransformFile.Path, exportBasePath, this.IntermediateFolder); | ||
56 | transform = command.Execute(); | ||
57 | } | ||
58 | |||
59 | patchTransforms.Add(new PatchTransform(tuple.Id.Id, transform)); | ||
60 | } | ||
61 | |||
62 | this.PatchTransforms = patchTransforms; | ||
63 | |||
64 | return this.PatchTransforms; | ||
65 | } | ||
66 | |||
67 | private WindowsInstallerData GetData(string path) | ||
68 | { | ||
69 | var ext = Path.GetExtension(path); | ||
70 | |||
71 | if (".msi".Equals(ext, StringComparison.OrdinalIgnoreCase)) | ||
72 | { | ||
73 | using (var database = new Database(path, OpenDatabase.ReadOnly)) | ||
74 | { | ||
75 | var exportBasePath = Path.Combine(this.IntermediateFolder, "_msi"); // TODO: come up with a better path. | ||
76 | |||
77 | var isAdminImage = false; // TODO: need a better way to set this | ||
78 | |||
79 | var command = new UnbindDatabaseCommand(this.Messaging, database, path, OutputType.Product, exportBasePath, this.IntermediateFolder, isAdminImage, suppressDemodularization: true, skipSummaryInfo: true); | ||
80 | return command.Execute(); | ||
81 | } | ||
82 | } | ||
83 | else // assume .wixpdb (or .wixout) | ||
84 | { | ||
85 | var data = WindowsInstallerData.Load(path, true); | ||
86 | return data; | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | } | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs index 5412c6f9..49b6a6f8 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs | |||
@@ -48,7 +48,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
48 | { | 48 | { |
49 | var mergeModulesFileFacades = new List<FileFacade>(); | 49 | var mergeModulesFileFacades = new List<FileFacade>(); |
50 | 50 | ||
51 | IMsmMerge2 merge = MsmInterop.GetMsmMerge(); | 51 | var merge = MsmInterop.GetMsmMerge(); |
52 | 52 | ||
53 | // Index all of the file rows to be able to detect collisions with files in the Merge Modules. | 53 | // Index all of the file rows to be able to detect collisions with files in the Merge Modules. |
54 | // It may seem a bit expensive to build up this index solely for the purpose of checking collisions | 54 | // It may seem a bit expensive to build up this index solely for the purpose of checking collisions |
@@ -57,11 +57,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
57 | // Now since Merge Modules are already slow and generally less desirable than .wixlibs we'll let | 57 | // Now since Merge Modules are already slow and generally less desirable than .wixlibs we'll let |
58 | // this case be slightly more expensive because the cost of maintaining an indexed file row collection | 58 | // this case be slightly more expensive because the cost of maintaining an indexed file row collection |
59 | // is a lot more costly for the common cases. | 59 | // is a lot more costly for the common cases. |
60 | var indexedFileFacades = this.FileFacades.ToDictionary(f => f.File.Id.Id, StringComparer.Ordinal); | 60 | var indexedFileFacades = this.FileFacades.ToDictionary(f => f.Id, StringComparer.Ordinal); |
61 | 61 | ||
62 | foreach (var wixMergeRow in this.WixMergeTuples) | 62 | foreach (var wixMergeRow in this.WixMergeTuples) |
63 | { | 63 | { |
64 | bool containsFiles = this.CreateFacadesForMergeModuleFiles(wixMergeRow, mergeModulesFileFacades, indexedFileFacades); | 64 | var containsFiles = this.CreateFacadesForMergeModuleFiles(wixMergeRow, mergeModulesFileFacades, indexedFileFacades); |
65 | 65 | ||
66 | // If the module has files and creating layout | 66 | // If the module has files and creating layout |
67 | if (containsFiles && !this.SuppressLayout) | 67 | if (containsFiles && !this.SuppressLayout) |
@@ -75,21 +75,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
75 | 75 | ||
76 | private bool CreateFacadesForMergeModuleFiles(WixMergeTuple wixMergeRow, List<FileFacade> mergeModulesFileFacades, Dictionary<string, FileFacade> indexedFileFacades) | 76 | private bool CreateFacadesForMergeModuleFiles(WixMergeTuple wixMergeRow, List<FileFacade> mergeModulesFileFacades, Dictionary<string, FileFacade> indexedFileFacades) |
77 | { | 77 | { |
78 | bool containsFiles = false; | 78 | var containsFiles = false; |
79 | 79 | ||
80 | try | 80 | try |
81 | { | 81 | { |
82 | // read the module's File table to get its FileMediaInformation entries and gather any other information needed from the module. | 82 | // read the module's File table to get its FileMediaInformation entries and gather any other information needed from the module. |
83 | using (Database db = new Database(wixMergeRow.SourceFile, OpenDatabase.ReadOnly)) | 83 | using (var db = new Database(wixMergeRow.SourceFile, OpenDatabase.ReadOnly)) |
84 | { | 84 | { |
85 | if (db.TableExists("File") && db.TableExists("Component")) | 85 | if (db.TableExists("File") && db.TableExists("Component")) |
86 | { | 86 | { |
87 | Dictionary<string, FileFacade> uniqueModuleFileIdentifiers = new Dictionary<string, FileFacade>(StringComparer.OrdinalIgnoreCase); | 87 | var uniqueModuleFileIdentifiers = new Dictionary<string, FileFacade>(StringComparer.OrdinalIgnoreCase); |
88 | 88 | ||
89 | using (View view = db.OpenExecuteView("SELECT `File`, `Directory_` FROM `File`, `Component` WHERE `Component_`=`Component`")) | 89 | using (var view = db.OpenExecuteView("SELECT `File`, `Directory_` FROM `File`, `Component` WHERE `Component_`=`Component`")) |
90 | { | 90 | { |
91 | // add each file row from the merge module into the file row collection (check for errors along the way) | 91 | // add each file row from the merge module into the file row collection (check for errors along the way) |
92 | foreach (Record record in view.Records) | 92 | foreach (var record in view.Records) |
93 | { | 93 | { |
94 | // NOTE: this is very tricky - the merge module file rows are not added to the | 94 | // NOTE: this is very tricky - the merge module file rows are not added to the |
95 | // file table because they should not be created via idt import. Instead, these | 95 | // file table because they should not be created via idt import. Instead, these |
@@ -103,21 +103,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
103 | var mergeModuleFileFacade = new FileFacade(true, fileTuple); | 103 | var mergeModuleFileFacade = new FileFacade(true, fileTuple); |
104 | 104 | ||
105 | // If case-sensitive collision with another merge module or a user-authored file identifier. | 105 | // If case-sensitive collision with another merge module or a user-authored file identifier. |
106 | if (indexedFileFacades.TryGetValue(mergeModuleFileFacade.File.Id.Id, out var collidingFacade)) | 106 | if (indexedFileFacades.TryGetValue(mergeModuleFileFacade.Id, out var collidingFacade)) |
107 | { | 107 | { |
108 | this.Messaging.Write(ErrorMessages.DuplicateModuleFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, collidingFacade.File.Id.Id)); | 108 | this.Messaging.Write(ErrorMessages.DuplicateModuleFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, collidingFacade.Id)); |
109 | } | 109 | } |
110 | else if (uniqueModuleFileIdentifiers.TryGetValue(mergeModuleFileFacade.File.Id.Id, out collidingFacade)) // case-insensitive collision with another file identifier in the same merge module | 110 | else if (uniqueModuleFileIdentifiers.TryGetValue(mergeModuleFileFacade.Id, out collidingFacade)) // case-insensitive collision with another file identifier in the same merge module |
111 | { | 111 | { |
112 | this.Messaging.Write(ErrorMessages.DuplicateModuleCaseInsensitiveFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, mergeModuleFileFacade.File.Id.Id, collidingFacade.File.Id.Id)); | 112 | this.Messaging.Write(ErrorMessages.DuplicateModuleCaseInsensitiveFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, mergeModuleFileFacade.Id, collidingFacade.Id)); |
113 | } | 113 | } |
114 | else // no collision | 114 | else // no collision |
115 | { | 115 | { |
116 | mergeModulesFileFacades.Add(mergeModuleFileFacade); | 116 | mergeModulesFileFacades.Add(mergeModuleFileFacade); |
117 | 117 | ||
118 | // Keep updating the indexes as new rows are added. | 118 | // Keep updating the indexes as new rows are added. |
119 | indexedFileFacades.Add(mergeModuleFileFacade.File.Id.Id, mergeModuleFileFacade); | 119 | indexedFileFacades.Add(mergeModuleFileFacade.Id, mergeModuleFileFacade); |
120 | uniqueModuleFileIdentifiers.Add(mergeModuleFileFacade.File.Id.Id, mergeModuleFileFacade); | 120 | uniqueModuleFileIdentifiers.Add(mergeModuleFileFacade.Id, mergeModuleFileFacade); |
121 | } | 121 | } |
122 | 122 | ||
123 | containsFiles = true; | 123 | containsFiles = true; |
@@ -126,13 +126,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
126 | } | 126 | } |
127 | 127 | ||
128 | // Get the summary information to detect the Schema | 128 | // Get the summary information to detect the Schema |
129 | using (SummaryInformation summaryInformation = new SummaryInformation(db)) | 129 | using (var summaryInformation = new SummaryInformation(db)) |
130 | { | 130 | { |
131 | string moduleInstallerVersionString = summaryInformation.GetProperty(14); | 131 | var moduleInstallerVersionString = summaryInformation.GetProperty(14); |
132 | 132 | ||
133 | try | 133 | try |
134 | { | 134 | { |
135 | int moduleInstallerVersion = Convert.ToInt32(moduleInstallerVersionString, CultureInfo.InvariantCulture); | 135 | var moduleInstallerVersion = Convert.ToInt32(moduleInstallerVersionString, CultureInfo.InvariantCulture); |
136 | if (moduleInstallerVersion > this.OutputInstallerVersion) | 136 | if (moduleInstallerVersion > this.OutputInstallerVersion) |
137 | { | 137 | { |
138 | this.Messaging.Write(WarningMessages.InvalidHigherInstallerVersionInModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, moduleInstallerVersion, this.OutputInstallerVersion)); | 138 | this.Messaging.Write(WarningMessages.InvalidHigherInstallerVersionInModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, moduleInstallerVersion, this.OutputInstallerVersion)); |
@@ -159,7 +159,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
159 | 159 | ||
160 | private void ExtractFilesFromMergeModule(IMsmMerge2 merge, WixMergeTuple wixMergeRow) | 160 | private void ExtractFilesFromMergeModule(IMsmMerge2 merge, WixMergeTuple wixMergeRow) |
161 | { | 161 | { |
162 | bool moduleOpen = false; | 162 | var moduleOpen = false; |
163 | short mergeLanguage; | 163 | short mergeLanguage; |
164 | 164 | ||
165 | var mergeId = wixMergeRow.Id.Id; | 165 | var mergeId = wixMergeRow.Id.Id; |
@@ -180,10 +180,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
180 | moduleOpen = true; | 180 | moduleOpen = true; |
181 | 181 | ||
182 | // extract the module cabinet, then explode all of the files to a temp directory | 182 | // extract the module cabinet, then explode all of the files to a temp directory |
183 | string moduleCabPath = Path.Combine(this.IntermediateFolder, mergeId + ".cab"); | 183 | var moduleCabPath = Path.Combine(this.IntermediateFolder, mergeId + ".cab"); |
184 | merge.ExtractCAB(moduleCabPath); | 184 | merge.ExtractCAB(moduleCabPath); |
185 | 185 | ||
186 | string mergeIdPath = Path.Combine(this.IntermediateFolder, mergeId); | 186 | var mergeIdPath = Path.Combine(this.IntermediateFolder, mergeId); |
187 | Directory.CreateDirectory(mergeIdPath); | 187 | Directory.CreateDirectory(mergeIdPath); |
188 | 188 | ||
189 | try | 189 | try |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs index 6b365ecd..ed3b6f01 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs | |||
@@ -5,367 +5,396 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
5 | using System; | 5 | using System; |
6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
7 | using System.ComponentModel; | 7 | using System.ComponentModel; |
8 | using System.Globalization; | ||
9 | using System.IO; | 8 | using System.IO; |
10 | using System.Text; | 9 | using System.Text; |
10 | using WixToolset.Core.WindowsInstaller.Msi; | ||
11 | using WixToolset.Data; | 11 | using WixToolset.Data; |
12 | using WixToolset.Extensibility; | ||
13 | using WixToolset.Data.WindowsInstaller; | 12 | using WixToolset.Data.WindowsInstaller; |
14 | using WixToolset.Extensibility.Services; | 13 | using WixToolset.Extensibility; |
15 | using WixToolset.Extensibility.Data; | 14 | using WixToolset.Extensibility.Data; |
16 | using WixToolset.Core.WindowsInstaller.Msi; | 15 | using WixToolset.Extensibility.Services; |
17 | 16 | ||
18 | internal class GenerateDatabaseCommand | 17 | internal class GenerateDatabaseCommand |
19 | { | 18 | { |
20 | public int Codepage { private get; set; } | 19 | public GenerateDatabaseCommand(IMessaging messaging, IBackendHelper backendHelper, IEnumerable<IFileSystemExtension> fileSystemExtensions, WindowsInstallerData data, string outputPath, TableDefinitionCollection tableDefinitions, string intermediateFolder, int codepage, bool keepAddedColumns, bool suppressAddingValidationRows, bool useSubdirectory) |
20 | { | ||
21 | this.Messaging = messaging; | ||
22 | this.BackendHelper = backendHelper; | ||
23 | this.Extensions = fileSystemExtensions; | ||
24 | this.Data = data; | ||
25 | this.OutputPath = outputPath; | ||
26 | this.TableDefinitions = tableDefinitions; | ||
27 | this.IntermediateFolder = intermediateFolder; | ||
28 | this.Codepage = codepage; | ||
29 | this.KeepAddedColumns = keepAddedColumns; | ||
30 | this.SuppressAddingValidationRows = suppressAddingValidationRows; | ||
31 | this.UseSubDirectory = useSubdirectory; | ||
32 | } | ||
33 | |||
34 | private int Codepage { get; } | ||
21 | 35 | ||
22 | public IBackendHelper BackendHelper { private get; set; } | 36 | private IBackendHelper BackendHelper { get; } |
23 | 37 | ||
24 | public IEnumerable<IFileSystemExtension> Extensions { private get; set; } | 38 | private IEnumerable<IFileSystemExtension> Extensions { get; } |
25 | 39 | ||
26 | /// <summary> | 40 | /// <summary> |
27 | /// Whether to keep columns added in a transform. | 41 | /// Whether to keep columns added in a transform. |
28 | /// </summary> | 42 | /// </summary> |
29 | public bool KeepAddedColumns { private get; set; } | 43 | private bool KeepAddedColumns { get; } |
30 | 44 | ||
31 | public IMessaging Messaging { private get; set; } | 45 | private IMessaging Messaging { get; } |
32 | 46 | ||
33 | public WindowsInstallerData Output { private get; set; } | 47 | private WindowsInstallerData Data { get; } |
34 | 48 | ||
35 | public string OutputPath { private get; set; } | 49 | private string OutputPath { get; } |
36 | 50 | ||
37 | public TableDefinitionCollection TableDefinitions { private get; set; } | 51 | private TableDefinitionCollection TableDefinitions { get; } |
38 | 52 | ||
39 | public string IntermediateFolder { private get; set; } | 53 | private string IntermediateFolder { get; } |
40 | 54 | ||
41 | public List<ITrackedFile> GeneratedTemporaryFiles { get; } = new List<ITrackedFile>(); | 55 | public List<ITrackedFile> GeneratedTemporaryFiles { get; } = new List<ITrackedFile>(); |
42 | 56 | ||
43 | /// <summary> | 57 | /// <summary> |
44 | /// Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files. | 58 | /// Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files. |
45 | /// </summary> | 59 | /// </summary> |
46 | public bool SuppressAddingValidationRows { private get; set; } | 60 | private bool SuppressAddingValidationRows { get; } |
47 | 61 | ||
48 | public bool UseSubDirectory { private get; set; } | 62 | private bool UseSubDirectory { get; } |
49 | 63 | ||
50 | public void Execute() | 64 | public void Execute() |
51 | { | 65 | { |
52 | // Add the _Validation rows. | 66 | // Add the _Validation rows. |
53 | if (!this.SuppressAddingValidationRows) | 67 | if (!this.SuppressAddingValidationRows) |
54 | { | 68 | { |
55 | var validationTable = this.Output.EnsureTable(this.TableDefinitions["_Validation"]); | 69 | this.AddValidationRows(); |
56 | |||
57 | foreach (var table in this.Output.Tables) | ||
58 | { | ||
59 | if (!table.Definition.Unreal) | ||
60 | { | ||
61 | // Add the validation rows for this table. | ||
62 | foreach (ColumnDefinition columnDef in table.Definition.Columns) | ||
63 | { | ||
64 | var row = validationTable.CreateRow(null); | ||
65 | |||
66 | row[0] = table.Name; | ||
67 | |||
68 | row[1] = columnDef.Name; | ||
69 | |||
70 | if (columnDef.Nullable) | ||
71 | { | ||
72 | row[2] = "Y"; | ||
73 | } | ||
74 | else | ||
75 | { | ||
76 | row[2] = "N"; | ||
77 | } | ||
78 | |||
79 | if (columnDef.MinValue.HasValue) | ||
80 | { | ||
81 | row[3] = columnDef.MinValue.Value; | ||
82 | } | ||
83 | |||
84 | if (columnDef.MaxValue.HasValue) | ||
85 | { | ||
86 | row[4] = columnDef.MaxValue.Value; | ||
87 | } | ||
88 | |||
89 | row[5] = columnDef.KeyTable; | ||
90 | |||
91 | if (columnDef.KeyColumn.HasValue) | ||
92 | { | ||
93 | row[6] = columnDef.KeyColumn.Value; | ||
94 | } | ||
95 | |||
96 | if (ColumnCategory.Unknown != columnDef.Category) | ||
97 | { | ||
98 | row[7] = columnDef.Category.ToString(); | ||
99 | } | ||
100 | |||
101 | row[8] = columnDef.Possibilities; | ||
102 | |||
103 | row[9] = columnDef.Description; | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | } | 70 | } |
108 | 71 | ||
109 | // Set the base directory. | ||
110 | var baseDirectory = this.IntermediateFolder; | 72 | var baseDirectory = this.IntermediateFolder; |
111 | 73 | ||
112 | if (this.UseSubDirectory) | 74 | if (this.UseSubDirectory) |
113 | { | 75 | { |
114 | string filename = Path.GetFileNameWithoutExtension(this.OutputPath); | 76 | var filename = Path.GetFileNameWithoutExtension(this.OutputPath); |
115 | baseDirectory = Path.Combine(baseDirectory, filename); | 77 | baseDirectory = Path.Combine(baseDirectory, filename); |
116 | |||
117 | // make sure the directory exists | ||
118 | Directory.CreateDirectory(baseDirectory); | ||
119 | } | 78 | } |
120 | 79 | ||
121 | var idtDirectory = Path.Combine(baseDirectory, "_idts"); | 80 | var idtFolder = Path.Combine(baseDirectory, "_idts"); |
122 | Directory.CreateDirectory(idtDirectory); | ||
123 | 81 | ||
124 | try | 82 | var type = OpenDatabase.CreateDirect; |
83 | |||
84 | if (OutputType.Patch == this.Data.Type) | ||
125 | { | 85 | { |
126 | OpenDatabase type = OpenDatabase.CreateDirect; | 86 | type |= OpenDatabase.OpenPatchFile; |
87 | } | ||
127 | 88 | ||
128 | // set special flag for patch files | 89 | // Localize the codepage if a value was specified directly. |
129 | if (OutputType.Patch == this.Output.Type) | 90 | if (-1 != this.Codepage) |
130 | { | 91 | { |
131 | type |= OpenDatabase.OpenPatchFile; | 92 | this.Data.Codepage = this.Codepage; |
132 | } | 93 | } |
133 | 94 | ||
95 | try | ||
96 | { | ||
134 | #if DEBUG | 97 | #if DEBUG |
135 | Console.WriteLine("Opening database at: {0}", this.OutputPath); | 98 | Console.WriteLine("Opening database at: {0}", this.OutputPath); |
136 | #endif | 99 | #endif |
137 | 100 | ||
138 | // Localize the codepage if a value was specified directly. | ||
139 | if (-1 != this.Codepage) | ||
140 | { | ||
141 | this.Output.Codepage = this.Codepage; | ||
142 | } | ||
143 | |||
144 | Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); | 101 | Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); |
145 | 102 | ||
146 | using (Database db = new Database(this.OutputPath, type)) | 103 | Directory.CreateDirectory(idtFolder); |
104 | |||
105 | using (var db = new Database(this.OutputPath, type)) | ||
147 | { | 106 | { |
148 | // if we're not using the default codepage, import a new one into our | 107 | // If we're not using the default codepage, import a new one into our |
149 | // database before we add any tables (or the tables would be added | 108 | // database before we add any tables (or the tables would be added |
150 | // with the wrong codepage). | 109 | // with the wrong codepage). |
151 | if (0 != this.Output.Codepage) | 110 | if (0 != this.Data.Codepage) |
152 | { | 111 | { |
153 | this.SetDatabaseCodepage(db, this.Output.Codepage, idtDirectory); | 112 | this.SetDatabaseCodepage(db, this.Data.Codepage, idtFolder); |
154 | } | 113 | } |
155 | 114 | ||
156 | foreach (Table table in this.Output.Tables) | 115 | this.ImportTables(db, idtFolder); |
116 | |||
117 | // Insert substorages (usually transforms inside a patch or instance transforms in a package). | ||
118 | this.ImportSubStorages(db); | ||
119 | |||
120 | // We're good, commit the changes to the new database. | ||
121 | db.Commit(); | ||
122 | } | ||
123 | } | ||
124 | catch (IOException e) | ||
125 | { | ||
126 | // TODO: this error message doesn't seem specific enough | ||
127 | throw new WixException(ErrorMessages.FileNotFound(new SourceLineNumber(this.OutputPath), this.OutputPath), e); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | private void AddValidationRows() | ||
132 | { | ||
133 | var validationTable = this.Data.EnsureTable(this.TableDefinitions["_Validation"]); | ||
134 | |||
135 | foreach (var table in this.Data.Tables) | ||
136 | { | ||
137 | if (!table.Definition.Unreal) | ||
138 | { | ||
139 | // Add the validation rows for this table. | ||
140 | foreach (var columnDef in table.Definition.Columns) | ||
157 | { | 141 | { |
158 | Table importTable = table; | 142 | var row = validationTable.CreateRow(null); |
159 | bool hasBinaryColumn = false; | 143 | |
144 | row[0] = table.Name; | ||
160 | 145 | ||
161 | // Skip all unreal tables other than _Streams. | 146 | row[1] = columnDef.Name; |
162 | if (table.Definition.Unreal && "_Streams" != table.Name) | 147 | |
148 | if (columnDef.Nullable) | ||
163 | { | 149 | { |
164 | continue; | 150 | row[2] = "Y"; |
151 | } | ||
152 | else | ||
153 | { | ||
154 | row[2] = "N"; | ||
165 | } | 155 | } |
166 | 156 | ||
167 | // Do not put the _Validation table in patches, it is not needed. | 157 | if (columnDef.MinValue.HasValue) |
168 | if (OutputType.Patch == this.Output.Type && "_Validation" == table.Name) | ||
169 | { | 158 | { |
170 | continue; | 159 | row[3] = columnDef.MinValue.Value; |
171 | } | 160 | } |
172 | 161 | ||
173 | // The only way to import binary data is to copy it to a local subdirectory first. | 162 | if (columnDef.MaxValue.HasValue) |
174 | // To avoid this extra copying and perf hit, import an empty table with the same | ||
175 | // definition and later import the binary data from source using records. | ||
176 | foreach (ColumnDefinition columnDefinition in table.Definition.Columns) | ||
177 | { | 163 | { |
178 | if (ColumnType.Object == columnDefinition.Type) | 164 | row[4] = columnDef.MaxValue.Value; |
179 | { | ||
180 | importTable = new Table(table.Definition); | ||
181 | hasBinaryColumn = true; | ||
182 | break; | ||
183 | } | ||
184 | } | 165 | } |
185 | 166 | ||
186 | // Create the table via IDT import. | 167 | row[5] = columnDef.KeyTable; |
187 | if ("_Streams" != importTable.Name) | 168 | |
169 | if (columnDef.KeyColumn.HasValue) | ||
188 | { | 170 | { |
189 | try | 171 | row[6] = columnDef.KeyColumn.Value; |
190 | { | 172 | } |
191 | var command = new CreateIdtFileCommand(this.Messaging, importTable, this.Output.Codepage, idtDirectory, this.KeepAddedColumns); | ||
192 | command.Execute(); | ||
193 | 173 | ||
194 | var buildOutput = this.BackendHelper.TrackFile(command.IdtPath, TrackedFileType.Temporary); | 174 | if (ColumnCategory.Unknown != columnDef.Category) |
195 | this.GeneratedTemporaryFiles.Add(buildOutput); | 175 | { |
176 | row[7] = columnDef.Category.ToString(); | ||
177 | } | ||
196 | 178 | ||
197 | db.Import(command.IdtPath); | 179 | row[8] = columnDef.Possibilities; |
198 | } | ||
199 | catch (WixInvalidIdtException) | ||
200 | { | ||
201 | // If ValidateRows finds anything it doesn't like, it throws | ||
202 | importTable.ValidateRows(); | ||
203 | 180 | ||
204 | // Otherwise we rethrow the InvalidIdt | 181 | row[9] = columnDef.Description; |
205 | throw; | 182 | } |
206 | } | 183 | } |
184 | } | ||
185 | } | ||
186 | |||
187 | private void ImportTables(Database db, string idtDirectory) | ||
188 | { | ||
189 | foreach (var table in this.Data.Tables) | ||
190 | { | ||
191 | var importTable = table; | ||
192 | var hasBinaryColumn = false; | ||
193 | |||
194 | // Skip all unreal tables other than _Streams. | ||
195 | if (table.Definition.Unreal && "_Streams" != table.Name) | ||
196 | { | ||
197 | continue; | ||
198 | } | ||
199 | |||
200 | // Do not put the _Validation table in patches, it is not needed. | ||
201 | if (OutputType.Patch == this.Data.Type && "_Validation" == table.Name) | ||
202 | { | ||
203 | continue; | ||
204 | } | ||
205 | |||
206 | // The only way to import binary data is to copy it to a local subdirectory first. | ||
207 | // To avoid this extra copying and perf hit, import an empty table with the same | ||
208 | // definition and later import the binary data from source using records. | ||
209 | foreach (var columnDefinition in table.Definition.Columns) | ||
210 | { | ||
211 | if (ColumnType.Object == columnDefinition.Type) | ||
212 | { | ||
213 | importTable = new Table(table.Definition); | ||
214 | hasBinaryColumn = true; | ||
215 | break; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | // Create the table via IDT import. | ||
220 | if ("_Streams" != importTable.Name) | ||
221 | { | ||
222 | try | ||
223 | { | ||
224 | var command = new CreateIdtFileCommand(this.Messaging, importTable, this.Data.Codepage, idtDirectory, this.KeepAddedColumns); | ||
225 | command.Execute(); | ||
226 | |||
227 | var buildOutput = this.BackendHelper.TrackFile(command.IdtPath, TrackedFileType.Temporary); | ||
228 | this.GeneratedTemporaryFiles.Add(buildOutput); | ||
229 | |||
230 | db.Import(command.IdtPath); | ||
231 | } | ||
232 | catch (WixInvalidIdtException) | ||
233 | { | ||
234 | // If ValidateRows finds anything it doesn't like, it throws | ||
235 | importTable.ValidateRows(); | ||
236 | |||
237 | // Otherwise we rethrow the InvalidIdt | ||
238 | throw; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | // insert the rows via SQL query if this table contains object fields | ||
243 | if (hasBinaryColumn) | ||
244 | { | ||
245 | var query = new StringBuilder("SELECT "); | ||
246 | |||
247 | // Build the query for the view. | ||
248 | var firstColumn = true; | ||
249 | foreach (var columnDefinition in table.Definition.Columns) | ||
250 | { | ||
251 | if (columnDefinition.Unreal) | ||
252 | { | ||
253 | continue; | ||
207 | } | 254 | } |
208 | 255 | ||
209 | // insert the rows via SQL query if this table contains object fields | 256 | if (!firstColumn) |
210 | if (hasBinaryColumn) | ||
211 | { | 257 | { |
212 | StringBuilder query = new StringBuilder("SELECT "); | 258 | query.Append(","); |
259 | } | ||
213 | 260 | ||
214 | // Build the query for the view. | 261 | query.AppendFormat(" `{0}`", columnDefinition.Name); |
215 | bool firstColumn = true; | 262 | firstColumn = false; |
216 | foreach (ColumnDefinition columnDefinition in table.Definition.Columns) | 263 | } |
264 | query.AppendFormat(" FROM `{0}`", table.Name); | ||
265 | |||
266 | using (var tableView = db.OpenExecuteView(query.ToString())) | ||
267 | { | ||
268 | // Import each row containing a stream | ||
269 | foreach (var row in table.Rows) | ||
270 | { | ||
271 | using (var record = new Record(table.Definition.Columns.Length)) | ||
217 | { | 272 | { |
218 | if (!firstColumn) | 273 | // Stream names are created by concatenating the name of the table with the values |
274 | // of the primary key (delimited by periods). | ||
275 | var streamName = new StringBuilder(); | ||
276 | |||
277 | // the _Streams table doesn't prepend the table name (or a period) | ||
278 | if ("_Streams" != table.Name) | ||
219 | { | 279 | { |
220 | query.Append(","); | 280 | streamName.Append(table.Name); |
221 | } | 281 | } |
222 | 282 | ||
223 | query.AppendFormat(" `{0}`", columnDefinition.Name); | 283 | var needStream = false; |
224 | firstColumn = false; | ||
225 | } | ||
226 | query.AppendFormat(" FROM `{0}`", table.Name); | ||
227 | 284 | ||
228 | using (View tableView = db.OpenExecuteView(query.ToString())) | 285 | for (var i = 0; i < table.Definition.Columns.Length; i++) |
229 | { | ||
230 | // Import each row containing a stream | ||
231 | foreach (Row row in table.Rows) | ||
232 | { | 286 | { |
233 | using (Record record = new Record(table.Definition.Columns.Length)) | 287 | var columnDefinition = table.Definition.Columns[i]; |
288 | |||
289 | if (columnDefinition.Unreal) | ||
234 | { | 290 | { |
235 | StringBuilder streamName = new StringBuilder(); | 291 | continue; |
236 | bool needStream = false; | 292 | } |
293 | |||
294 | switch (columnDefinition.Type) | ||
295 | { | ||
296 | case ColumnType.Localized: | ||
297 | case ColumnType.Preserved: | ||
298 | case ColumnType.String: | ||
299 | var str = row.FieldAsString(i); | ||
237 | 300 | ||
238 | // the _Streams table doesn't prepend the table name (or a period) | 301 | if (columnDefinition.PrimaryKey) |
239 | if ("_Streams" != table.Name) | 302 | { |
240 | { | 303 | if (0 < streamName.Length) |
241 | streamName.Append(table.Name); | 304 | { |
242 | } | 305 | streamName.Append("."); |
306 | } | ||
307 | |||
308 | streamName.Append(str); | ||
309 | } | ||
243 | 310 | ||
244 | for (int i = 0; i < table.Definition.Columns.Length; i++) | 311 | record.SetString(i + 1, str); |
245 | { | 312 | break; |
246 | ColumnDefinition columnDefinition = table.Definition.Columns[i]; | 313 | case ColumnType.Number: |
314 | record.SetInteger(i + 1, row.FieldAsInteger(i)); | ||
315 | break; | ||
247 | 316 | ||
248 | switch (columnDefinition.Type) | 317 | case ColumnType.Object: |
318 | if (null != row[i]) | ||
249 | { | 319 | { |
250 | case ColumnType.Localized: | 320 | needStream = true; |
251 | case ColumnType.Preserved: | 321 | try |
252 | case ColumnType.String: | 322 | { |
253 | if (columnDefinition.PrimaryKey) | 323 | record.SetStream(i + 1, row.FieldAsString(i)); |
324 | } | ||
325 | catch (Win32Exception e) | ||
326 | { | ||
327 | if (0xA1 == e.NativeErrorCode) // ERROR_BAD_PATHNAME | ||
254 | { | 328 | { |
255 | if (0 < streamName.Length) | 329 | throw new WixException(ErrorMessages.FileNotFound(row.SourceLineNumbers, row.FieldAsString(i))); |
256 | { | ||
257 | streamName.Append("."); | ||
258 | } | ||
259 | streamName.Append((string)row[i]); | ||
260 | } | 330 | } |
261 | 331 | else | |
262 | record.SetString(i + 1, (string)row[i]); | ||
263 | break; | ||
264 | case ColumnType.Number: | ||
265 | record.SetInteger(i + 1, Convert.ToInt32(row[i], CultureInfo.InvariantCulture)); | ||
266 | break; | ||
267 | case ColumnType.Object: | ||
268 | if (null != row[i]) | ||
269 | { | 332 | { |
270 | needStream = true; | 333 | throw new WixException(ErrorMessages.Win32Exception(e.NativeErrorCode, e.Message)); |
271 | try | ||
272 | { | ||
273 | record.SetStream(i + 1, (string)row[i]); | ||
274 | } | ||
275 | catch (Win32Exception e) | ||
276 | { | ||
277 | if (0xA1 == e.NativeErrorCode) // ERROR_BAD_PATHNAME | ||
278 | { | ||
279 | throw new WixException(ErrorMessages.FileNotFound(row.SourceLineNumbers, (string)row[i])); | ||
280 | } | ||
281 | else | ||
282 | { | ||
283 | throw new WixException(ErrorMessages.Win32Exception(e.NativeErrorCode, e.Message)); | ||
284 | } | ||
285 | } | ||
286 | } | 334 | } |
287 | break; | 335 | } |
288 | } | 336 | } |
289 | } | 337 | break; |
290 | |||
291 | // stream names are created by concatenating the name of the table with the values | ||
292 | // of the primary key (delimited by periods) | ||
293 | // check for a stream name that is more than 62 characters long (the maximum allowed length) | ||
294 | if (needStream && MsiInterop.MsiMaxStreamNameLength < streamName.Length) | ||
295 | { | ||
296 | this.Messaging.Write(ErrorMessages.StreamNameTooLong(row.SourceLineNumbers, table.Name, streamName.ToString(), streamName.Length)); | ||
297 | } | ||
298 | else // add the row to the database | ||
299 | { | ||
300 | tableView.Modify(ModifyView.Assign, record); | ||
301 | } | ||
302 | } | 338 | } |
303 | } | 339 | } |
304 | } | ||
305 | |||
306 | // Remove rows from the _Streams table for wixpdbs. | ||
307 | if ("_Streams" == table.Name) | ||
308 | { | ||
309 | table.Rows.Clear(); | ||
310 | } | ||
311 | } | ||
312 | } | ||
313 | 340 | ||
314 | // Insert substorages (usually transforms inside a patch or instance transforms in a package). | 341 | // check for a stream name that is more than 62 characters long (the maximum allowed length) |
315 | if (0 < this.Output.SubStorages.Count) | 342 | if (needStream && MsiInterop.MsiMaxStreamNameLength < streamName.Length) |
316 | { | ||
317 | using (View storagesView = new View(db, "SELECT `Name`, `Data` FROM `_Storages`")) | ||
318 | { | ||
319 | foreach (SubStorage subStorage in this.Output.SubStorages) | ||
320 | { | ||
321 | string transformFile = Path.Combine(this.IntermediateFolder, String.Concat(subStorage.Name, ".mst")); | ||
322 | |||
323 | // Bind the transform. | ||
324 | this.BindTransform(subStorage.Data, transformFile); | ||
325 | |||
326 | if (this.Messaging.EncounteredError) | ||
327 | { | 343 | { |
328 | continue; | 344 | this.Messaging.Write(ErrorMessages.StreamNameTooLong(row.SourceLineNumbers, table.Name, streamName.ToString(), streamName.Length)); |
329 | } | 345 | } |
330 | 346 | else // add the row to the database | |
331 | // add the storage | ||
332 | using (Record record = new Record(2)) | ||
333 | { | 347 | { |
334 | record.SetString(1, subStorage.Name); | 348 | tableView.Modify(ModifyView.Assign, record); |
335 | record.SetStream(2, transformFile); | ||
336 | storagesView.Modify(ModifyView.Assign, record); | ||
337 | } | 349 | } |
338 | } | 350 | } |
339 | } | 351 | } |
340 | } | 352 | } |
341 | 353 | ||
342 | // We're good, commit the changes to the new database. | 354 | // Remove rows from the _Streams table for wixpdbs. |
343 | db.Commit(); | 355 | if ("_Streams" == table.Name) |
356 | { | ||
357 | table.Rows.Clear(); | ||
358 | } | ||
344 | } | 359 | } |
345 | } | 360 | } |
346 | catch (IOException e) | ||
347 | { | ||
348 | // TODO: this error message doesn't seem specific enough | ||
349 | throw new WixException(ErrorMessages.FileNotFound(new SourceLineNumber(this.OutputPath), this.OutputPath), e); | ||
350 | } | ||
351 | } | 361 | } |
352 | 362 | ||
353 | private void BindTransform(WindowsInstallerData transform, string outputPath) | 363 | private void ImportSubStorages(Database db) |
354 | { | 364 | { |
355 | var command = new BindTransformCommand(); | 365 | if (0 < this.Data.SubStorages.Count) |
356 | command.Messaging = this.Messaging; | 366 | { |
357 | command.Extensions = this.Extensions; | 367 | using (var storagesView = new View(db, "SELECT `Name`, `Data` FROM `_Storages`")) |
358 | command.TempFilesLocation = this.IntermediateFolder; | 368 | { |
359 | command.Transform = transform; | 369 | foreach (var subStorage in this.Data.SubStorages) |
360 | command.OutputPath = outputPath; | 370 | { |
361 | command.TableDefinitions = this.TableDefinitions; | 371 | var transformFile = Path.Combine(this.IntermediateFolder, String.Concat(subStorage.Name, ".mst")); |
362 | command.Execute(); | 372 | |
373 | // Bind the transform. | ||
374 | var command = new BindTransformCommand(this.Messaging, this.BackendHelper, this.Extensions, this.IntermediateFolder, subStorage.Data, transformFile, this.TableDefinitions); | ||
375 | command.Execute(); | ||
376 | |||
377 | if (this.Messaging.EncounteredError) | ||
378 | { | ||
379 | continue; | ||
380 | } | ||
381 | |||
382 | // Add the storage to the database. | ||
383 | using (var record = new Record(2)) | ||
384 | { | ||
385 | record.SetString(1, subStorage.Name); | ||
386 | record.SetStream(2, transformFile); | ||
387 | storagesView.Modify(ModifyView.Assign, record); | ||
388 | } | ||
389 | } | ||
390 | } | ||
391 | } | ||
363 | } | 392 | } |
364 | 393 | ||
365 | private void SetDatabaseCodepage(Database db, int codepage, string idtDirectory) | 394 | private void SetDatabaseCodepage(Database db, int codepage, string idtFolder) |
366 | { | 395 | { |
367 | // write out the _ForceCodepage IDT file | 396 | // Write out the _ForceCodepage IDT file. |
368 | var idtPath = Path.Combine(idtDirectory, "_ForceCodepage.idt"); | 397 | var idtPath = Path.Combine(idtFolder, "_ForceCodepage.idt"); |
369 | using (var idtFile = new StreamWriter(idtPath, false, Encoding.ASCII)) | 398 | using (var idtFile = new StreamWriter(idtPath, false, Encoding.ASCII)) |
370 | { | 399 | { |
371 | idtFile.WriteLine(); // dummy column name record | 400 | idtFile.WriteLine(); // dummy column name record |
@@ -377,14 +406,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
377 | var trackId = this.BackendHelper.TrackFile(idtPath, TrackedFileType.Temporary); | 406 | var trackId = this.BackendHelper.TrackFile(idtPath, TrackedFileType.Temporary); |
378 | this.GeneratedTemporaryFiles.Add(trackId); | 407 | this.GeneratedTemporaryFiles.Add(trackId); |
379 | 408 | ||
380 | // try to import the table into the MSI | 409 | // Try to import the table into the MSI. |
381 | try | 410 | try |
382 | { | 411 | { |
383 | db.Import(idtPath); | 412 | db.Import(idtPath); |
384 | } | 413 | } |
385 | catch (WixInvalidIdtException) | 414 | catch (WixInvalidIdtException) |
386 | { | 415 | { |
387 | // the IDT should be valid, so an invalid code page was given | 416 | // The IDT should be valid, so an invalid code page was given. |
388 | throw new WixException(ErrorMessages.IllegalCodepage(codepage)); | 417 | throw new WixException(ErrorMessages.IllegalCodepage(codepage)); |
389 | } | 418 | } |
390 | } | 419 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs new file mode 100644 index 00000000..8a7dd702 --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs | |||
@@ -0,0 +1,588 @@ | |||
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 | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Globalization; | ||
8 | using WixToolset.Core.WindowsInstaller.Msi; | ||
9 | using WixToolset.Data; | ||
10 | using WixToolset.Data.Tuples; | ||
11 | using WixToolset.Data.WindowsInstaller; | ||
12 | using WixToolset.Data.WindowsInstaller.Rows; | ||
13 | using WixToolset.Extensibility; | ||
14 | using WixToolset.Extensibility.Services; | ||
15 | |||
16 | /// <summary> | ||
17 | /// Creates a transform by diffing two outputs. | ||
18 | /// </summary> | ||
19 | public sealed class GenerateTransformCommand | ||
20 | { | ||
21 | private const char sectionDelimiter = '/'; | ||
22 | private readonly IMessaging messaging; | ||
23 | private SummaryInformationStreams transformSummaryInfo; | ||
24 | |||
25 | /// <summary> | ||
26 | /// Instantiates a new Differ class. | ||
27 | /// </summary> | ||
28 | public GenerateTransformCommand(IMessaging messaging, WindowsInstallerData targetOutput, WindowsInstallerData updatedOutput, bool showPedanticMessages) | ||
29 | { | ||
30 | this.messaging = messaging; | ||
31 | this.TargetOutput = targetOutput; | ||
32 | this.UpdatedOutput = updatedOutput; | ||
33 | this.ShowPedanticMessages = showPedanticMessages; | ||
34 | } | ||
35 | |||
36 | private WindowsInstallerData TargetOutput { get; } | ||
37 | |||
38 | private WindowsInstallerData UpdatedOutput { get; } | ||
39 | |||
40 | private TransformFlags ValidationFlags { get; } | ||
41 | |||
42 | /// <summary> | ||
43 | /// Gets or sets the option to show pedantic messages. | ||
44 | /// </summary> | ||
45 | /// <value>The option to show pedantic messages.</value> | ||
46 | private bool ShowPedanticMessages { get; } | ||
47 | |||
48 | /// <summary> | ||
49 | /// Gets or sets the option to suppress keeping special rows. | ||
50 | /// </summary> | ||
51 | /// <value>The option to suppress keeping special rows.</value> | ||
52 | private bool SuppressKeepingSpecialRows { get; } | ||
53 | |||
54 | /// <summary> | ||
55 | /// Gets or sets the flag to determine if all rows, even unchanged ones will be persisted in the output. | ||
56 | /// </summary> | ||
57 | /// <value>The option to keep all rows including unchanged rows.</value> | ||
58 | private bool PreserveUnchangedRows { get; } | ||
59 | |||
60 | public WindowsInstallerData Transform { get; private set; } | ||
61 | |||
62 | /// <summary> | ||
63 | /// Creates a transform by diffing two outputs. | ||
64 | /// </summary> | ||
65 | /// <param name="targetOutput">The target output.</param> | ||
66 | /// <param name="updatedOutput">The updated output.</param> | ||
67 | /// <param name="validationFlags"></param> | ||
68 | /// <returns>The transform.</returns> | ||
69 | public WindowsInstallerData Execute() | ||
70 | { | ||
71 | var targetOutput = this.TargetOutput; | ||
72 | var updatedOutput = this.UpdatedOutput; | ||
73 | var validationFlags = this.ValidationFlags; | ||
74 | |||
75 | var transform = new WindowsInstallerData(null) | ||
76 | { | ||
77 | Type = OutputType.Transform, | ||
78 | Codepage = updatedOutput.Codepage | ||
79 | }; | ||
80 | |||
81 | this.transformSummaryInfo = new SummaryInformationStreams(); | ||
82 | |||
83 | // compare the codepages | ||
84 | if (targetOutput.Codepage != updatedOutput.Codepage && 0 == (TransformFlags.ErrorChangeCodePage & validationFlags)) | ||
85 | { | ||
86 | this.messaging.Write(ErrorMessages.OutputCodepageMismatch(targetOutput.SourceLineNumbers, targetOutput.Codepage, updatedOutput.Codepage)); | ||
87 | if (null != updatedOutput.SourceLineNumbers) | ||
88 | { | ||
89 | this.messaging.Write(ErrorMessages.OutputCodepageMismatch2(updatedOutput.SourceLineNumbers)); | ||
90 | } | ||
91 | } | ||
92 | |||
93 | // compare the output types | ||
94 | if (targetOutput.Type != updatedOutput.Type) | ||
95 | { | ||
96 | throw new WixException(ErrorMessages.OutputTypeMismatch(targetOutput.SourceLineNumbers, targetOutput.Type.ToString(), updatedOutput.Type.ToString())); | ||
97 | } | ||
98 | |||
99 | // compare the contents of the tables | ||
100 | foreach (var targetTable in targetOutput.Tables) | ||
101 | { | ||
102 | var updatedTable = updatedOutput.Tables[targetTable.Name]; | ||
103 | var operation = TableOperation.None; | ||
104 | |||
105 | var rows = this.CompareTables(targetOutput, targetTable, updatedTable, out operation); | ||
106 | |||
107 | if (TableOperation.Drop == operation) | ||
108 | { | ||
109 | var droppedTable = transform.EnsureTable(targetTable.Definition); | ||
110 | droppedTable.Operation = TableOperation.Drop; | ||
111 | } | ||
112 | else if (TableOperation.None == operation) | ||
113 | { | ||
114 | var modified = transform.EnsureTable(updatedTable.Definition); | ||
115 | foreach (var row in rows) | ||
116 | { | ||
117 | modified.Rows.Add(row); | ||
118 | } | ||
119 | } | ||
120 | } | ||
121 | |||
122 | // added tables | ||
123 | foreach (var updatedTable in updatedOutput.Tables) | ||
124 | { | ||
125 | if (null == targetOutput.Tables[updatedTable.Name]) | ||
126 | { | ||
127 | var addedTable = transform.EnsureTable(updatedTable.Definition); | ||
128 | addedTable.Operation = TableOperation.Add; | ||
129 | |||
130 | foreach (var updatedRow in updatedTable.Rows) | ||
131 | { | ||
132 | updatedRow.Operation = RowOperation.Add; | ||
133 | updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId; | ||
134 | addedTable.Rows.Add(updatedRow); | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | |||
139 | // set summary information properties | ||
140 | if (!this.SuppressKeepingSpecialRows) | ||
141 | { | ||
142 | var summaryInfoTable = transform.Tables["_SummaryInformation"]; | ||
143 | this.UpdateTransformSummaryInformationTable(summaryInfoTable, validationFlags); | ||
144 | } | ||
145 | |||
146 | this.Transform = transform; | ||
147 | return this.Transform; | ||
148 | } | ||
149 | |||
150 | /// <summary> | ||
151 | /// Add a row to the <paramref name="index"/> using the primary key. | ||
152 | /// </summary> | ||
153 | /// <param name="index">The indexed rows.</param> | ||
154 | /// <param name="row">The row to index.</param> | ||
155 | private void AddIndexedRow(Dictionary<string, Row> index, Row row) | ||
156 | { | ||
157 | var primaryKey = row.GetPrimaryKey(); | ||
158 | |||
159 | if (null != primaryKey) | ||
160 | { | ||
161 | if (index.TryGetValue(primaryKey, out var collisionRow)) | ||
162 | { | ||
163 | // Overriding WixActionRows have a primary key defined and take precedence in the index. | ||
164 | if (row is WixActionRow actionRow) | ||
165 | { | ||
166 | // If the current row is not overridable, see if the indexed row is. | ||
167 | if (!actionRow.Overridable) | ||
168 | { | ||
169 | if (collisionRow is WixActionRow indexedRow && indexedRow.Overridable) | ||
170 | { | ||
171 | // The indexed key is overridable and should be replaced. | ||
172 | index[primaryKey] = actionRow; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | // If we got this far, the row does not need to be indexed. | ||
177 | return; | ||
178 | } | ||
179 | |||
180 | if (this.ShowPedanticMessages) | ||
181 | { | ||
182 | this.messaging.Write(ErrorMessages.DuplicatePrimaryKey(row.SourceLineNumbers, primaryKey, row.Table.Name)); | ||
183 | } | ||
184 | } | ||
185 | else | ||
186 | { | ||
187 | index.Add(primaryKey, row); | ||
188 | } | ||
189 | } | ||
190 | else // use the string representation of the row as its primary key (it may not be unique) | ||
191 | { | ||
192 | // this is provided for compatibility with unreal tables with no primary key | ||
193 | // all real tables must specify at least one column as the primary key | ||
194 | primaryKey = row.ToString(); | ||
195 | index[primaryKey] = row; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | private bool CompareRows(Table targetTable, Row targetRow, Row updatedRow, out Row comparedRow) | ||
200 | { | ||
201 | comparedRow = null; | ||
202 | |||
203 | var keepRow = false; | ||
204 | |||
205 | if (null == targetRow ^ null == updatedRow) | ||
206 | { | ||
207 | if (null == targetRow) | ||
208 | { | ||
209 | updatedRow.Operation = RowOperation.Add; | ||
210 | comparedRow = updatedRow; | ||
211 | } | ||
212 | else if (null == updatedRow) | ||
213 | { | ||
214 | targetRow.Operation = RowOperation.Delete; | ||
215 | targetRow.SectionId += sectionDelimiter; | ||
216 | |||
217 | comparedRow = targetRow; | ||
218 | keepRow = true; | ||
219 | } | ||
220 | } | ||
221 | else // possibly modified | ||
222 | { | ||
223 | updatedRow.Operation = RowOperation.None; | ||
224 | if (!this.SuppressKeepingSpecialRows && "_SummaryInformation" == targetTable.Name) | ||
225 | { | ||
226 | // ignore rows that shouldn't be in a transform | ||
227 | if (Enum.IsDefined(typeof(SummaryInformation.Transform), (int)updatedRow[0])) | ||
228 | { | ||
229 | updatedRow.SectionId = targetRow.SectionId + sectionDelimiter + updatedRow.SectionId; | ||
230 | comparedRow = updatedRow; | ||
231 | keepRow = true; | ||
232 | } | ||
233 | } | ||
234 | else | ||
235 | { | ||
236 | if (this.PreserveUnchangedRows) | ||
237 | { | ||
238 | keepRow = true; | ||
239 | } | ||
240 | |||
241 | for (var i = 0; i < updatedRow.Fields.Length; i++) | ||
242 | { | ||
243 | var columnDefinition = updatedRow.Fields[i].Column; | ||
244 | |||
245 | if (columnDefinition.Unreal) | ||
246 | { | ||
247 | } | ||
248 | else if (!columnDefinition.PrimaryKey) | ||
249 | { | ||
250 | var modified = false; | ||
251 | |||
252 | if (i >= targetRow.Fields.Length) | ||
253 | { | ||
254 | columnDefinition.Added = true; | ||
255 | modified = true; | ||
256 | } | ||
257 | else if (ColumnType.Number == columnDefinition.Type && !columnDefinition.IsLocalizable) | ||
258 | { | ||
259 | if (null == targetRow[i] ^ null == updatedRow[i]) | ||
260 | { | ||
261 | modified = true; | ||
262 | } | ||
263 | else if (null != targetRow[i] && null != updatedRow[i]) | ||
264 | { | ||
265 | modified = (targetRow.FieldAsInteger(i) != updatedRow.FieldAsInteger(i)); | ||
266 | } | ||
267 | } | ||
268 | else if (ColumnType.Preserved == columnDefinition.Type) | ||
269 | { | ||
270 | updatedRow.Fields[i].PreviousData = targetRow.FieldAsString(i); | ||
271 | |||
272 | // keep rows containing preserved fields so the historical data is available to the binder | ||
273 | keepRow = !this.SuppressKeepingSpecialRows; | ||
274 | } | ||
275 | else if (ColumnType.Object == columnDefinition.Type) | ||
276 | { | ||
277 | var targetObjectField = (ObjectField)targetRow.Fields[i]; | ||
278 | var updatedObjectField = (ObjectField)updatedRow.Fields[i]; | ||
279 | |||
280 | updatedObjectField.PreviousEmbeddedFileIndex = targetObjectField.EmbeddedFileIndex; | ||
281 | updatedObjectField.PreviousBaseUri = targetObjectField.BaseUri; | ||
282 | |||
283 | // always keep a copy of the previous data even if they are identical | ||
284 | // This makes diff.wixmst clean and easier to control patch logic | ||
285 | updatedObjectField.PreviousData = (string)targetObjectField.Data; | ||
286 | |||
287 | // always remember the unresolved data for target build | ||
288 | updatedObjectField.UnresolvedPreviousData = targetObjectField.UnresolvedData; | ||
289 | |||
290 | // keep rows containing object fields so the files can be compared in the binder | ||
291 | keepRow = !this.SuppressKeepingSpecialRows; | ||
292 | } | ||
293 | else | ||
294 | { | ||
295 | modified = (targetRow.FieldAsString(i) != updatedRow.FieldAsString(i)); | ||
296 | } | ||
297 | |||
298 | if (modified) | ||
299 | { | ||
300 | if (null != updatedRow.Fields[i].PreviousData) | ||
301 | { | ||
302 | updatedRow.Fields[i].PreviousData = targetRow.FieldAsString(i); | ||
303 | } | ||
304 | |||
305 | updatedRow.Fields[i].Modified = true; | ||
306 | updatedRow.Operation = RowOperation.Modify; | ||
307 | keepRow = true; | ||
308 | } | ||
309 | } | ||
310 | } | ||
311 | |||
312 | if (keepRow) | ||
313 | { | ||
314 | comparedRow = updatedRow; | ||
315 | comparedRow.SectionId = targetRow.SectionId + sectionDelimiter + updatedRow.SectionId; | ||
316 | } | ||
317 | } | ||
318 | } | ||
319 | |||
320 | return keepRow; | ||
321 | } | ||
322 | |||
323 | private List<Row> CompareTables(WindowsInstallerData targetOutput, Table targetTable, Table updatedTable, out TableOperation operation) | ||
324 | { | ||
325 | var rows = new List<Row>(); | ||
326 | operation = TableOperation.None; | ||
327 | |||
328 | // dropped tables | ||
329 | if (null == updatedTable ^ null == targetTable) | ||
330 | { | ||
331 | if (null == targetTable) | ||
332 | { | ||
333 | operation = TableOperation.Add; | ||
334 | rows.AddRange(updatedTable.Rows); | ||
335 | } | ||
336 | else if (null == updatedTable) | ||
337 | { | ||
338 | operation = TableOperation.Drop; | ||
339 | } | ||
340 | } | ||
341 | else // possibly modified tables | ||
342 | { | ||
343 | var updatedPrimaryKeys = new Dictionary<string, Row>(); | ||
344 | var targetPrimaryKeys = new Dictionary<string, Row>(); | ||
345 | |||
346 | // compare the table definitions | ||
347 | if (0 != targetTable.Definition.CompareTo(updatedTable.Definition)) | ||
348 | { | ||
349 | // continue to the next table; may be more mismatches | ||
350 | this.messaging.Write(ErrorMessages.DatabaseSchemaMismatch(targetOutput.SourceLineNumbers, targetTable.Name)); | ||
351 | } | ||
352 | else | ||
353 | { | ||
354 | this.IndexPrimaryKeys(targetTable, targetPrimaryKeys, updatedTable, updatedPrimaryKeys); | ||
355 | |||
356 | // diff the target and updated rows | ||
357 | foreach (var targetPrimaryKeyEntry in targetPrimaryKeys) | ||
358 | { | ||
359 | var targetPrimaryKey = targetPrimaryKeyEntry.Key; | ||
360 | var targetRow = targetPrimaryKeyEntry.Value; | ||
361 | updatedPrimaryKeys.TryGetValue(targetPrimaryKey, out var updatedRow); | ||
362 | |||
363 | var keepRow = this.CompareRows(targetTable, targetRow, updatedRow, out var compared); | ||
364 | |||
365 | if (keepRow) | ||
366 | { | ||
367 | rows.Add(compared); | ||
368 | } | ||
369 | } | ||
370 | |||
371 | // find the inserted rows | ||
372 | foreach (var updatedPrimaryKeyEntry in updatedPrimaryKeys) | ||
373 | { | ||
374 | var updatedPrimaryKey = updatedPrimaryKeyEntry.Key; | ||
375 | |||
376 | if (!targetPrimaryKeys.ContainsKey(updatedPrimaryKey)) | ||
377 | { | ||
378 | var updatedRow = updatedPrimaryKeyEntry.Value; | ||
379 | |||
380 | updatedRow.Operation = RowOperation.Add; | ||
381 | updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId; | ||
382 | rows.Add(updatedRow); | ||
383 | } | ||
384 | } | ||
385 | } | ||
386 | } | ||
387 | |||
388 | return rows; | ||
389 | } | ||
390 | |||
391 | private void IndexPrimaryKeys(Table targetTable, Dictionary<string, Row> targetPrimaryKeys, Table updatedTable, Dictionary<string, Row> updatedPrimaryKeys) | ||
392 | { | ||
393 | // index the target rows | ||
394 | foreach (var row in targetTable.Rows) | ||
395 | { | ||
396 | this.AddIndexedRow(targetPrimaryKeys, row); | ||
397 | |||
398 | if ("Property" == targetTable.Name) | ||
399 | { | ||
400 | var id = row.FieldAsString(0); | ||
401 | |||
402 | if ("ProductCode" == id) | ||
403 | { | ||
404 | this.transformSummaryInfo.TargetProductCode = row.FieldAsString(1); | ||
405 | |||
406 | if ("*" == this.transformSummaryInfo.TargetProductCode) | ||
407 | { | ||
408 | this.messaging.Write(ErrorMessages.ProductCodeInvalidForTransform(row.SourceLineNumbers)); | ||
409 | } | ||
410 | } | ||
411 | else if ("ProductVersion" == id) | ||
412 | { | ||
413 | this.transformSummaryInfo.TargetProductVersion = row.FieldAsString(1); | ||
414 | } | ||
415 | else if ("UpgradeCode" == id) | ||
416 | { | ||
417 | this.transformSummaryInfo.TargetUpgradeCode = row.FieldAsString(1); | ||
418 | } | ||
419 | } | ||
420 | else if ("_SummaryInformation" == targetTable.Name) | ||
421 | { | ||
422 | var id = row.FieldAsInteger(0); | ||
423 | |||
424 | if (1 == id) // PID_CODEPAGE | ||
425 | { | ||
426 | this.transformSummaryInfo.TargetSummaryInfoCodepage = row.FieldAsString(1); | ||
427 | } | ||
428 | else if (7 == id) // PID_TEMPLATE | ||
429 | { | ||
430 | this.transformSummaryInfo.TargetPlatformAndLanguage = row.FieldAsString(1); | ||
431 | } | ||
432 | else if (14 == id) // PID_PAGECOUNT | ||
433 | { | ||
434 | this.transformSummaryInfo.TargetMinimumVersion = row.FieldAsString(1); | ||
435 | } | ||
436 | } | ||
437 | } | ||
438 | |||
439 | // index the updated rows | ||
440 | foreach (var row in updatedTable.Rows) | ||
441 | { | ||
442 | this.AddIndexedRow(updatedPrimaryKeys, row); | ||
443 | |||
444 | if ("Property" == updatedTable.Name) | ||
445 | { | ||
446 | var id = row.FieldAsString(0); | ||
447 | |||
448 | if ("ProductCode" == id) | ||
449 | { | ||
450 | this.transformSummaryInfo.UpdatedProductCode = row.FieldAsString(1); | ||
451 | |||
452 | if ("*" == this.transformSummaryInfo.UpdatedProductCode) | ||
453 | { | ||
454 | this.messaging.Write(ErrorMessages.ProductCodeInvalidForTransform(row.SourceLineNumbers)); | ||
455 | } | ||
456 | } | ||
457 | else if ("ProductVersion" == id) | ||
458 | { | ||
459 | this.transformSummaryInfo.UpdatedProductVersion = row.FieldAsString(1); | ||
460 | } | ||
461 | } | ||
462 | else if ("_SummaryInformation" == updatedTable.Name) | ||
463 | { | ||
464 | var id = row.FieldAsInteger(0); | ||
465 | |||
466 | if (1 == id) // PID_CODEPAGE | ||
467 | { | ||
468 | this.transformSummaryInfo.UpdatedSummaryInfoCodepage = row.FieldAsString(1); | ||
469 | } | ||
470 | else if (7 == id) // PID_TEMPLATE | ||
471 | { | ||
472 | this.transformSummaryInfo.UpdatedPlatformAndLanguage = row.FieldAsString(1); | ||
473 | } | ||
474 | else if (14 == id) // PID_PAGECOUNT | ||
475 | { | ||
476 | this.transformSummaryInfo.UpdatedMinimumVersion = row.FieldAsString(1); | ||
477 | } | ||
478 | } | ||
479 | } | ||
480 | } | ||
481 | |||
482 | private void UpdateTransformSummaryInformationTable(Table summaryInfoTable, TransformFlags validationFlags) | ||
483 | { | ||
484 | // calculate the minimum version of MSI required to process the transform | ||
485 | var minimumVersion = 100; | ||
486 | |||
487 | if (Int32.TryParse(this.transformSummaryInfo.TargetMinimumVersion, out var targetMin) && Int32.TryParse(this.transformSummaryInfo.UpdatedMinimumVersion, out var updatedMin)) | ||
488 | { | ||
489 | minimumVersion = Math.Max(targetMin, updatedMin); | ||
490 | } | ||
491 | |||
492 | var summaryRows = new Dictionary<int, Row>(summaryInfoTable.Rows.Count); | ||
493 | |||
494 | foreach (var row in summaryInfoTable.Rows) | ||
495 | { | ||
496 | var id = row.FieldAsInteger(0); | ||
497 | |||
498 | summaryRows[id] = row; | ||
499 | |||
500 | if ((int)SummaryInformation.Transform.CodePage == id) | ||
501 | { | ||
502 | row.Fields[1].Data = this.transformSummaryInfo.UpdatedSummaryInfoCodepage; | ||
503 | row.Fields[1].PreviousData = this.transformSummaryInfo.TargetSummaryInfoCodepage; | ||
504 | } | ||
505 | else if ((int)SummaryInformation.Transform.TargetPlatformAndLanguage == id) | ||
506 | { | ||
507 | row[1] = this.transformSummaryInfo.TargetPlatformAndLanguage; | ||
508 | } | ||
509 | else if ((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == id) | ||
510 | { | ||
511 | row[1] = this.transformSummaryInfo.UpdatedPlatformAndLanguage; | ||
512 | } | ||
513 | else if ((int)SummaryInformation.Transform.ProductCodes == id) | ||
514 | { | ||
515 | row[1] = String.Concat(this.transformSummaryInfo.TargetProductCode, this.transformSummaryInfo.TargetProductVersion, ';', this.transformSummaryInfo.UpdatedProductCode, this.transformSummaryInfo.UpdatedProductVersion, ';', this.transformSummaryInfo.TargetUpgradeCode); | ||
516 | } | ||
517 | else if ((int)SummaryInformation.Transform.InstallerRequirement == id) | ||
518 | { | ||
519 | row[1] = minimumVersion.ToString(CultureInfo.InvariantCulture); | ||
520 | } | ||
521 | else if ((int)SummaryInformation.Transform.Security == id) | ||
522 | { | ||
523 | row[1] = "4"; | ||
524 | } | ||
525 | } | ||
526 | |||
527 | if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.TargetPlatformAndLanguage)) | ||
528 | { | ||
529 | var summaryRow = summaryInfoTable.CreateRow(null); | ||
530 | summaryRow[0] = (int)SummaryInformation.Transform.TargetPlatformAndLanguage; | ||
531 | summaryRow[1] = this.transformSummaryInfo.TargetPlatformAndLanguage; | ||
532 | } | ||
533 | |||
534 | if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage)) | ||
535 | { | ||
536 | var summaryRow = summaryInfoTable.CreateRow(null); | ||
537 | summaryRow[0] = (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage; | ||
538 | summaryRow[1] = this.transformSummaryInfo.UpdatedPlatformAndLanguage; | ||
539 | } | ||
540 | |||
541 | if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.ValidationFlags)) | ||
542 | { | ||
543 | var summaryRow = summaryInfoTable.CreateRow(null); | ||
544 | summaryRow[0] = (int)SummaryInformation.Transform.ValidationFlags; | ||
545 | summaryRow[1] = ((int)validationFlags).ToString(CultureInfo.InvariantCulture); | ||
546 | } | ||
547 | |||
548 | if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.InstallerRequirement)) | ||
549 | { | ||
550 | var summaryRow = summaryInfoTable.CreateRow(null); | ||
551 | summaryRow[0] = (int)SummaryInformation.Transform.InstallerRequirement; | ||
552 | summaryRow[1] = minimumVersion.ToString(CultureInfo.InvariantCulture); | ||
553 | } | ||
554 | |||
555 | if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.Security)) | ||
556 | { | ||
557 | var summaryRow = summaryInfoTable.CreateRow(null); | ||
558 | summaryRow[0] = (int)SummaryInformation.Transform.Security; | ||
559 | summaryRow[1] = "4"; | ||
560 | } | ||
561 | } | ||
562 | |||
563 | private class SummaryInformationStreams | ||
564 | { | ||
565 | public string TargetSummaryInfoCodepage { get; set; } | ||
566 | |||
567 | public string TargetPlatformAndLanguage { get; set; } | ||
568 | |||
569 | public string TargetProductCode { get; set; } | ||
570 | |||
571 | public string TargetProductVersion { get; set; } | ||
572 | |||
573 | public string TargetUpgradeCode { get; set; } | ||
574 | |||
575 | public string TargetMinimumVersion { get; set; } | ||
576 | |||
577 | public string UpdatedSummaryInfoCodepage { get; set; } | ||
578 | |||
579 | public string UpdatedPlatformAndLanguage { get; set; } | ||
580 | |||
581 | public string UpdatedProductCode { get; set; } | ||
582 | |||
583 | public string UpdatedProductVersion { get; set; } | ||
584 | |||
585 | public string UpdatedMinimumVersion { get; set; } | ||
586 | } | ||
587 | } | ||
588 | } | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs index 0da6a6b0..2844f797 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs | |||
@@ -132,7 +132,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
132 | file.SymbolPaths = String.Concat(file.SymbolPaths, ";", row.SymbolPaths); | 132 | file.SymbolPaths = String.Concat(file.SymbolPaths, ";", row.SymbolPaths); |
133 | } | 133 | } |
134 | 134 | ||
135 | #if REVISIT_FOR_PATCHING | 135 | #if TODO_PATCHING |
136 | Field field = row.Fields[2]; | 136 | Field field = row.Fields[2]; |
137 | if (null != field.PreviousData) | 137 | if (null != field.PreviousData) |
138 | { | 138 | { |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs new file mode 100644 index 00000000..9818f01a --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs | |||
@@ -0,0 +1,585 @@ | |||
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 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Diagnostics; | ||
8 | using System.IO; | ||
9 | using System.Linq; | ||
10 | using WixToolset.Core.Bind; | ||
11 | using WixToolset.Data; | ||
12 | using WixToolset.Data.Tuples; | ||
13 | using WixToolset.Data.WindowsInstaller; | ||
14 | using WixToolset.Data.WindowsInstaller.Rows; | ||
15 | using WixToolset.Extensibility; | ||
16 | using WixToolset.Extensibility.Services; | ||
17 | |||
18 | internal class GetFileFacadesFromTransforms | ||
19 | { | ||
20 | public GetFileFacadesFromTransforms(IMessaging messaging, IEnumerable<SubStorage> subStorages, TableDefinitionCollection tableDefinitions) | ||
21 | { | ||
22 | this.Messaging = messaging; | ||
23 | this.SubStorages = subStorages; | ||
24 | this.TableDefinitions = tableDefinitions; | ||
25 | } | ||
26 | |||
27 | public IEnumerable<IFileSystemExtension> Extensions { get; } | ||
28 | |||
29 | private IMessaging Messaging { get; } | ||
30 | |||
31 | public IEnumerable<SubStorage> SubStorages { get; } | ||
32 | |||
33 | private TableDefinitionCollection TableDefinitions { get; } | ||
34 | |||
35 | public IEnumerable<FileFacade> FileFacades { get; private set; } | ||
36 | |||
37 | public void Execute() | ||
38 | { | ||
39 | var allFileRows = new List<FileFacade>(); | ||
40 | var copyToPatch = (allFileRows != null); | ||
41 | #if TODO_PATCHING | ||
42 | var patchMediaRows = new RowDictionary<MediaRow>(); | ||
43 | |||
44 | var patchMediaFileRows = new Dictionary<int, RowDictionary<WixFileRow>>(); | ||
45 | |||
46 | var patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]); | ||
47 | var patchFileTable = this.Output.EnsureTable(this.TableDefinitions["WixFile"]); | ||
48 | |||
49 | if (copyFromPatch) | ||
50 | { | ||
51 | // index patch files by diskId+fileId | ||
52 | foreach (WixFileRow patchFileRow in patchFileTable.Rows) | ||
53 | { | ||
54 | int diskId = patchFileRow.DiskId; | ||
55 | if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows)) | ||
56 | { | ||
57 | mediaFileRows = new RowDictionary<WixFileRow>(); | ||
58 | patchMediaFileRows.Add(diskId, mediaFileRows); | ||
59 | } | ||
60 | |||
61 | mediaFileRows.Add(patchFileRow); | ||
62 | } | ||
63 | |||
64 | var patchMediaTable = this.Output.EnsureTable(this.TableDefinitions["Media"]); | ||
65 | patchMediaRows = new RowDictionary<MediaRow>(patchMediaTable); | ||
66 | } | ||
67 | |||
68 | // Index paired transforms by name without their "#" prefix. | ||
69 | var pairedTransforms = this.SubStorages.Where(s => s.Name.StartsWith("#")).ToDictionary(s => s.Name.Substring(1), s => s.Data); | ||
70 | |||
71 | foreach (var substorage in this.SubStorages) | ||
72 | { | ||
73 | if (substorage.Name.StartsWith("#")) | ||
74 | { | ||
75 | // Skip paired transforms. | ||
76 | continue; | ||
77 | } | ||
78 | |||
79 | var mainTransform = substorage.Data; | ||
80 | var mainWixFiles = new RowDictionary<WixFileRow>(mainTransform.Tables["WixFile"]); | ||
81 | var mainMsiFileHashIndex = new RowDictionary<Row>(mainTransform.Tables["MsiFileHash"]); | ||
82 | |||
83 | var mainFileTable = mainTransform.Tables["File"]; | ||
84 | var pairedTransform = pairedTransforms[substorage.Name]; | ||
85 | |||
86 | // copy Media.LastSequence and index the MsiFileHash table if it exists. | ||
87 | if (copyFromPatch) | ||
88 | { | ||
89 | var pairedMediaTable = pairedTransform.Tables["Media"]; | ||
90 | foreach (MediaRow pairedMediaRow in pairedMediaTable.Rows) | ||
91 | { | ||
92 | var patchMediaRow = patchMediaRows.Get(pairedMediaRow.DiskId); | ||
93 | pairedMediaRow.Fields[1] = patchMediaRow.Fields[1]; | ||
94 | } | ||
95 | |||
96 | if (null != mainMsiFileHashTable) | ||
97 | { | ||
98 | mainMsiFileHashIndex = new RowDictionary<Row>(mainMsiFileHashTable); | ||
99 | } | ||
100 | |||
101 | // Validate file row changes for keypath-related issues | ||
102 | this.ValidateFileRowChanges(mainTransform); | ||
103 | } | ||
104 | |||
105 | if (null == mainFileTable) | ||
106 | { | ||
107 | continue; | ||
108 | } | ||
109 | |||
110 | // Index File table of pairedTransform | ||
111 | var pairedFileRows = new RowDictionary<FileRow>(pairedTransform.Tables["File"]); | ||
112 | |||
113 | foreach (FileRow mainFileRow in mainFileTable.Rows) | ||
114 | { | ||
115 | if (RowOperation.Delete == mainFileRow.Operation) | ||
116 | { | ||
117 | continue; | ||
118 | } | ||
119 | else if (RowOperation.None == mainFileRow.Operation) | ||
120 | { | ||
121 | continue; | ||
122 | } | ||
123 | |||
124 | var mainWixFileRow = mainWixFiles.Get(mainFileRow.File); | ||
125 | |||
126 | if (copyToPatch) // when copying to the patch, we need compare the underlying files and include all file changes. | ||
127 | { | ||
128 | var objectField = (ObjectField)mainWixFileRow.Fields[6]; | ||
129 | var pairedFileRow = pairedFileRows.Get(mainFileRow.File); | ||
130 | |||
131 | // If the file is new, we always need to add it to the patch. | ||
132 | if (mainFileRow.Operation != RowOperation.Add) | ||
133 | { | ||
134 | // If PreviousData doesn't exist, target and upgrade layout point to the same location. No need to compare. | ||
135 | if (null == objectField.PreviousData) | ||
136 | { | ||
137 | if (mainFileRow.Operation == RowOperation.None) | ||
138 | { | ||
139 | continue; | ||
140 | } | ||
141 | } | ||
142 | else | ||
143 | { | ||
144 | // TODO: should this entire condition be placed in the binder file manager? | ||
145 | if ((0 == (PatchAttributeType.Ignore & mainWixFileRow.PatchAttributes)) && | ||
146 | !this.CompareFiles(objectField.PreviousData.ToString(), objectField.Data.ToString())) | ||
147 | { | ||
148 | // If the file is different, we need to mark the mainFileRow and pairedFileRow as modified. | ||
149 | mainFileRow.Operation = RowOperation.Modify; | ||
150 | if (null != pairedFileRow) | ||
151 | { | ||
152 | // Always patch-added, but never non-compressed. | ||
153 | pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded; | ||
154 | pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed; | ||
155 | pairedFileRow.Fields[6].Modified = true; | ||
156 | pairedFileRow.Operation = RowOperation.Modify; | ||
157 | } | ||
158 | } | ||
159 | else | ||
160 | { | ||
161 | // The File is same. We need mark all the attributes as unchanged. | ||
162 | mainFileRow.Operation = RowOperation.None; | ||
163 | foreach (var field in mainFileRow.Fields) | ||
164 | { | ||
165 | field.Modified = false; | ||
166 | } | ||
167 | |||
168 | if (null != pairedFileRow) | ||
169 | { | ||
170 | pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesPatchAdded; | ||
171 | pairedFileRow.Fields[6].Modified = false; | ||
172 | pairedFileRow.Operation = RowOperation.None; | ||
173 | } | ||
174 | continue; | ||
175 | } | ||
176 | } | ||
177 | } | ||
178 | else if (null != pairedFileRow) // RowOperation.Add | ||
179 | { | ||
180 | // Always patch-added, but never non-compressed. | ||
181 | pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded; | ||
182 | pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed; | ||
183 | pairedFileRow.Fields[6].Modified = true; | ||
184 | pairedFileRow.Operation = RowOperation.Add; | ||
185 | } | ||
186 | } | ||
187 | |||
188 | // index patch files by diskId+fileId | ||
189 | int diskId = mainWixFileRow.DiskId; | ||
190 | |||
191 | if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows)) | ||
192 | { | ||
193 | mediaFileRows = new RowDictionary<WixFileRow>(); | ||
194 | patchMediaFileRows.Add(diskId, mediaFileRows); | ||
195 | } | ||
196 | |||
197 | var fileId = mainFileRow.File; | ||
198 | var patchFileRow = mediaFileRows.Get(fileId); | ||
199 | if (copyToPatch) | ||
200 | { | ||
201 | if (null == patchFileRow) | ||
202 | { | ||
203 | var patchActualFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
204 | patchActualFileRow.CopyFrom(mainFileRow); | ||
205 | |||
206 | patchFileRow = (WixFileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
207 | patchFileRow.CopyFrom(mainWixFileRow); | ||
208 | |||
209 | mediaFileRows.Add(patchFileRow); | ||
210 | |||
211 | allFileRows.Add(new FileFacade(patchActualFileRow, patchFileRow, null)); // TODO: should we be passing along delta information? Probably, right? | ||
212 | } | ||
213 | else | ||
214 | { | ||
215 | // TODO: confirm the rest of data is identical? | ||
216 | |||
217 | // make sure Source is same. Otherwise we are silently ignoring a file. | ||
218 | if (0 != String.Compare(patchFileRow.Source, mainWixFileRow.Source, StringComparison.OrdinalIgnoreCase)) | ||
219 | { | ||
220 | this.Messaging.Write(ErrorMessages.SameFileIdDifferentSource(mainFileRow.SourceLineNumbers, fileId, patchFileRow.Source, mainWixFileRow.Source)); | ||
221 | } | ||
222 | |||
223 | // capture the previous file versions (and associated data) from this targeted instance of the baseline into the current filerow. | ||
224 | patchFileRow.AppendPreviousDataFrom(mainWixFileRow); | ||
225 | } | ||
226 | } | ||
227 | //else | ||
228 | //{ | ||
229 | // // copy data from the patch back to the transform | ||
230 | // if (null != patchFileRow) | ||
231 | // { | ||
232 | // var pairedFileRow = pairedFileRows.Get(fileId); | ||
233 | // for (var i = 0; i < patchFileRow.Fields.Length; i++) | ||
234 | // { | ||
235 | // var patchValue = patchFileRow[i] == null ? String.Empty : patchFileRow.FieldAsString(i); | ||
236 | // var mainValue = mainFileRow[i] == null ? String.Empty : mainFileRow.FieldAsString(i); | ||
237 | |||
238 | // if (1 == i) | ||
239 | // { | ||
240 | // // File.Component_ changes should not come from the shared file rows | ||
241 | // // that contain the file information as each individual transform might | ||
242 | // // have different changes (or no changes at all). | ||
243 | // } | ||
244 | // // File.Attributes should not changed for binary deltas | ||
245 | // else if (6 == i) | ||
246 | // { | ||
247 | // if (null != patchFileRow.Patch) | ||
248 | // { | ||
249 | // // File.Attribute should not change for binary deltas | ||
250 | // pairedFileRow.Attributes = mainFileRow.Attributes; | ||
251 | // mainFileRow.Fields[i].Modified = false; | ||
252 | // } | ||
253 | // } | ||
254 | // // File.Sequence is updated in pairedTransform, not mainTransform | ||
255 | // else if (7 == i) | ||
256 | // { | ||
257 | // // file sequence is updated in Patch table instead of File table for delta patches | ||
258 | // if (null != patchFileRow.Patch) | ||
259 | // { | ||
260 | // pairedFileRow.Fields[i].Modified = false; | ||
261 | // } | ||
262 | // else | ||
263 | // { | ||
264 | // pairedFileRow[i] = patchFileRow[i]; | ||
265 | // pairedFileRow.Fields[i].Modified = true; | ||
266 | // } | ||
267 | // mainFileRow.Fields[i].Modified = false; | ||
268 | // } | ||
269 | // else if (patchValue != mainValue) | ||
270 | // { | ||
271 | // mainFileRow[i] = patchFileRow[i]; | ||
272 | // mainFileRow.Fields[i].Modified = true; | ||
273 | // if (mainFileRow.Operation == RowOperation.None) | ||
274 | // { | ||
275 | // mainFileRow.Operation = RowOperation.Modify; | ||
276 | // } | ||
277 | // } | ||
278 | // } | ||
279 | |||
280 | // // copy MsiFileHash row for this File | ||
281 | // if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out var patchHashRow)) | ||
282 | // { | ||
283 | // patchHashRow = patchFileRow.Hash; | ||
284 | // } | ||
285 | |||
286 | // if (null != patchHashRow) | ||
287 | // { | ||
288 | // var mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]); | ||
289 | // var mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
290 | // for (var i = 0; i < patchHashRow.Fields.Length; i++) | ||
291 | // { | ||
292 | // mainHashRow[i] = patchHashRow[i]; | ||
293 | // if (i > 1) | ||
294 | // { | ||
295 | // // assume all hash fields have been modified | ||
296 | // mainHashRow.Fields[i].Modified = true; | ||
297 | // } | ||
298 | // } | ||
299 | |||
300 | // // assume the MsiFileHash operation follows the File one | ||
301 | // mainHashRow.Operation = mainFileRow.Operation; | ||
302 | // } | ||
303 | |||
304 | // // copy MsiAssemblyName rows for this File | ||
305 | // List<Row> patchAssemblyNameRows = patchFileRow.AssemblyNames; | ||
306 | // if (null != patchAssemblyNameRows) | ||
307 | // { | ||
308 | // var mainAssemblyNameTable = mainTransform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); | ||
309 | // foreach (var patchAssemblyNameRow in patchAssemblyNameRows) | ||
310 | // { | ||
311 | // // Copy if there isn't an identical modified/added row already in the transform. | ||
312 | // var foundMatchingModifiedRow = false; | ||
313 | // foreach (var mainAssemblyNameRow in mainAssemblyNameTable.Rows) | ||
314 | // { | ||
315 | // if (RowOperation.None != mainAssemblyNameRow.Operation && mainAssemblyNameRow.GetPrimaryKey('/').Equals(patchAssemblyNameRow.GetPrimaryKey('/'))) | ||
316 | // { | ||
317 | // foundMatchingModifiedRow = true; | ||
318 | // break; | ||
319 | // } | ||
320 | // } | ||
321 | |||
322 | // if (!foundMatchingModifiedRow) | ||
323 | // { | ||
324 | // var mainAssemblyNameRow = mainAssemblyNameTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
325 | // for (var i = 0; i < patchAssemblyNameRow.Fields.Length; i++) | ||
326 | // { | ||
327 | // mainAssemblyNameRow[i] = patchAssemblyNameRow[i]; | ||
328 | // } | ||
329 | |||
330 | // // assume value field has been modified | ||
331 | // mainAssemblyNameRow.Fields[2].Modified = true; | ||
332 | // mainAssemblyNameRow.Operation = mainFileRow.Operation; | ||
333 | // } | ||
334 | // } | ||
335 | // } | ||
336 | |||
337 | // // Add patch header for this file | ||
338 | // if (null != patchFileRow.Patch) | ||
339 | // { | ||
340 | // // Add the PatchFiles action automatically to the AdminExecuteSequence and InstallExecuteSequence tables. | ||
341 | // this.AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow); | ||
342 | // this.AddPatchFilesActionToSequenceTable(SequenceTable.InstallExecuteSequence, mainTransform, pairedTransform, mainFileRow); | ||
343 | |||
344 | // // Add to Patch table | ||
345 | // var patchTable = pairedTransform.EnsureTable(this.TableDefinitions["Patch"]); | ||
346 | // if (0 == patchTable.Rows.Count) | ||
347 | // { | ||
348 | // patchTable.Operation = TableOperation.Add; | ||
349 | // } | ||
350 | |||
351 | // var patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
352 | // patchRow[0] = patchFileRow.File; | ||
353 | // patchRow[1] = patchFileRow.Sequence; | ||
354 | |||
355 | // var patchFile = new FileInfo(patchFileRow.Source); | ||
356 | // patchRow[2] = (int)patchFile.Length; | ||
357 | // patchRow[3] = 0 == (PatchAttributeType.AllowIgnoreOnError & patchFileRow.PatchAttributes) ? 0 : 1; | ||
358 | |||
359 | // var streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1]; | ||
360 | // if (Msi.MsiInterop.MsiMaxStreamNameLength < streamName.Length) | ||
361 | // { | ||
362 | // streamName = "_" + Guid.NewGuid().ToString("D").ToUpperInvariant().Replace('-', '_'); | ||
363 | |||
364 | // var patchHeadersTable = pairedTransform.EnsureTable(this.TableDefinitions["MsiPatchHeaders"]); | ||
365 | // if (0 == patchHeadersTable.Rows.Count) | ||
366 | // { | ||
367 | // patchHeadersTable.Operation = TableOperation.Add; | ||
368 | // } | ||
369 | |||
370 | // var patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
371 | // patchHeadersRow[0] = streamName; | ||
372 | // patchHeadersRow[1] = patchFileRow.Patch; | ||
373 | // patchRow[5] = streamName; | ||
374 | // patchHeadersRow.Operation = RowOperation.Add; | ||
375 | // } | ||
376 | // else | ||
377 | // { | ||
378 | // patchRow[4] = patchFileRow.Patch; | ||
379 | // } | ||
380 | // patchRow.Operation = RowOperation.Add; | ||
381 | // } | ||
382 | // } | ||
383 | // else | ||
384 | // { | ||
385 | // // TODO: throw because all transform rows should have made it into the patch | ||
386 | // } | ||
387 | //} | ||
388 | } | ||
389 | } | ||
390 | #endif | ||
391 | this.FileFacades = allFileRows; | ||
392 | } | ||
393 | |||
394 | /// <summary> | ||
395 | /// Adds the PatchFiles action to the sequence table if it does not already exist. | ||
396 | /// </summary> | ||
397 | /// <param name="table">The sequence table to check or modify.</param> | ||
398 | /// <param name="mainTransform">The primary authoring transform.</param> | ||
399 | /// <param name="pairedTransform">The secondary patch transform.</param> | ||
400 | /// <param name="mainFileRow">The file row that contains information about the patched file.</param> | ||
401 | private void AddPatchFilesActionToSequenceTable(SequenceTable table, WindowsInstallerData mainTransform, WindowsInstallerData pairedTransform, Row mainFileRow) | ||
402 | { | ||
403 | var tableName = table.ToString(); | ||
404 | |||
405 | // Find/add PatchFiles action (also determine sequence for it). | ||
406 | // Search mainTransform first, then pairedTransform (pairedTransform overrides). | ||
407 | var hasPatchFilesAction = false; | ||
408 | var installFilesSequence = 0; | ||
409 | var duplicateFilesSequence = 0; | ||
410 | |||
411 | TestSequenceTableForPatchFilesAction( | ||
412 | mainTransform.Tables[tableName], | ||
413 | ref hasPatchFilesAction, | ||
414 | ref installFilesSequence, | ||
415 | ref duplicateFilesSequence); | ||
416 | TestSequenceTableForPatchFilesAction( | ||
417 | pairedTransform.Tables[tableName], | ||
418 | ref hasPatchFilesAction, | ||
419 | ref installFilesSequence, | ||
420 | ref duplicateFilesSequence); | ||
421 | if (!hasPatchFilesAction) | ||
422 | { | ||
423 | WindowsInstallerStandard.TryGetStandardAction(tableName, "PatchFiles", out var patchFilesActionTuple); | ||
424 | |||
425 | var sequence = patchFilesActionTuple.Sequence; | ||
426 | |||
427 | // Test for default sequence value's appropriateness | ||
428 | if (installFilesSequence >= sequence || (0 != duplicateFilesSequence && duplicateFilesSequence <= sequence)) | ||
429 | { | ||
430 | if (0 != duplicateFilesSequence) | ||
431 | { | ||
432 | if (duplicateFilesSequence < installFilesSequence) | ||
433 | { | ||
434 | throw new WixException(ErrorMessages.InsertInvalidSequenceActionOrder(mainFileRow.SourceLineNumbers, tableName, "InstallFiles", "DuplicateFiles", patchFilesActionTuple.Action)); | ||
435 | } | ||
436 | else | ||
437 | { | ||
438 | sequence = (duplicateFilesSequence + installFilesSequence) / 2; | ||
439 | if (installFilesSequence == sequence || duplicateFilesSequence == sequence) | ||
440 | { | ||
441 | throw new WixException(ErrorMessages.InsertSequenceNoSpace(mainFileRow.SourceLineNumbers, tableName, "InstallFiles", "DuplicateFiles", patchFilesActionTuple.Action)); | ||
442 | } | ||
443 | } | ||
444 | } | ||
445 | else | ||
446 | { | ||
447 | sequence = installFilesSequence + 1; | ||
448 | } | ||
449 | } | ||
450 | |||
451 | var sequenceTable = pairedTransform.EnsureTable(this.TableDefinitions[tableName]); | ||
452 | if (0 == sequenceTable.Rows.Count) | ||
453 | { | ||
454 | sequenceTable.Operation = TableOperation.Add; | ||
455 | } | ||
456 | |||
457 | var patchAction = sequenceTable.CreateRow(null); | ||
458 | patchAction[0] = patchFilesActionTuple.Action; | ||
459 | patchAction[1] = patchFilesActionTuple.Condition; | ||
460 | patchAction[2] = sequence; | ||
461 | patchAction.Operation = RowOperation.Add; | ||
462 | } | ||
463 | } | ||
464 | |||
465 | /// <summary> | ||
466 | /// Tests sequence table for PatchFiles and associated actions | ||
467 | /// </summary> | ||
468 | /// <param name="sequenceTable">The table to test.</param> | ||
469 | /// <param name="hasPatchFilesAction">Set to true if PatchFiles action is found. Left unchanged otherwise.</param> | ||
470 | /// <param name="installFilesSequence">Set to sequence value of InstallFiles action if found. Left unchanged otherwise.</param> | ||
471 | /// <param name="duplicateFilesSequence">Set to sequence value of DuplicateFiles action if found. Left unchanged otherwise.</param> | ||
472 | private static void TestSequenceTableForPatchFilesAction(Table sequenceTable, ref bool hasPatchFilesAction, ref int installFilesSequence, ref int duplicateFilesSequence) | ||
473 | { | ||
474 | if (null != sequenceTable) | ||
475 | { | ||
476 | foreach (var row in sequenceTable.Rows) | ||
477 | { | ||
478 | var actionName = row.FieldAsString(0); | ||
479 | switch (actionName) | ||
480 | { | ||
481 | case "PatchFiles": | ||
482 | hasPatchFilesAction = true; | ||
483 | break; | ||
484 | |||
485 | case "InstallFiles": | ||
486 | installFilesSequence = row.FieldAsInteger(2); | ||
487 | break; | ||
488 | |||
489 | case "DuplicateFiles": | ||
490 | duplicateFilesSequence = row.FieldAsInteger(2); | ||
491 | break; | ||
492 | } | ||
493 | } | ||
494 | } | ||
495 | } | ||
496 | |||
497 | /// <summary> | ||
498 | /// Signal a warning if a non-keypath file was changed in a patch without also changing the keypath file of the component. | ||
499 | /// </summary> | ||
500 | /// <param name="output">The output to validate.</param> | ||
501 | private void ValidateFileRowChanges(WindowsInstallerData transform) | ||
502 | { | ||
503 | var componentTable = transform.Tables["Component"]; | ||
504 | var fileTable = transform.Tables["File"]; | ||
505 | |||
506 | // There's no sense validating keypaths if the transform has no component or file table | ||
507 | if (componentTable == null || fileTable == null) | ||
508 | { | ||
509 | return; | ||
510 | } | ||
511 | |||
512 | var componentKeyPath = new Dictionary<string, string>(componentTable.Rows.Count); | ||
513 | |||
514 | // Index the Component table for non-directory & non-registry key paths. | ||
515 | foreach (var row in componentTable.Rows) | ||
516 | { | ||
517 | var keyPath = row.FieldAsString(5); | ||
518 | if (keyPath != null && 0 != (row.FieldAsInteger(3) & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath)) | ||
519 | { | ||
520 | componentKeyPath.Add(row.FieldAsString(0), keyPath); | ||
521 | } | ||
522 | } | ||
523 | |||
524 | var componentWithChangedKeyPath = new Dictionary<string, string>(); | ||
525 | var componentWithNonKeyPathChanged = new Dictionary<string, string>(); | ||
526 | // Verify changes in the file table, now that file diffing has occurred | ||
527 | foreach (FileRow row in fileTable.Rows) | ||
528 | { | ||
529 | if (RowOperation.Modify != row.Operation) | ||
530 | { | ||
531 | continue; | ||
532 | } | ||
533 | |||
534 | var fileId = row.FieldAsString(0); | ||
535 | var componentId = row.FieldAsString(1); | ||
536 | |||
537 | // If this file is the keypath of a component | ||
538 | if (componentKeyPath.ContainsValue(fileId)) | ||
539 | { | ||
540 | if (!componentWithChangedKeyPath.ContainsKey(componentId)) | ||
541 | { | ||
542 | componentWithChangedKeyPath.Add(componentId, fileId); | ||
543 | } | ||
544 | } | ||
545 | else | ||
546 | { | ||
547 | if (!componentWithNonKeyPathChanged.ContainsKey(componentId)) | ||
548 | { | ||
549 | componentWithNonKeyPathChanged.Add(componentId, fileId); | ||
550 | } | ||
551 | } | ||
552 | } | ||
553 | |||
554 | foreach (var componentFile in componentWithNonKeyPathChanged) | ||
555 | { | ||
556 | // Make sure all changes to non keypath files also had a change in the keypath. | ||
557 | if (!componentWithChangedKeyPath.ContainsKey(componentFile.Key) && componentKeyPath.TryGetValue(componentFile.Key, out var keyPath)) | ||
558 | { | ||
559 | this.Messaging.Write(WarningMessages.UpdateOfNonKeyPathFile(componentFile.Value, componentFile.Key, keyPath)); | ||
560 | } | ||
561 | } | ||
562 | } | ||
563 | |||
564 | private bool CompareFiles(string targetFile, string updatedFile) | ||
565 | { | ||
566 | bool? compared = null; | ||
567 | foreach (var extension in this.Extensions) | ||
568 | { | ||
569 | compared = extension.CompareFiles(targetFile, updatedFile); | ||
570 | |||
571 | if (compared.HasValue) | ||
572 | { | ||
573 | break; | ||
574 | } | ||
575 | } | ||
576 | |||
577 | if (!compared.HasValue) | ||
578 | { | ||
579 | throw new InvalidOperationException(); // TODO: something needs to be said here that none of the binder file managers returned a result. | ||
580 | } | ||
581 | |||
582 | return compared.Value; | ||
583 | } | ||
584 | } | ||
585 | } | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs index 92ddad6f..1aa4065e 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs | |||
@@ -9,15 +9,22 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
9 | using WixToolset.Data; | 9 | using WixToolset.Data; |
10 | using WixToolset.Data.Tuples; | 10 | using WixToolset.Data.Tuples; |
11 | using WixToolset.Data.WindowsInstaller; | 11 | using WixToolset.Data.WindowsInstaller; |
12 | using WixToolset.Extensibility; | ||
12 | 13 | ||
13 | internal class LoadTableDefinitionsCommand | 14 | internal class LoadTableDefinitionsCommand |
14 | { | 15 | { |
15 | public LoadTableDefinitionsCommand(IntermediateSection section) => this.Section = section; | 16 | public LoadTableDefinitionsCommand(IntermediateSection section, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtensions) |
16 | 17 | { | |
17 | public TableDefinitionCollection TableDefinitions { get; private set; } | 18 | this.Section = section; |
19 | this.BackendExtensions = backendExtensions; | ||
20 | } | ||
18 | 21 | ||
19 | private IntermediateSection Section { get; } | 22 | private IntermediateSection Section { get; } |
20 | 23 | ||
24 | private IEnumerable<IWindowsInstallerBackendBinderExtension> BackendExtensions { get; } | ||
25 | |||
26 | public TableDefinitionCollection TableDefinitions { get; private set; } | ||
27 | |||
21 | public TableDefinitionCollection Execute() | 28 | public TableDefinitionCollection Execute() |
22 | { | 29 | { |
23 | var tableDefinitions = new TableDefinitionCollection(WindowsInstallerStandardInternal.GetTableDefinitions()); | 30 | var tableDefinitions = new TableDefinitionCollection(WindowsInstallerStandardInternal.GetTableDefinitions()); |
@@ -28,6 +35,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
28 | tableDefinitions.Add(customTableDefinition); | 35 | tableDefinitions.Add(customTableDefinition); |
29 | } | 36 | } |
30 | 37 | ||
38 | foreach (var backendExtension in this.BackendExtensions) | ||
39 | { | ||
40 | foreach (var tableDefinition in backendExtension.TableDefinitions) | ||
41 | { | ||
42 | tableDefinitions.Add(tableDefinition); | ||
43 | } | ||
44 | } | ||
45 | |||
31 | this.TableDefinitions = tableDefinitions; | 46 | this.TableDefinitions = tableDefinitions; |
32 | return this.TableDefinitions; | 47 | return this.TableDefinitions; |
33 | } | 48 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs index 8c11555e..b90aecd1 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs | |||
@@ -277,7 +277,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
277 | 277 | ||
278 | using (Record record = new Record(1)) | 278 | using (Record record = new Record(1)) |
279 | { | 279 | { |
280 | record.SetString(1, file.File.Id.Id); | 280 | record.SetString(1, file.Id); |
281 | view.Execute(record); | 281 | view.Execute(record); |
282 | } | 282 | } |
283 | 283 | ||
@@ -288,7 +288,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
288 | throw new InvalidOperationException("Failed to fetch a File row from the database that was merged in from a module."); | 288 | throw new InvalidOperationException("Failed to fetch a File row from the database that was merged in from a module."); |
289 | } | 289 | } |
290 | 290 | ||
291 | recordUpdate.SetInteger(1, file.File.Sequence); | 291 | recordUpdate.SetInteger(1, file.Sequence); |
292 | 292 | ||
293 | // Update the file attributes to match the compression specified | 293 | // Update the file attributes to match the compression specified |
294 | // on the Merge element or on the Package element. | 294 | // on the Merge element or on the Package element. |
@@ -300,12 +300,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
300 | attributes = recordUpdate.GetInteger(2); | 300 | attributes = recordUpdate.GetInteger(2); |
301 | } | 301 | } |
302 | 302 | ||
303 | if ((file.File.Attributes & FileTupleAttributes.Compressed) == FileTupleAttributes.Compressed) | 303 | if (file.Compressed) |
304 | { | 304 | { |
305 | attributes |= WindowsInstallerConstants.MsidbFileAttributesCompressed; | 305 | attributes |= WindowsInstallerConstants.MsidbFileAttributesCompressed; |
306 | attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed; | 306 | attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed; |
307 | } | 307 | } |
308 | else if ((file.File.Attributes & FileTupleAttributes.Uncompressed) == FileTupleAttributes.Uncompressed) | 308 | else if (file.Uncompressed) |
309 | { | 309 | { |
310 | attributes |= WindowsInstallerConstants.MsidbFileAttributesNoncompressed; | 310 | attributes |= WindowsInstallerConstants.MsidbFileAttributesNoncompressed; |
311 | attributes &= ~WindowsInstallerConstants.MsidbFileAttributesCompressed; | 311 | attributes &= ~WindowsInstallerConstants.MsidbFileAttributesCompressed; |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/PatchTransform.cs b/src/WixToolset.Core.WindowsInstaller/Bind/PatchTransform.cs new file mode 100644 index 00000000..5ada29de --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/PatchTransform.cs | |||
@@ -0,0 +1,246 @@ | |||
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 | using System; | ||
6 | using System.Collections; | ||
7 | using System.Globalization; | ||
8 | using System.Text; | ||
9 | using System.Text.RegularExpressions; | ||
10 | using WixToolset.Data; | ||
11 | using WixToolset.Data.WindowsInstaller; | ||
12 | using WixToolset.Extensibility; | ||
13 | |||
14 | internal class PatchTransform | ||
15 | { | ||
16 | public PatchTransform(string baseline, WindowsInstallerData transform) | ||
17 | { | ||
18 | this.Baseline = baseline; | ||
19 | this.Transform = transform; | ||
20 | } | ||
21 | |||
22 | public string Baseline { get; } | ||
23 | |||
24 | public WindowsInstallerData Transform { get; } | ||
25 | |||
26 | /// <summary> | ||
27 | /// Validates that the differences in the transform are valid for patch transforms. | ||
28 | /// </summary> | ||
29 | public void Validate() | ||
30 | { | ||
31 | #if TODO_PATCHING | ||
32 | // Changing the ProdocutCode in a patch transform is not recommended. | ||
33 | Table propertyTable = this.Transform.Tables["Property"]; | ||
34 | if (null != propertyTable) | ||
35 | { | ||
36 | foreach (Row row in propertyTable.Rows) | ||
37 | { | ||
38 | // Only interested in modified rows; fast check. | ||
39 | if (RowOperation.Modify == row.Operation) | ||
40 | { | ||
41 | if (0 == String.CompareOrdinal("ProductCode", (string)row[0])) | ||
42 | { | ||
43 | this.OnMessage(WixWarnings.MajorUpgradePatchNotRecommended()); | ||
44 | } | ||
45 | } | ||
46 | } | ||
47 | } | ||
48 | |||
49 | // If there is nothing in the component table we can return early because the remaining checks are component based. | ||
50 | Table componentTable = this.Transform.Tables["Component"]; | ||
51 | if (null == componentTable) | ||
52 | { | ||
53 | return; | ||
54 | } | ||
55 | |||
56 | // Index Feature table row operations | ||
57 | Table featureTable = this.Transform.Tables["Feature"]; | ||
58 | Table featureComponentsTable = this.Transform.Tables["FeatureComponents"]; | ||
59 | Hashtable featureOps = null; | ||
60 | if (null != featureTable) | ||
61 | { | ||
62 | int capacity = featureTable.Rows.Count; | ||
63 | featureOps = new Hashtable(capacity); | ||
64 | |||
65 | foreach (Row row in featureTable.Rows) | ||
66 | { | ||
67 | featureOps[(string)row[0]] = row.Operation; | ||
68 | } | ||
69 | } | ||
70 | else | ||
71 | { | ||
72 | featureOps = new Hashtable(); | ||
73 | } | ||
74 | |||
75 | // Index Component table and check for keypath modifications | ||
76 | Hashtable deletedComponent = new Hashtable(); | ||
77 | Hashtable componentKeyPath = new Hashtable(); | ||
78 | foreach (Row row in componentTable.Rows) | ||
79 | { | ||
80 | string id = row.Fields[0].Data.ToString(); | ||
81 | string keypath = (null == row.Fields[5].Data) ? String.Empty : row.Fields[5].Data.ToString(); | ||
82 | |||
83 | componentKeyPath.Add(id, keypath); | ||
84 | if (RowOperation.Delete == row.Operation) | ||
85 | { | ||
86 | deletedComponent.Add(id, row); | ||
87 | } | ||
88 | else if (RowOperation.Modify == row.Operation) | ||
89 | { | ||
90 | if (row.Fields[1].Modified) | ||
91 | { | ||
92 | // Changing the guid of a component is equal to deleting the old one and adding a new one. | ||
93 | deletedComponent.Add(id, row); | ||
94 | } | ||
95 | |||
96 | // If the keypath is modified its an error | ||
97 | if (row.Fields[5].Modified) | ||
98 | { | ||
99 | this.OnMessage(WixErrors.InvalidKeypathChange(row.SourceLineNumbers, id, this.transformPath)); | ||
100 | } | ||
101 | } | ||
102 | } | ||
103 | |||
104 | // Verify changes in the file table | ||
105 | Table fileTable = this.Transform.Tables["File"]; | ||
106 | if (null != fileTable) | ||
107 | { | ||
108 | Hashtable componentWithChangedKeyPath = new Hashtable(); | ||
109 | foreach (Row row in fileTable.Rows) | ||
110 | { | ||
111 | if (RowOperation.None != row.Operation) | ||
112 | { | ||
113 | string fileId = row.Fields[0].Data.ToString(); | ||
114 | string componentId = row.Fields[1].Data.ToString(); | ||
115 | |||
116 | // If this file is the keypath of a component | ||
117 | if (String.Equals((string)componentKeyPath[componentId], fileId, StringComparison.Ordinal)) | ||
118 | { | ||
119 | if (row.Fields[2].Modified) | ||
120 | { | ||
121 | // You cant change the filename of a file that is the keypath of a component. | ||
122 | this.OnMessage(WixErrors.InvalidKeypathChange(row.SourceLineNumbers, componentId, this.transformPath)); | ||
123 | } | ||
124 | |||
125 | if (!componentWithChangedKeyPath.ContainsKey(componentId)) | ||
126 | { | ||
127 | componentWithChangedKeyPath.Add(componentId, fileId); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | if (RowOperation.Delete == row.Operation) | ||
132 | { | ||
133 | // If the file is removed from a component that is not deleted. | ||
134 | if (!deletedComponent.ContainsKey(componentId)) | ||
135 | { | ||
136 | bool foundRemoveFileEntry = false; | ||
137 | string filename = Common.GetName((string)row[2], false, true); | ||
138 | |||
139 | Table removeFileTable = this.Transform.Tables["RemoveFile"]; | ||
140 | if (null != removeFileTable) | ||
141 | { | ||
142 | foreach (Row removeFileRow in removeFileTable.Rows) | ||
143 | { | ||
144 | if (RowOperation.Delete == removeFileRow.Operation) | ||
145 | { | ||
146 | continue; | ||
147 | } | ||
148 | |||
149 | if (componentId == (string)removeFileRow[1]) | ||
150 | { | ||
151 | // Check if there is a RemoveFile entry for this file | ||
152 | if (null != removeFileRow[2]) | ||
153 | { | ||
154 | string removeFileName = Common.GetName((string)removeFileRow[2], false, true); | ||
155 | |||
156 | // Convert the MSI format for a wildcard string to Regex format. | ||
157 | removeFileName = removeFileName.Replace('.', '|').Replace('?', '.').Replace("*", ".*").Replace("|", "\\."); | ||
158 | |||
159 | Regex regex = new Regex(removeFileName, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); | ||
160 | if (regex.IsMatch(filename)) | ||
161 | { | ||
162 | foundRemoveFileEntry = true; | ||
163 | break; | ||
164 | } | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | |||
170 | if (!foundRemoveFileEntry) | ||
171 | { | ||
172 | this.OnMessage(WixWarnings.InvalidRemoveFile(row.SourceLineNumbers, fileId, componentId)); | ||
173 | } | ||
174 | } | ||
175 | } | ||
176 | } | ||
177 | } | ||
178 | } | ||
179 | |||
180 | if (0 < deletedComponent.Count) | ||
181 | { | ||
182 | // Index FeatureComponents table. | ||
183 | Hashtable featureComponents = new Hashtable(); | ||
184 | |||
185 | if (null != featureComponentsTable) | ||
186 | { | ||
187 | foreach (Row row in featureComponentsTable.Rows) | ||
188 | { | ||
189 | ArrayList features; | ||
190 | string componentId = row.Fields[1].Data.ToString(); | ||
191 | |||
192 | if (featureComponents.Contains(componentId)) | ||
193 | { | ||
194 | features = (ArrayList)featureComponents[componentId]; | ||
195 | } | ||
196 | else | ||
197 | { | ||
198 | features = new ArrayList(); | ||
199 | featureComponents.Add(componentId, features); | ||
200 | } | ||
201 | features.Add(row.Fields[0].Data.ToString()); | ||
202 | } | ||
203 | } | ||
204 | |||
205 | // Check to make sure if a component was deleted, the feature was too. | ||
206 | foreach (DictionaryEntry entry in deletedComponent) | ||
207 | { | ||
208 | if (featureComponents.Contains(entry.Key.ToString())) | ||
209 | { | ||
210 | ArrayList features = (ArrayList)featureComponents[entry.Key.ToString()]; | ||
211 | foreach (string featureId in features) | ||
212 | { | ||
213 | if (!featureOps.ContainsKey(featureId) || RowOperation.Delete != (RowOperation)featureOps[featureId]) | ||
214 | { | ||
215 | // The feature was not deleted. | ||
216 | this.OnMessage(WixErrors.InvalidRemoveComponent(((Row)entry.Value).SourceLineNumbers, entry.Key.ToString(), featureId, this.transformPath)); | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | } | ||
221 | } | ||
222 | |||
223 | // Warn if new components are added to existing features | ||
224 | if (null != featureComponentsTable) | ||
225 | { | ||
226 | foreach (Row row in featureComponentsTable.Rows) | ||
227 | { | ||
228 | if (RowOperation.Add == row.Operation) | ||
229 | { | ||
230 | // Check if the feature is in the Feature table | ||
231 | string feature_ = (string)row[0]; | ||
232 | string component_ = (string)row[1]; | ||
233 | |||
234 | // Features may not be present if not referenced | ||
235 | if (!featureOps.ContainsKey(feature_) || RowOperation.Add != (RowOperation)featureOps[feature_]) | ||
236 | { | ||
237 | this.OnMessage(WixWarnings.NewComponentAddedToExistingFeature(row.SourceLineNumbers, component_, feature_, this.transformPath)); | ||
238 | } | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | #endif | ||
243 | throw new NotImplementedException(); | ||
244 | } | ||
245 | } | ||
246 | } | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs index 373ada38..13d47215 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs | |||
@@ -86,14 +86,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
86 | 86 | ||
87 | // setup up the query record and find the appropriate file in the | 87 | // setup up the query record and find the appropriate file in the |
88 | // previously executed file view | 88 | // previously executed file view |
89 | fileQueryRecord[1] = facade.File.Id.Id; | 89 | fileQueryRecord[1] = facade.Id; |
90 | fileView.Execute(fileQueryRecord); | 90 | fileView.Execute(fileQueryRecord); |
91 | 91 | ||
92 | using (Record fileRecord = fileView.Fetch()) | 92 | using (Record fileRecord = fileView.Fetch()) |
93 | { | 93 | { |
94 | if (null == fileRecord) | 94 | if (null == fileRecord) |
95 | { | 95 | { |
96 | throw new WixException(ErrorMessages.FileIdentifierNotFound(facade.File.SourceLineNumbers, facade.File.Id.Id)); | 96 | throw new WixException(ErrorMessages.FileIdentifierNotFound(facade.SourceLineNumber, facade.Id)); |
97 | } | 97 | } |
98 | 98 | ||
99 | relativeFileLayoutPath = this.PathResolver.GetFileSourcePath(directories, fileRecord[1], fileRecord[2], this.Compressed, this.LongNamesInImage); | 99 | relativeFileLayoutPath = this.PathResolver.GetFileSourcePath(directories, fileRecord[1], fileRecord[2], this.Compressed, this.LongNamesInImage); |
@@ -102,7 +102,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
102 | // finally put together the base media layout path and the relative file layout path | 102 | // finally put together the base media layout path and the relative file layout path |
103 | var fileLayoutPath = Path.Combine(mediaLayoutDirectory, relativeFileLayoutPath); | 103 | var fileLayoutPath = Path.Combine(mediaLayoutDirectory, relativeFileLayoutPath); |
104 | 104 | ||
105 | var transfer = this.BackendHelper.CreateFileTransfer(facade.File.Source.Path, fileLayoutPath, false, facade.File.SourceLineNumbers); | 105 | var transfer = this.BackendHelper.CreateFileTransfer(facade.SourcePath, fileLayoutPath, false, facade.SourceLineNumber); |
106 | fileTransfers.Add(transfer); | 106 | fileTransfers.Add(transfer); |
107 | 107 | ||
108 | // Track the location where the cabinet will be placed. If the transfer is | 108 | // Track the location where the cabinet will be placed. If the transfer is |
@@ -110,7 +110,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
110 | // because if the source and destination of the transfer is the same, we | 110 | // because if the source and destination of the transfer is the same, we |
111 | // don't want to clean the file because we'd be deleting the original | 111 | // don't want to clean the file because we'd be deleting the original |
112 | // (and that would be bad). | 112 | // (and that would be bad). |
113 | var tracked = this.BackendHelper.TrackFile(transfer.Destination, TrackedFileType.Final, facade.File.SourceLineNumbers); | 113 | var tracked = this.BackendHelper.TrackFile(transfer.Destination, TrackedFileType.Final, facade.SourceLineNumber); |
114 | tracked.Clean = !transfer.Redundant; | 114 | tracked.Clean = !transfer.Redundant; |
115 | 115 | ||
116 | trackedFiles.Add(tracked); | 116 | trackedFiles.Add(tracked); |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs index e9b0d612..749f9ac0 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs | |||
@@ -11,24 +11,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
11 | using WixToolset.Data.WindowsInstaller; | 11 | using WixToolset.Data.WindowsInstaller; |
12 | using WixToolset.Extensibility.Services; | 12 | using WixToolset.Extensibility.Services; |
13 | 13 | ||
14 | /// <summary> | ||
15 | /// Set sequence numbers for all the actions and create tuples in the output object. | ||
16 | /// </summary> | ||
14 | internal class SequenceActionsCommand | 17 | internal class SequenceActionsCommand |
15 | { | 18 | { |
16 | public SequenceActionsCommand(IntermediateSection section) | 19 | public SequenceActionsCommand(IMessaging messaging, IntermediateSection section) |
17 | { | 20 | { |
21 | this.Messaging = messaging; | ||
18 | this.Section = section; | 22 | this.Section = section; |
19 | 23 | ||
20 | this.RelativeActionsForActions = new Dictionary<string, RelativeActions>(); | 24 | this.RelativeActionsForActions = new Dictionary<string, RelativeActions>(); |
21 | } | 25 | } |
22 | 26 | ||
27 | private IMessaging Messaging { get; } | ||
28 | |||
23 | private IntermediateSection Section { get; } | 29 | private IntermediateSection Section { get; } |
24 | 30 | ||
25 | private Dictionary<string, RelativeActions> RelativeActionsForActions { get; } | 31 | private Dictionary<string, RelativeActions> RelativeActionsForActions { get; } |
26 | 32 | ||
27 | public IMessaging Messaging { private get; set; } | ||
28 | |||
29 | /// <summary> | ||
30 | /// Set sequence numbers for all the actions and create tuples in the output object. | ||
31 | /// </summary> | ||
32 | public void Execute() | 33 | public void Execute() |
33 | { | 34 | { |
34 | var requiredActionTuples = new Dictionary<string, WixActionTuple>(); | 35 | var requiredActionTuples = new Dictionary<string, WixActionTuple>(); |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs index 1f2a22d9..81d46b41 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs | |||
@@ -59,27 +59,27 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
59 | FileInfo fileInfo = null; | 59 | FileInfo fileInfo = null; |
60 | try | 60 | try |
61 | { | 61 | { |
62 | fileInfo = new FileInfo(facade.File.Source.Path); | 62 | fileInfo = new FileInfo(facade.SourcePath); |
63 | } | 63 | } |
64 | catch (ArgumentException) | 64 | catch (ArgumentException) |
65 | { | 65 | { |
66 | this.Messaging.Write(ErrorMessages.InvalidFileName(facade.File.SourceLineNumbers, facade.File.Source.Path)); | 66 | this.Messaging.Write(ErrorMessages.InvalidFileName(facade.SourceLineNumber, facade.SourcePath)); |
67 | return; | 67 | return; |
68 | } | 68 | } |
69 | catch (PathTooLongException) | 69 | catch (PathTooLongException) |
70 | { | 70 | { |
71 | this.Messaging.Write(ErrorMessages.InvalidFileName(facade.File.SourceLineNumbers, facade.File.Source.Path)); | 71 | this.Messaging.Write(ErrorMessages.InvalidFileName(facade.SourceLineNumber, facade.SourcePath)); |
72 | return; | 72 | return; |
73 | } | 73 | } |
74 | catch (NotSupportedException) | 74 | catch (NotSupportedException) |
75 | { | 75 | { |
76 | this.Messaging.Write(ErrorMessages.InvalidFileName(facade.File.SourceLineNumbers, facade.File.Source.Path)); | 76 | this.Messaging.Write(ErrorMessages.InvalidFileName(facade.SourceLineNumber, facade.SourcePath)); |
77 | return; | 77 | return; |
78 | } | 78 | } |
79 | 79 | ||
80 | if (!fileInfo.Exists) | 80 | if (!fileInfo.Exists) |
81 | { | 81 | { |
82 | this.Messaging.Write(ErrorMessages.CannotFindFile(facade.File.SourceLineNumbers, facade.File.Id.Id, facade.File.Name, facade.File.Source.Path)); | 82 | this.Messaging.Write(ErrorMessages.CannotFindFile(facade.SourceLineNumber, facade.Id, facade.FileName, facade.SourcePath)); |
83 | return; | 83 | return; |
84 | } | 84 | } |
85 | 85 | ||
@@ -87,10 +87,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
87 | { | 87 | { |
88 | if (Int32.MaxValue < fileStream.Length) | 88 | if (Int32.MaxValue < fileStream.Length) |
89 | { | 89 | { |
90 | throw new WixException(ErrorMessages.FileTooLarge(facade.File.SourceLineNumbers, facade.File.Source.Path)); | 90 | throw new WixException(ErrorMessages.FileTooLarge(facade.SourceLineNumber, facade.SourcePath)); |
91 | } | 91 | } |
92 | 92 | ||
93 | facade.File.FileSize = Convert.ToInt32(fileStream.Length, CultureInfo.InvariantCulture); | 93 | facade.FileSize = Convert.ToInt32(fileStream.Length, CultureInfo.InvariantCulture); |
94 | } | 94 | } |
95 | 95 | ||
96 | string version = null; | 96 | string version = null; |
@@ -103,7 +103,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
103 | { | 103 | { |
104 | if (0x2 == e.NativeErrorCode) // ERROR_FILE_NOT_FOUND | 104 | if (0x2 == e.NativeErrorCode) // ERROR_FILE_NOT_FOUND |
105 | { | 105 | { |
106 | throw new WixException(ErrorMessages.FileNotFound(facade.File.SourceLineNumbers, fileInfo.FullName)); | 106 | throw new WixException(ErrorMessages.FileNotFound(facade.SourceLineNumber, fileInfo.FullName)); |
107 | } | 107 | } |
108 | else | 108 | else |
109 | { | 109 | { |
@@ -118,7 +118,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
118 | { | 118 | { |
119 | // not overwriting hash, so don't do the rest of these options. | 119 | // not overwriting hash, so don't do the rest of these options. |
120 | } | 120 | } |
121 | else if (null != facade.File.Version) | 121 | else if (null != facade.Version) |
122 | { | 122 | { |
123 | // Search all of the file rows available to see if the specified version is actually a companion file. Yes, this looks | 123 | // Search all of the file rows available to see if the specified version is actually a companion file. Yes, this looks |
124 | // very expensive and you're probably thinking it would be better to create an index of some sort to do an O(1) look up. | 124 | // very expensive and you're probably thinking it would be better to create an index of some sort to do an O(1) look up. |
@@ -127,16 +127,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
127 | // | 127 | // |
128 | // Also, if we do not find a matching file identifier then the user provided a default version and is providing a version | 128 | // Also, if we do not find a matching file identifier then the user provided a default version and is providing a version |
129 | // for unversioned file. That's allowed but generally a dangerous thing to do so let's point that out to the user. | 129 | // for unversioned file. That's allowed but generally a dangerous thing to do so let's point that out to the user. |
130 | if (!this.FileFacades.Any(r => facade.File.Version.Equals(r.File.Id.Id, StringComparison.Ordinal))) | 130 | if (!this.FileFacades.Any(r => facade.Version.Equals(r.Id, StringComparison.Ordinal))) |
131 | { | 131 | { |
132 | this.Messaging.Write(WarningMessages.DefaultVersionUsedForUnversionedFile(facade.File.SourceLineNumbers, facade.File.Version, facade.File.Id.Id)); | 132 | this.Messaging.Write(WarningMessages.DefaultVersionUsedForUnversionedFile(facade.SourceLineNumber, facade.Version, facade.Id)); |
133 | } | 133 | } |
134 | } | 134 | } |
135 | else | 135 | else |
136 | { | 136 | { |
137 | if (null != facade.File.Language) | 137 | if (null != facade.Language) |
138 | { | 138 | { |
139 | this.Messaging.Write(WarningMessages.DefaultLanguageUsedForUnversionedFile(facade.File.SourceLineNumbers, facade.File.Language, facade.File.Id.Id)); | 139 | this.Messaging.Write(WarningMessages.DefaultLanguageUsedForUnversionedFile(facade.SourceLineNumber, facade.Language, facade.Id)); |
140 | } | 140 | } |
141 | 141 | ||
142 | int[] hash; | 142 | int[] hash; |
@@ -148,7 +148,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
148 | { | 148 | { |
149 | if (0x2 == e.NativeErrorCode) // ERROR_FILE_NOT_FOUND | 149 | if (0x2 == e.NativeErrorCode) // ERROR_FILE_NOT_FOUND |
150 | { | 150 | { |
151 | throw new WixException(ErrorMessages.FileNotFound(facade.File.SourceLineNumbers, fileInfo.FullName)); | 151 | throw new WixException(ErrorMessages.FileNotFound(facade.SourceLineNumber, fileInfo.FullName)); |
152 | } | 152 | } |
153 | else | 153 | else |
154 | { | 154 | { |
@@ -158,7 +158,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
158 | 158 | ||
159 | if (null == facade.Hash) | 159 | if (null == facade.Hash) |
160 | { | 160 | { |
161 | facade.Hash = new MsiFileHashTuple(facade.File.SourceLineNumbers, facade.File.Id); | 161 | facade.Hash = new MsiFileHashTuple(facade.SourceLineNumber, facade.Identifier); |
162 | this.Section.Tuples.Add(facade.Hash); | 162 | this.Section.Tuples.Add(facade.Hash); |
163 | } | 163 | } |
164 | 164 | ||
@@ -173,11 +173,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
173 | { | 173 | { |
174 | // If no version was provided by the user, use the version from the file itself. | 174 | // If no version was provided by the user, use the version from the file itself. |
175 | // This is the most common case. | 175 | // This is the most common case. |
176 | if (String.IsNullOrEmpty(facade.File.Version)) | 176 | if (String.IsNullOrEmpty(facade.Version)) |
177 | { | 177 | { |
178 | facade.File.Version = version; | 178 | facade.Version = version; |
179 | } | 179 | } |
180 | else if (!this.FileFacades.Any(r => facade.File.Version.Equals(r.File.Id.Id, StringComparison.Ordinal))) // this looks expensive, but see explanation below. | 180 | else if (!this.FileFacades.Any(r => facade.Version.Equals(r.Id, StringComparison.Ordinal))) // this looks expensive, but see explanation below. |
181 | { | 181 | { |
182 | // The user provided a default version for the file row so we looked for a companion file (a file row with Id matching | 182 | // The user provided a default version for the file row so we looked for a companion file (a file row with Id matching |
183 | // the version value). We didn't find it so, we will override the default version they provided with the actual | 183 | // the version value). We didn't find it so, we will override the default version they provided with the actual |
@@ -188,41 +188,41 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
188 | // | 188 | // |
189 | // Also note this case can occur when the file is being updated using the WixBindUpdatedFiles extension mechanism. | 189 | // Also note this case can occur when the file is being updated using the WixBindUpdatedFiles extension mechanism. |
190 | // That's typically even more rare than companion files so again, no index, just search. | 190 | // That's typically even more rare than companion files so again, no index, just search. |
191 | facade.File.Version = version; | 191 | facade.Version = version; |
192 | } | 192 | } |
193 | 193 | ||
194 | if (!String.IsNullOrEmpty(facade.File.Language) && String.IsNullOrEmpty(language)) | 194 | if (!String.IsNullOrEmpty(facade.Language) && String.IsNullOrEmpty(language)) |
195 | { | 195 | { |
196 | this.Messaging.Write(WarningMessages.DefaultLanguageUsedForVersionedFile(facade.File.SourceLineNumbers, facade.File.Language, facade.File.Id.Id)); | 196 | this.Messaging.Write(WarningMessages.DefaultLanguageUsedForVersionedFile(facade.SourceLineNumber, facade.Language, facade.Id)); |
197 | } | 197 | } |
198 | else // override the default provided by the user (usually nothing) with the actual language from the file itself. | 198 | else // override the default provided by the user (usually nothing) with the actual language from the file itself. |
199 | { | 199 | { |
200 | facade.File.Language = language; | 200 | facade.Language = language; |
201 | } | 201 | } |
202 | 202 | ||
203 | // Populate the binder variables for this file information if requested. | 203 | // Populate the binder variables for this file information if requested. |
204 | if (null != this.VariableCache) | 204 | if (null != this.VariableCache) |
205 | { | 205 | { |
206 | if (!String.IsNullOrEmpty(facade.File.Version)) | 206 | if (!String.IsNullOrEmpty(facade.Version)) |
207 | { | 207 | { |
208 | var key = String.Format(CultureInfo.InvariantCulture, "fileversion.{0}", facade.File.Id.Id); | 208 | var key = String.Format(CultureInfo.InvariantCulture, "fileversion.{0}", facade.Id); |
209 | this.VariableCache[key] = facade.File.Version; | 209 | this.VariableCache[key] = facade.Version; |
210 | } | 210 | } |
211 | 211 | ||
212 | if (!String.IsNullOrEmpty(facade.File.Language)) | 212 | if (!String.IsNullOrEmpty(facade.Language)) |
213 | { | 213 | { |
214 | var key = String.Format(CultureInfo.InvariantCulture, "filelanguage.{0}", facade.File.Id.Id); | 214 | var key = String.Format(CultureInfo.InvariantCulture, "filelanguage.{0}", facade.Id); |
215 | this.VariableCache[key] = facade.File.Language; | 215 | this.VariableCache[key] = facade.Language; |
216 | } | 216 | } |
217 | } | 217 | } |
218 | } | 218 | } |
219 | 219 | ||
220 | // If this is a CLR assembly, load the assembly and get the assembly name information | 220 | // If this is a CLR assembly, load the assembly and get the assembly name information |
221 | if (AssemblyType.DotNetAssembly == facade.Assembly?.Type) | 221 | if (AssemblyType.DotNetAssembly == facade.AssemblyType) |
222 | { | 222 | { |
223 | try | 223 | try |
224 | { | 224 | { |
225 | var assemblyName = AssemblyNameReader.ReadAssembly(facade.File.SourceLineNumbers, fileInfo.FullName, version); | 225 | var assemblyName = AssemblyNameReader.ReadAssembly(facade.SourceLineNumber, fileInfo.FullName, version); |
226 | 226 | ||
227 | this.SetMsiAssemblyName(assemblyNameTuples, facade, "name", assemblyName.Name); | 227 | this.SetMsiAssemblyName(assemblyNameTuples, facade, "name", assemblyName.Name); |
228 | this.SetMsiAssemblyName(assemblyNameTuples, facade, "culture", assemblyName.Culture); | 228 | this.SetMsiAssemblyName(assemblyNameTuples, facade, "culture", assemblyName.Culture); |
@@ -242,9 +242,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
242 | { | 242 | { |
243 | this.SetMsiAssemblyName(assemblyNameTuples, facade, "publicKeyToken", assemblyName.PublicKeyToken); | 243 | this.SetMsiAssemblyName(assemblyNameTuples, facade, "publicKeyToken", assemblyName.PublicKeyToken); |
244 | } | 244 | } |
245 | else if (facade.Assembly.ApplicationFileRef == null) | 245 | else if (facade.AssemblyApplicationFileRef == null) |
246 | { | 246 | { |
247 | throw new WixException(ErrorMessages.GacAssemblyNoStrongName(facade.File.SourceLineNumbers, fileInfo.FullName, facade.File.ComponentRef)); | 247 | throw new WixException(ErrorMessages.GacAssemblyNoStrongName(facade.SourceLineNumber, fileInfo.FullName, facade.ComponentRef)); |
248 | } | 248 | } |
249 | 249 | ||
250 | if (!String.IsNullOrEmpty(assemblyName.FileVersion)) | 250 | if (!String.IsNullOrEmpty(assemblyName.FileVersion)) |
@@ -255,7 +255,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
255 | // add the assembly name to the information cache | 255 | // add the assembly name to the information cache |
256 | if (null != this.VariableCache) | 256 | if (null != this.VariableCache) |
257 | { | 257 | { |
258 | this.VariableCache[$"assemblyfullname.{facade.File.Id.Id}"] = assemblyName.GetFullName(); | 258 | this.VariableCache[$"assemblyfullname.{facade.Id}"] = assemblyName.GetFullName(); |
259 | } | 259 | } |
260 | } | 260 | } |
261 | catch (WixException e) | 261 | catch (WixException e) |
@@ -263,20 +263,20 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
263 | this.Messaging.Write(e.Error); | 263 | this.Messaging.Write(e.Error); |
264 | } | 264 | } |
265 | } | 265 | } |
266 | else if (AssemblyType.Win32Assembly == facade.Assembly?.Type) | 266 | else if (AssemblyType.Win32Assembly == facade.AssemblyType) |
267 | { | 267 | { |
268 | // TODO: Consider passing in the this.FileFacades as an indexed collection instead of searching through | 268 | // TODO: Consider passing in the this.FileFacades as an indexed collection instead of searching through |
269 | // all files like this. Even though this is a rare case it looks like we might be able to index the | 269 | // all files like this. Even though this is a rare case it looks like we might be able to index the |
270 | // file earlier. | 270 | // file earlier. |
271 | var fileManifest = this.FileFacades.FirstOrDefault(r => r.File.Id.Id.Equals(facade.Assembly.ManifestFileRef, StringComparison.Ordinal)); | 271 | var fileManifest = this.FileFacades.FirstOrDefault(r => r.Id.Equals(facade.AssemblyManifestFileRef, StringComparison.Ordinal)); |
272 | if (null == fileManifest) | 272 | if (null == fileManifest) |
273 | { | 273 | { |
274 | this.Messaging.Write(ErrorMessages.MissingManifestForWin32Assembly(facade.File.SourceLineNumbers, facade.File.Id.Id, facade.Assembly.ManifestFileRef)); | 274 | this.Messaging.Write(ErrorMessages.MissingManifestForWin32Assembly(facade.SourceLineNumber, facade.Id, facade.AssemblyManifestFileRef)); |
275 | } | 275 | } |
276 | 276 | ||
277 | try | 277 | try |
278 | { | 278 | { |
279 | var assemblyName = AssemblyNameReader.ReadAssemblyManifest(facade.File.SourceLineNumbers, fileManifest.File.Source.Path); | 279 | var assemblyName = AssemblyNameReader.ReadAssemblyManifest(facade.SourceLineNumber, fileManifest.SourcePath); |
280 | 280 | ||
281 | if (!String.IsNullOrEmpty(assemblyName.Name)) | 281 | if (!String.IsNullOrEmpty(assemblyName.Name)) |
282 | { | 282 | { |
@@ -315,41 +315,41 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
315 | /// create a new row. | 315 | /// create a new row. |
316 | /// </summary> | 316 | /// </summary> |
317 | /// <param name="assemblyNameTuples">MsiAssemblyName table.</param> | 317 | /// <param name="assemblyNameTuples">MsiAssemblyName table.</param> |
318 | /// <param name="file">FileFacade containing the assembly read for the MsiAssemblyName row.</param> | 318 | /// <param name="facade">FileFacade containing the assembly read for the MsiAssemblyName row.</param> |
319 | /// <param name="name">MsiAssemblyName name.</param> | 319 | /// <param name="name">MsiAssemblyName name.</param> |
320 | /// <param name="value">MsiAssemblyName value.</param> | 320 | /// <param name="value">MsiAssemblyName value.</param> |
321 | private void SetMsiAssemblyName(Dictionary<string, MsiAssemblyNameTuple> assemblyNameTuples, FileFacade file, string name, string value) | 321 | private void SetMsiAssemblyName(Dictionary<string, MsiAssemblyNameTuple> assemblyNameTuples, FileFacade facade, string name, string value) |
322 | { | 322 | { |
323 | // check for null value (this can occur when grabbing the file version from an assembly without one) | 323 | // check for null value (this can occur when grabbing the file version from an assembly without one) |
324 | if (String.IsNullOrEmpty(value)) | 324 | if (String.IsNullOrEmpty(value)) |
325 | { | 325 | { |
326 | this.Messaging.Write(WarningMessages.NullMsiAssemblyNameValue(file.File.SourceLineNumbers, file.File.ComponentRef, name)); | 326 | this.Messaging.Write(WarningMessages.NullMsiAssemblyNameValue(facade.SourceLineNumber, facade.ComponentRef, name)); |
327 | } | 327 | } |
328 | else | 328 | else |
329 | { | 329 | { |
330 | // if the assembly will be GAC'd and the name in the file table doesn't match the name in the MsiAssemblyName table, error because the install will fail. | 330 | // if the assembly will be GAC'd and the name in the file table doesn't match the name in the MsiAssemblyName table, error because the install will fail. |
331 | if ("name" == name && AssemblyType.DotNetAssembly == file.Assembly.Type && | 331 | if ("name" == name && AssemblyType.DotNetAssembly == facade.AssemblyType && |
332 | String.IsNullOrEmpty(file.Assembly.ApplicationFileRef) && | 332 | String.IsNullOrEmpty(facade.AssemblyApplicationFileRef) && |
333 | !String.Equals(Path.GetFileNameWithoutExtension(file.File.Name), value, StringComparison.OrdinalIgnoreCase)) | 333 | !String.Equals(Path.GetFileNameWithoutExtension(facade.FileName), value, StringComparison.OrdinalIgnoreCase)) |
334 | { | 334 | { |
335 | this.Messaging.Write(ErrorMessages.GACAssemblyIdentityWarning(file.File.SourceLineNumbers, Path.GetFileNameWithoutExtension(file.File.Name), value)); | 335 | this.Messaging.Write(ErrorMessages.GACAssemblyIdentityWarning(facade.SourceLineNumber, Path.GetFileNameWithoutExtension(facade.FileName), value)); |
336 | } | 336 | } |
337 | 337 | ||
338 | // override directly authored value | 338 | // override directly authored value |
339 | var lookup = String.Concat(file.File.ComponentRef, "/", name); | 339 | var lookup = String.Concat(facade.ComponentRef, "/", name); |
340 | if (!assemblyNameTuples.TryGetValue(lookup, out var assemblyNameRow)) | 340 | if (!assemblyNameTuples.TryGetValue(lookup, out var assemblyNameRow)) |
341 | { | 341 | { |
342 | assemblyNameRow = new MsiAssemblyNameTuple(file.File.SourceLineNumbers); | 342 | assemblyNameRow = new MsiAssemblyNameTuple(facade.SourceLineNumber); |
343 | assemblyNameRow.ComponentRef = file.File.ComponentRef; | 343 | assemblyNameRow.ComponentRef = facade.ComponentRef; |
344 | assemblyNameRow.Name = name; | 344 | assemblyNameRow.Name = name; |
345 | assemblyNameRow.Value = value; | 345 | assemblyNameRow.Value = value; |
346 | 346 | ||
347 | if (null == file.AssemblyNames) | 347 | if (null == facade.AssemblyNames) |
348 | { | 348 | { |
349 | file.AssemblyNames = new List<MsiAssemblyNameTuple>(); | 349 | facade.AssemblyNames = new List<MsiAssemblyNameTuple>(); |
350 | } | 350 | } |
351 | 351 | ||
352 | file.AssemblyNames.Add(assemblyNameRow); | 352 | facade.AssemblyNames.Add(assemblyNameRow); |
353 | this.Section.Tuples.Add(assemblyNameRow); | 353 | this.Section.Tuples.Add(assemblyNameRow); |
354 | } | 354 | } |
355 | 355 | ||
@@ -357,7 +357,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
357 | 357 | ||
358 | if (this.VariableCache != null) | 358 | if (this.VariableCache != null) |
359 | { | 359 | { |
360 | var key = String.Format(CultureInfo.InvariantCulture, "assembly{0}.{1}", name, file.File.Id.Id).ToLowerInvariant(); | 360 | var key = String.Format(CultureInfo.InvariantCulture, "assembly{0}.{1}", name, facade.Id).ToLowerInvariant(); |
361 | this.VariableCache[key] = value; | 361 | this.VariableCache[key] = value; |
362 | } | 362 | } |
363 | } | 363 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs index f9e3bd5a..ae872f45 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs | |||
@@ -33,7 +33,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
33 | 33 | ||
34 | // Order by Component to group the files by directory. | 34 | // Order by Component to group the files by directory. |
35 | var optimized = this.OptimizedFileFacades(); | 35 | var optimized = this.OptimizedFileFacades(); |
36 | foreach (var fileId in optimized.Select(f => f.File.Id.Id)) | 36 | foreach (var fileId in optimized.Select(f => f.Id)) |
37 | { | 37 | { |
38 | var fileRow = fileRows.Get(fileId); | 38 | var fileRow = fileRows.Get(fileId); |
39 | fileRow.Sequence = ++lastSequence; | 39 | fileRow.Sequence = ++lastSequence; |
@@ -41,13 +41,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
41 | } | 41 | } |
42 | else | 42 | else |
43 | { | 43 | { |
44 | int lastSequence = 0; | 44 | var lastSequence = 0; |
45 | MediaRow mediaRow = null; | 45 | MediaRow mediaRow = null; |
46 | Dictionary<int, List<FileFacade>> patchGroups = new Dictionary<int, List<FileFacade>>(); | 46 | var patchGroups = new Dictionary<int, List<FileFacade>>(); |
47 | 47 | ||
48 | // sequence the non-patch-added files | 48 | // sequence the non-patch-added files |
49 | var optimized = this.OptimizedFileFacades(); | 49 | var optimized = this.OptimizedFileFacades(); |
50 | foreach (FileFacade facade in optimized) | 50 | foreach (var facade in optimized) |
51 | { | 51 | { |
52 | if (null == mediaRow) | 52 | if (null == mediaRow) |
53 | { | 53 | { |
@@ -64,19 +64,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
64 | mediaRow = mediaRows.Get(facade.DiskId); | 64 | mediaRow = mediaRows.Get(facade.DiskId); |
65 | } | 65 | } |
66 | 66 | ||
67 | if (facade.File.PatchGroup.HasValue) | 67 | if (facade.PatchGroup.HasValue) |
68 | { | 68 | { |
69 | if (patchGroups.TryGetValue(facade.File.PatchGroup.Value, out var patchGroup)) | 69 | if (patchGroups.TryGetValue(facade.PatchGroup.Value, out var patchGroup)) |
70 | { | 70 | { |
71 | patchGroup = new List<FileFacade>(); | 71 | patchGroup = new List<FileFacade>(); |
72 | patchGroups.Add(facade.File.PatchGroup.Value, patchGroup); | 72 | patchGroups.Add(facade.PatchGroup.Value, patchGroup); |
73 | } | 73 | } |
74 | 74 | ||
75 | patchGroup.Add(facade); | 75 | patchGroup.Add(facade); |
76 | } | 76 | } |
77 | else | 77 | else |
78 | { | 78 | { |
79 | var fileRow = fileRows.Get(facade.File.Id.Id); | 79 | var fileRow = fileRows.Get(facade.Id); |
80 | fileRow.Sequence = ++lastSequence; | 80 | fileRow.Sequence = ++lastSequence; |
81 | } | 81 | } |
82 | } | 82 | } |
@@ -102,7 +102,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
102 | mediaRow = mediaRows.Get(facade.DiskId); | 102 | mediaRow = mediaRows.Get(facade.DiskId); |
103 | } | 103 | } |
104 | 104 | ||
105 | var fileRow = fileRows.Get(facade.File.Id.Id); | 105 | var fileRow = fileRows.Get(facade.Id); |
106 | fileRow.Sequence = ++lastSequence; | 106 | fileRow.Sequence = ++lastSequence; |
107 | } | 107 | } |
108 | } | 108 | } |
@@ -119,7 +119,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
119 | // TODO: Sort these facades even smarter by directory path and component id | 119 | // TODO: Sort these facades even smarter by directory path and component id |
120 | // and maybe file size or file extension and other creative ideas to | 120 | // and maybe file size or file extension and other creative ideas to |
121 | // get optimal install speed out of MSI. | 121 | // get optimal install speed out of MSI. |
122 | return this.FileFacades.OrderBy(f => f.File.ComponentRef); | 122 | return this.FileFacades.OrderBy(f => f.ComponentRef); |
123 | } | 123 | } |
124 | } | 124 | } |
125 | } | 125 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Data/tables.xml b/src/WixToolset.Core.WindowsInstaller/Data/tables.xml index e4b5e954..7cd1767b 100644 --- a/src/WixToolset.Core.WindowsInstaller/Data/tables.xml +++ b/src/WixToolset.Core.WindowsInstaller/Data/tables.xml | |||
@@ -156,6 +156,10 @@ | |||
156 | minValue="0" maxValue="32767" description="Integer containing bit flags representing file attributes (with the decimal value of each bit position in parentheses)"/> | 156 | minValue="0" maxValue="32767" description="Integer containing bit flags representing file attributes (with the decimal value of each bit position in parentheses)"/> |
157 | <columnDefinition name="Sequence" type="number" length="4" | 157 | <columnDefinition name="Sequence" type="number" length="4" |
158 | minValue="1" maxValue="2147483647" description="Sequence with respect to the media images; order must track cabinet order."/> | 158 | minValue="1" maxValue="2147483647" description="Sequence with respect to the media images; order must track cabinet order."/> |
159 | <columnDefinition name="DiskId" type="number" length="4" unreal="yes" | ||
160 | minValue="1" maxValue="32767" description="Disk identifier for the file."/> | ||
161 | <columnDefinition name="Source" type="object" length="0" unreal="yes" | ||
162 | category="binary" description="Path to source of file."/> | ||
159 | </tableDefinition> | 163 | </tableDefinition> |
160 | <tableDefinition name="CCPSearch"> | 164 | <tableDefinition name="CCPSearch"> |
161 | <columnDefinition name="Signature_" type="string" length="72" primaryKey="yes" | 165 | <columnDefinition name="Signature_" type="string" length="72" primaryKey="yes" |
diff --git a/src/WixToolset.Core.WindowsInstaller/Differ.cs b/src/WixToolset.Core.WindowsInstaller/Differ.cs index 32172ffd..0e1a7315 100644 --- a/src/WixToolset.Core.WindowsInstaller/Differ.cs +++ b/src/WixToolset.Core.WindowsInstaller/Differ.cs | |||
@@ -1,5 +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 | 2 | ||
3 | #if DELETE | ||
4 | |||
3 | namespace WixToolset.Core.WindowsInstaller | 5 | namespace WixToolset.Core.WindowsInstaller |
4 | { | 6 | { |
5 | using System; | 7 | using System; |
@@ -604,3 +606,5 @@ namespace WixToolset.Core.WindowsInstaller | |||
604 | } | 606 | } |
605 | } | 607 | } |
606 | } | 608 | } |
609 | |||
610 | #endif | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs b/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs index ff7472ff..582e179e 100644 --- a/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs | |||
@@ -33,40 +33,40 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
33 | public bool Execute() | 33 | public bool Execute() |
34 | { | 34 | { |
35 | // Keeps track of whether we've encountered at least one signed cab or not - we'll throw a warning if no signed cabs were encountered | 35 | // Keeps track of whether we've encountered at least one signed cab or not - we'll throw a warning if no signed cabs were encountered |
36 | bool foundUnsignedExternals = false; | 36 | var foundUnsignedExternals = false; |
37 | bool shouldCommit = false; | 37 | var shouldCommit = false; |
38 | 38 | ||
39 | FileAttributes attributes = File.GetAttributes(this.Context.InputFilePath); | 39 | var attributes = File.GetAttributes(this.Context.InputFilePath); |
40 | if (FileAttributes.ReadOnly == (attributes & FileAttributes.ReadOnly)) | 40 | if (FileAttributes.ReadOnly == (attributes & FileAttributes.ReadOnly)) |
41 | { | 41 | { |
42 | this.Messaging.Write(ErrorMessages.ReadOnlyOutputFile(this.Context.InputFilePath)); | 42 | this.Messaging.Write(ErrorMessages.ReadOnlyOutputFile(this.Context.InputFilePath)); |
43 | return shouldCommit; | 43 | return shouldCommit; |
44 | } | 44 | } |
45 | 45 | ||
46 | using (Database database = new Database(this.Context.InputFilePath, OpenDatabase.Transact)) | 46 | using (var database = new Database(this.Context.InputFilePath, OpenDatabase.Transact)) |
47 | { | 47 | { |
48 | // Just use the English codepage, because the tables we're importing only have binary streams / MSI identifiers / other non-localizable content | 48 | // Just use the English codepage, because the tables we're importing only have binary streams / MSI identifiers / other non-localizable content |
49 | int codepage = 1252; | 49 | var codepage = 1252; |
50 | 50 | ||
51 | // list of certificates for this database (hash/identifier) | 51 | // list of certificates for this database (hash/identifier) |
52 | Dictionary<string, string> certificates = new Dictionary<string, string>(); | 52 | var certificates = new Dictionary<string, string>(); |
53 | 53 | ||
54 | // Reset the in-memory tables for this new database | 54 | // Reset the in-memory tables for this new database |
55 | Table digitalSignatureTable = new Table(this.TableDefinitions["MsiDigitalSignature"]); | 55 | var digitalSignatureTable = new Table(this.TableDefinitions["MsiDigitalSignature"]); |
56 | Table digitalCertificateTable = new Table(this.TableDefinitions["MsiDigitalCertificate"]); | 56 | var digitalCertificateTable = new Table(this.TableDefinitions["MsiDigitalCertificate"]); |
57 | 57 | ||
58 | // If any digital signature records exist that are not of the media type, preserve them | 58 | // If any digital signature records exist that are not of the media type, preserve them |
59 | if (database.TableExists("MsiDigitalSignature")) | 59 | if (database.TableExists("MsiDigitalSignature")) |
60 | { | 60 | { |
61 | using (View digitalSignatureView = database.OpenExecuteView("SELECT `Table`, `SignObject`, `DigitalCertificate_`, `Hash` FROM `MsiDigitalSignature` WHERE `Table` <> 'Media'")) | 61 | using (var digitalSignatureView = database.OpenExecuteView("SELECT `Table`, `SignObject`, `DigitalCertificate_`, `Hash` FROM `MsiDigitalSignature` WHERE `Table` <> 'Media'")) |
62 | { | 62 | { |
63 | foreach (Record digitalSignatureRecord in digitalSignatureView.Records) | 63 | foreach (var digitalSignatureRecord in digitalSignatureView.Records) |
64 | { | 64 | { |
65 | Row digitalSignatureRow = null; | 65 | Row digitalSignatureRow = null; |
66 | digitalSignatureRow = digitalSignatureTable.CreateRow(null); | 66 | digitalSignatureRow = digitalSignatureTable.CreateRow(null); |
67 | 67 | ||
68 | string table = digitalSignatureRecord.GetString(0); | 68 | var table = digitalSignatureRecord.GetString(0); |
69 | string signObject = digitalSignatureRecord.GetString(1); | 69 | var signObject = digitalSignatureRecord.GetString(1); |
70 | 70 | ||
71 | digitalSignatureRow[0] = table; | 71 | digitalSignatureRow[0] = table; |
72 | digitalSignatureRow[1] = signObject; | 72 | digitalSignatureRow[1] = signObject; |
@@ -75,16 +75,16 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
75 | if (false == digitalSignatureRecord.IsNull(3)) | 75 | if (false == digitalSignatureRecord.IsNull(3)) |
76 | { | 76 | { |
77 | // Export to a file, because the MSI API's require us to provide a file path on disk | 77 | // Export to a file, because the MSI API's require us to provide a file path on disk |
78 | string hashPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalSignature"); | 78 | var hashPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalSignature"); |
79 | string hashFileName = string.Concat(table, ".", signObject, ".bin"); | 79 | var hashFileName = String.Concat(table, ".", signObject, ".bin"); |
80 | 80 | ||
81 | Directory.CreateDirectory(hashPath); | 81 | Directory.CreateDirectory(hashPath); |
82 | hashPath = Path.Combine(hashPath, hashFileName); | 82 | hashPath = Path.Combine(hashPath, hashFileName); |
83 | 83 | ||
84 | using (FileStream fs = File.Create(hashPath)) | 84 | using (var fs = File.Create(hashPath)) |
85 | { | 85 | { |
86 | int bytesRead; | 86 | int bytesRead; |
87 | byte[] buffer = new byte[1024 * 4]; | 87 | var buffer = new byte[1024 * 4]; |
88 | 88 | ||
89 | while (0 != (bytesRead = digitalSignatureRecord.GetStream(3, buffer, buffer.Length))) | 89 | while (0 != (bytesRead = digitalSignatureRecord.GetStream(3, buffer, buffer.Length))) |
90 | { | 90 | { |
@@ -101,21 +101,21 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
101 | // If any digital certificates exist, extract and preserve them | 101 | // If any digital certificates exist, extract and preserve them |
102 | if (database.TableExists("MsiDigitalCertificate")) | 102 | if (database.TableExists("MsiDigitalCertificate")) |
103 | { | 103 | { |
104 | using (View digitalCertificateView = database.OpenExecuteView("SELECT * FROM `MsiDigitalCertificate`")) | 104 | using (var digitalCertificateView = database.OpenExecuteView("SELECT * FROM `MsiDigitalCertificate`")) |
105 | { | 105 | { |
106 | foreach (Record digitalCertificateRecord in digitalCertificateView.Records) | 106 | foreach (var digitalCertificateRecord in digitalCertificateView.Records) |
107 | { | 107 | { |
108 | string certificateId = digitalCertificateRecord.GetString(1); // get the identifier of the certificate | 108 | var certificateId = digitalCertificateRecord.GetString(1); // get the identifier of the certificate |
109 | 109 | ||
110 | // Export to a file, because the MSI API's require us to provide a file path on disk | 110 | // Export to a file, because the MSI API's require us to provide a file path on disk |
111 | string certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate"); | 111 | var certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate"); |
112 | Directory.CreateDirectory(certPath); | 112 | Directory.CreateDirectory(certPath); |
113 | certPath = Path.Combine(certPath, string.Concat(certificateId, ".cer")); | 113 | certPath = Path.Combine(certPath, String.Concat(certificateId, ".cer")); |
114 | 114 | ||
115 | using (FileStream fs = File.Create(certPath)) | 115 | using (var fs = File.Create(certPath)) |
116 | { | 116 | { |
117 | int bytesRead; | 117 | int bytesRead; |
118 | byte[] buffer = new byte[1024 * 4]; | 118 | var buffer = new byte[1024 * 4]; |
119 | 119 | ||
120 | while (0 != (bytesRead = digitalCertificateRecord.GetStream(2, buffer, buffer.Length))) | 120 | while (0 != (bytesRead = digitalCertificateRecord.GetStream(2, buffer, buffer.Length))) |
121 | { | 121 | { |
@@ -124,37 +124,37 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
124 | } | 124 | } |
125 | 125 | ||
126 | // Add it to our "add to MsiDigitalCertificate" table dictionary | 126 | // Add it to our "add to MsiDigitalCertificate" table dictionary |
127 | Row digitalCertificateRow = digitalCertificateTable.CreateRow(null); | 127 | var digitalCertificateRow = digitalCertificateTable.CreateRow(null); |
128 | digitalCertificateRow[0] = certificateId; | 128 | digitalCertificateRow[0] = certificateId; |
129 | 129 | ||
130 | // Now set the file path on disk where this binary stream will be picked up at import time | 130 | // Now set the file path on disk where this binary stream will be picked up at import time |
131 | digitalCertificateRow[1] = string.Concat(certificateId, ".cer"); | 131 | digitalCertificateRow[1] = String.Concat(certificateId, ".cer"); |
132 | 132 | ||
133 | // Load the cert to get it's thumbprint | 133 | // Load the cert to get it's thumbprint |
134 | X509Certificate cert = X509Certificate.CreateFromCertFile(certPath); | 134 | var cert = X509Certificate.CreateFromCertFile(certPath); |
135 | X509Certificate2 cert2 = new X509Certificate2(cert); | 135 | var cert2 = new X509Certificate2(cert); |
136 | 136 | ||
137 | certificates.Add(cert2.Thumbprint, certificateId); | 137 | certificates.Add(cert2.Thumbprint, certificateId); |
138 | } | 138 | } |
139 | } | 139 | } |
140 | } | 140 | } |
141 | 141 | ||
142 | using (View mediaView = database.OpenExecuteView("SELECT * FROM `Media`")) | 142 | using (var mediaView = database.OpenExecuteView("SELECT * FROM `Media`")) |
143 | { | 143 | { |
144 | foreach (Record mediaRecord in mediaView.Records) | 144 | foreach (var mediaRecord in mediaView.Records) |
145 | { | 145 | { |
146 | X509Certificate2 cert2 = null; | 146 | X509Certificate2 cert2 = null; |
147 | Row digitalSignatureRow = null; | 147 | Row digitalSignatureRow = null; |
148 | 148 | ||
149 | string cabName = mediaRecord.GetString(4); // get the name of the cab | 149 | var cabName = mediaRecord.GetString(4); // get the name of the cab |
150 | // If there is no cabinet or it's an internal cab, skip it. | 150 | // If there is no cabinet or it's an internal cab, skip it. |
151 | if (String.IsNullOrEmpty(cabName) || cabName.StartsWith("#", StringComparison.Ordinal)) | 151 | if (String.IsNullOrEmpty(cabName) || cabName.StartsWith("#", StringComparison.Ordinal)) |
152 | { | 152 | { |
153 | continue; | 153 | continue; |
154 | } | 154 | } |
155 | 155 | ||
156 | string cabId = mediaRecord.GetString(1); // get the ID of the cab | 156 | var cabId = mediaRecord.GetString(1); // get the ID of the cab |
157 | string cabPath = Path.Combine(Path.GetDirectoryName(this.Context.InputFilePath), cabName); | 157 | var cabPath = Path.Combine(Path.GetDirectoryName(this.Context.InputFilePath), cabName); |
158 | 158 | ||
159 | // If the cabs aren't there, throw an error but continue to catch the other errors | 159 | // If the cabs aren't there, throw an error but continue to catch the other errors |
160 | if (!File.Exists(cabPath)) | 160 | if (!File.Exists(cabPath)) |
@@ -166,12 +166,12 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
166 | try | 166 | try |
167 | { | 167 | { |
168 | // Get the certificate from the cab | 168 | // Get the certificate from the cab |
169 | X509Certificate signedFileCert = X509Certificate.CreateFromSignedFile(cabPath); | 169 | var signedFileCert = X509Certificate.CreateFromSignedFile(cabPath); |
170 | cert2 = new X509Certificate2(signedFileCert); | 170 | cert2 = new X509Certificate2(signedFileCert); |
171 | } | 171 | } |
172 | catch (System.Security.Cryptography.CryptographicException e) | 172 | catch (System.Security.Cryptography.CryptographicException e) |
173 | { | 173 | { |
174 | uint HResult = unchecked((uint)Marshal.GetHRForException(e)); | 174 | var HResult = unchecked((uint)Marshal.GetHRForException(e)); |
175 | 175 | ||
176 | // If the file has no cert, continue, but flag that we found at least one so we can later give a warning | 176 | // If the file has no cert, continue, but flag that we found at least one so we can later give a warning |
177 | if (0x80092009 == HResult) // CRYPT_E_NO_MATCH | 177 | if (0x80092009 == HResult) // CRYPT_E_NO_MATCH |
@@ -197,26 +197,26 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
197 | if (!certificates.ContainsKey(cert2.Thumbprint)) | 197 | if (!certificates.ContainsKey(cert2.Thumbprint)) |
198 | { | 198 | { |
199 | // generate a stable identifier | 199 | // generate a stable identifier |
200 | string certificateGeneratedId = Common.GenerateIdentifier("cer", cert2.Thumbprint); | 200 | var certificateGeneratedId = Common.GenerateIdentifier("cer", cert2.Thumbprint); |
201 | 201 | ||
202 | // Add it to our "add to MsiDigitalCertificate" table dictionary | 202 | // Add it to our "add to MsiDigitalCertificate" table dictionary |
203 | Row digitalCertificateRow = digitalCertificateTable.CreateRow(null); | 203 | var digitalCertificateRow = digitalCertificateTable.CreateRow(null); |
204 | digitalCertificateRow[0] = certificateGeneratedId; | 204 | digitalCertificateRow[0] = certificateGeneratedId; |
205 | 205 | ||
206 | // Export to a file, because the MSI API's require us to provide a file path on disk | 206 | // Export to a file, because the MSI API's require us to provide a file path on disk |
207 | string certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate"); | 207 | var certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate"); |
208 | Directory.CreateDirectory(certPath); | 208 | Directory.CreateDirectory(certPath); |
209 | certPath = Path.Combine(certPath, string.Concat(cert2.Thumbprint, ".cer")); | 209 | certPath = Path.Combine(certPath, String.Concat(cert2.Thumbprint, ".cer")); |
210 | File.Delete(certPath); | 210 | File.Delete(certPath); |
211 | 211 | ||
212 | using (BinaryWriter writer = new BinaryWriter(File.Open(certPath, FileMode.Create))) | 212 | using (var writer = new BinaryWriter(File.Open(certPath, FileMode.Create))) |
213 | { | 213 | { |
214 | writer.Write(cert2.RawData); | 214 | writer.Write(cert2.RawData); |
215 | writer.Close(); | 215 | writer.Close(); |
216 | } | 216 | } |
217 | 217 | ||
218 | // Now set the file path on disk where this binary stream will be picked up at import time | 218 | // Now set the file path on disk where this binary stream will be picked up at import time |
219 | digitalCertificateRow[1] = string.Concat(cert2.Thumbprint, ".cer"); | 219 | digitalCertificateRow[1] = String.Concat(cert2.Thumbprint, ".cer"); |
220 | 220 | ||
221 | certificates.Add(cert2.Thumbprint, certificateGeneratedId); | 221 | certificates.Add(cert2.Thumbprint, certificateGeneratedId); |
222 | } | 222 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/MspBackend.cs b/src/WixToolset.Core.WindowsInstaller/MspBackend.cs index b6e72e11..8aa450bf 100644 --- a/src/WixToolset.Core.WindowsInstaller/MspBackend.cs +++ b/src/WixToolset.Core.WindowsInstaller/MspBackend.cs | |||
@@ -3,36 +3,74 @@ | |||
3 | namespace WixToolset.Core.WindowsInstaller | 3 | namespace WixToolset.Core.WindowsInstaller |
4 | { | 4 | { |
5 | using System; | 5 | using System; |
6 | using System.ComponentModel; | 6 | using System.Collections.Generic; |
7 | using System.IO; | 7 | using System.IO; |
8 | using WixToolset.Core.Native; | 8 | using System.Linq; |
9 | using WixToolset.Core.WindowsInstaller.Bind; | ||
10 | using WixToolset.Core.WindowsInstaller.Msi; | ||
9 | using WixToolset.Core.WindowsInstaller.Unbind; | 11 | using WixToolset.Core.WindowsInstaller.Unbind; |
10 | using WixToolset.Data; | 12 | using WixToolset.Data; |
11 | using WixToolset.Data.Bind; | 13 | using WixToolset.Data.Tuples; |
14 | using WixToolset.Data.WindowsInstaller; | ||
12 | using WixToolset.Extensibility; | 15 | using WixToolset.Extensibility; |
13 | using WixToolset.Extensibility.Data; | 16 | using WixToolset.Extensibility.Data; |
14 | using WixToolset.Ole32; | 17 | using WixToolset.Extensibility.Services; |
15 | 18 | ||
16 | internal class MspBackend : IBackend | 19 | internal class MspBackend : IBackend |
17 | { | 20 | { |
18 | public IBindResult Bind(IBindContext context) | 21 | public IBindResult Bind(IBindContext context) |
19 | { | 22 | { |
20 | throw new NotImplementedException(); | 23 | var messaging = context.ServiceProvider.GetService<IMessaging>(); |
21 | } | ||
22 | 24 | ||
23 | public IDecompileResult Decompile(IDecompileContext context) | 25 | var extensionManager = context.ServiceProvider.GetService<IExtensionManager>(); |
24 | { | ||
25 | throw new NotImplementedException(); | ||
26 | } | ||
27 | 26 | ||
28 | public bool Inscribe(IInscribeContext context) | 27 | var backendExtensions = extensionManager.GetServices<IWindowsInstallerBackendBinderExtension>(); |
29 | { | 28 | |
30 | throw new NotImplementedException(); | 29 | foreach (var extension in backendExtensions) |
30 | { | ||
31 | extension.PreBackendBind(context); | ||
32 | } | ||
33 | |||
34 | // Create transforms named in patch transforms. | ||
35 | IEnumerable<PatchTransform> patchTransforms; | ||
36 | { | ||
37 | var command = new CreatePatchTransformsCommand(messaging, context.IntermediateRepresentation, context.IntermediateFolder); | ||
38 | patchTransforms = command.Execute(); | ||
39 | } | ||
40 | |||
41 | // Enhance the intermediate by attaching the created patch transforms. | ||
42 | IEnumerable<SubStorage> subStorages; | ||
43 | { | ||
44 | var command = new AttachPatchTransformsCommand(messaging, context.IntermediateRepresentation, patchTransforms); | ||
45 | subStorages = command.Execute(); | ||
46 | } | ||
47 | |||
48 | // Create WindowsInstallerData with patch metdata and transforms as sub-storages | ||
49 | // Create MSP from WindowsInstallerData | ||
50 | using (var command = new BindDatabaseCommand(context, backendExtensions, subStorages, null)) | ||
51 | { | ||
52 | command.Execute(); | ||
53 | |||
54 | var result = context.ServiceProvider.GetService<IBindResult>(); | ||
55 | result.FileTransfers = command.FileTransfers; | ||
56 | result.TrackedFiles = command.TrackedFiles; | ||
57 | |||
58 | foreach (var extension in backendExtensions) | ||
59 | { | ||
60 | extension.PostBackendBind(result, command.Wixout); | ||
61 | } | ||
62 | |||
63 | return result; | ||
64 | } | ||
31 | } | 65 | } |
32 | 66 | ||
67 | public IDecompileResult Decompile(IDecompileContext context) => throw new NotImplementedException(); | ||
68 | |||
69 | public bool Inscribe(IInscribeContext context) => throw new NotImplementedException(); | ||
70 | |||
33 | public Intermediate Unbind(IUnbindContext context) | 71 | public Intermediate Unbind(IUnbindContext context) |
34 | { | 72 | { |
35 | #if REVISIT_FOR_PATCHING | 73 | #if TODO_PATCHING |
36 | Output patch; | 74 | Output patch; |
37 | 75 | ||
38 | // patch files are essentially database files (use a special flag to let the API know its a patch file) | 76 | // patch files are essentially database files (use a special flag to let the API know its a patch file) |
@@ -116,4 +154,4 @@ namespace WixToolset.Core.WindowsInstaller | |||
116 | throw new NotImplementedException(); | 154 | throw new NotImplementedException(); |
117 | } | 155 | } |
118 | } | 156 | } |
119 | } \ No newline at end of file | 157 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/MstBackend.cs b/src/WixToolset.Core.WindowsInstaller/MstBackend.cs index b64f417a..a6d86c10 100644 --- a/src/WixToolset.Core.WindowsInstaller/MstBackend.cs +++ b/src/WixToolset.Core.WindowsInstaller/MstBackend.cs | |||
@@ -12,7 +12,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
12 | { | 12 | { |
13 | public IBindResult Bind(IBindContext context) | 13 | public IBindResult Bind(IBindContext context) |
14 | { | 14 | { |
15 | #if REVISIT_FOR_PATCHING | 15 | #if TODO_PATCHING |
16 | var command = new BindTransformCommand(); | 16 | var command = new BindTransformCommand(); |
17 | command.Extensions = context.Extensions; | 17 | command.Extensions = context.Extensions; |
18 | command.TempFilesLocation = context.IntermediateFolder; | 18 | command.TempFilesLocation = context.IntermediateFolder; |
diff --git a/src/WixToolset.Core.WindowsInstaller/Ole32/Storage.cs b/src/WixToolset.Core.WindowsInstaller/Ole32/Storage.cs index c6a43bc4..541d899a 100644 --- a/src/WixToolset.Core.WindowsInstaller/Ole32/Storage.cs +++ b/src/WixToolset.Core.WindowsInstaller/Ole32/Storage.cs | |||
@@ -63,8 +63,8 @@ namespace WixToolset.Ole32 | |||
63 | /// </summary> | 63 | /// </summary> |
64 | internal sealed class Storage : IDisposable | 64 | internal sealed class Storage : IDisposable |
65 | { | 65 | { |
66 | private readonly IStorage storage; | ||
66 | private bool disposed; | 67 | private bool disposed; |
67 | private IStorage storage; | ||
68 | 68 | ||
69 | /// <summary> | 69 | /// <summary> |
70 | /// Instantiate a new Storage. | 70 | /// Instantiate a new Storage. |
diff --git a/src/WixToolset.Core.WindowsInstaller/Patch.cs b/src/WixToolset.Core.WindowsInstaller/Patch.cs index 6549e830..42cd7152 100644 --- a/src/WixToolset.Core.WindowsInstaller/Patch.cs +++ b/src/WixToolset.Core.WindowsInstaller/Patch.cs | |||
@@ -1,5 +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 | 2 | ||
3 | #if DELETE | ||
4 | |||
3 | namespace WixToolset.Data | 5 | namespace WixToolset.Data |
4 | { | 6 | { |
5 | using System; | 7 | using System; |
@@ -50,7 +52,6 @@ namespace WixToolset.Data | |||
50 | [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.InvalidOperationException.#ctor(System.String)")] | 52 | [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.InvalidOperationException.#ctor(System.String)")] |
51 | public void AttachTransforms(List<PatchTransform> transforms) | 53 | public void AttachTransforms(List<PatchTransform> transforms) |
52 | { | 54 | { |
53 | #if REVISIT_FOR_PATCHING | ||
54 | // Track if at least one transform gets attached. | 55 | // Track if at least one transform gets attached. |
55 | bool attachedTransform = false; | 56 | bool attachedTransform = false; |
56 | 57 | ||
@@ -1229,8 +1230,10 @@ namespace WixToolset.Data | |||
1229 | } | 1230 | } |
1230 | 1231 | ||
1231 | return pairedTransform; | 1232 | return pairedTransform; |
1232 | #endif | 1233 | |
1233 | throw new NotImplementedException(); | 1234 | throw new NotImplementedException(); |
1234 | } | 1235 | } |
1235 | } | 1236 | } |
1236 | } | 1237 | } |
1238 | |||
1239 | #endif | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/PatchTransform.cs b/src/WixToolset.Core.WindowsInstaller/PatchTransform.cs index 0dc1e874..f58ca53f 100644 --- a/src/WixToolset.Core.WindowsInstaller/PatchTransform.cs +++ b/src/WixToolset.Core.WindowsInstaller/PatchTransform.cs | |||
@@ -1,5 +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 | 2 | ||
3 | #if DELETE | ||
4 | |||
3 | namespace WixToolset | 5 | namespace WixToolset |
4 | { | 6 | { |
5 | using System; | 7 | using System; |
@@ -50,7 +52,6 @@ namespace WixToolset | |||
50 | /// </summary> | 52 | /// </summary> |
51 | public void Validate() | 53 | public void Validate() |
52 | { | 54 | { |
53 | #if REVISIT_FOR_PATCHING | ||
54 | // Changing the ProdocutCode in a patch transform is not recommended. | 55 | // Changing the ProdocutCode in a patch transform is not recommended. |
55 | Table propertyTable = this.Transform.Tables["Property"]; | 56 | Table propertyTable = this.Transform.Tables["Property"]; |
56 | if (null != propertyTable) | 57 | if (null != propertyTable) |
@@ -261,8 +262,10 @@ namespace WixToolset | |||
261 | } | 262 | } |
262 | } | 263 | } |
263 | } | 264 | } |
264 | #endif | 265 | |
265 | throw new NotImplementedException(); | 266 | throw new NotImplementedException(); |
266 | } | 267 | } |
267 | } | 268 | } |
268 | } | 269 | } |
270 | |||
271 | #endif \ No newline at end of file | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs index eca51caf..eea0fe23 100644 --- a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs | |||
@@ -19,7 +19,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
19 | 19 | ||
20 | public Intermediate Execute() | 20 | public Intermediate Execute() |
21 | { | 21 | { |
22 | #if REVISIT_FOR_PATCHING | 22 | #if TODO_PATCHING |
23 | Output output; | 23 | Output output; |
24 | 24 | ||
25 | try | 25 | try |
diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs index bdf8d542..9261fda0 100644 --- a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs | |||
@@ -4,6 +4,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
4 | { | 4 | { |
5 | using System; | 5 | using System; |
6 | using System.Collections; | 6 | using System.Collections; |
7 | using System.Collections.Generic; | ||
7 | using System.ComponentModel; | 8 | using System.ComponentModel; |
8 | using System.Globalization; | 9 | using System.Globalization; |
9 | using System.IO; | 10 | using System.IO; |
@@ -12,7 +13,6 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
12 | using WixToolset.Core.WindowsInstaller.Msi; | 13 | using WixToolset.Core.WindowsInstaller.Msi; |
13 | using WixToolset.Data; | 14 | using WixToolset.Data; |
14 | using WixToolset.Data.WindowsInstaller; | 15 | using WixToolset.Data.WindowsInstaller; |
15 | using WixToolset.Extensibility; | ||
16 | using WixToolset.Extensibility.Services; | 16 | using WixToolset.Extensibility.Services; |
17 | 17 | ||
18 | internal class UnbindTransformCommand | 18 | internal class UnbindTransformCommand |
@@ -41,21 +41,21 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
41 | 41 | ||
42 | public WindowsInstallerData Execute() | 42 | public WindowsInstallerData Execute() |
43 | { | 43 | { |
44 | WindowsInstallerData transform = new WindowsInstallerData(new SourceLineNumber(this.TransformFile)); | 44 | var transform = new WindowsInstallerData(new SourceLineNumber(this.TransformFile)); |
45 | transform.Type = OutputType.Transform; | 45 | transform.Type = OutputType.Transform; |
46 | 46 | ||
47 | // get the summary information table | 47 | // get the summary information table |
48 | using (SummaryInformation summaryInformation = new SummaryInformation(this.TransformFile)) | 48 | using (var summaryInformation = new SummaryInformation(this.TransformFile)) |
49 | { | 49 | { |
50 | Table table = transform.EnsureTable(this.TableDefinitions["_SummaryInformation"]); | 50 | var table = transform.EnsureTable(this.TableDefinitions["_SummaryInformation"]); |
51 | 51 | ||
52 | for (int i = 1; 19 >= i; i++) | 52 | for (var i = 1; 19 >= i; i++) |
53 | { | 53 | { |
54 | string value = summaryInformation.GetProperty(i); | 54 | var value = summaryInformation.GetProperty(i); |
55 | 55 | ||
56 | if (0 < value.Length) | 56 | if (0 < value.Length) |
57 | { | 57 | { |
58 | Row row = table.CreateRow(transform.SourceLineNumbers); | 58 | var row = table.CreateRow(transform.SourceLineNumbers); |
59 | row[0] = i; | 59 | row[0] = i; |
60 | row[1] = value; | 60 | row[1] = value; |
61 | } | 61 | } |
@@ -63,9 +63,9 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
63 | } | 63 | } |
64 | 64 | ||
65 | // create a schema msi which hopefully matches the table schemas in the transform | 65 | // create a schema msi which hopefully matches the table schemas in the transform |
66 | WindowsInstallerData schemaOutput = new WindowsInstallerData(null); | 66 | var schemaOutput = new WindowsInstallerData(null); |
67 | string msiDatabaseFile = Path.Combine(this.IntermediateFolder, "schema.msi"); | 67 | var msiDatabaseFile = Path.Combine(this.IntermediateFolder, "schema.msi"); |
68 | foreach (TableDefinition tableDefinition in this.TableDefinitions) | 68 | foreach (var tableDefinition in this.TableDefinitions) |
69 | { | 69 | { |
70 | // skip unreal tables and the Patch table | 70 | // skip unreal tables and the Patch table |
71 | if (!tableDefinition.Unreal && "Patch" != tableDefinition.Name) | 71 | if (!tableDefinition.Unreal && "Patch" != tableDefinition.Name) |
@@ -74,40 +74,40 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
74 | } | 74 | } |
75 | } | 75 | } |
76 | 76 | ||
77 | Hashtable addedRows = new Hashtable(); | 77 | var addedRows = new Dictionary<string, Row>(); |
78 | Table transformViewTable; | 78 | Table transformViewTable; |
79 | 79 | ||
80 | // Bind the schema msi. | 80 | // Bind the schema msi. |
81 | this.GenerateDatabase(schemaOutput, msiDatabaseFile); | 81 | this.GenerateDatabase(schemaOutput, msiDatabaseFile); |
82 | 82 | ||
83 | // apply the transform to the database and retrieve the modifications | 83 | // apply the transform to the database and retrieve the modifications |
84 | using (Database msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact)) | 84 | using (var msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact)) |
85 | { | 85 | { |
86 | // apply the transform with the ViewTransform option to collect all the modifications | 86 | // apply the transform with the ViewTransform option to collect all the modifications |
87 | msiDatabase.ApplyTransform(this.TransformFile, TransformErrorConditions.All | TransformErrorConditions.ViewTransform); | 87 | msiDatabase.ApplyTransform(this.TransformFile, TransformErrorConditions.All | TransformErrorConditions.ViewTransform); |
88 | 88 | ||
89 | // unbind the database | 89 | // unbind the database |
90 | var unbindCommand = new UnbindDatabaseCommand(this.Messaging, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true); | 90 | var unbindCommand = new UnbindDatabaseCommand(this.Messaging, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true); |
91 | WindowsInstallerData transformViewOutput = unbindCommand.Execute(); | 91 | var transformViewOutput = unbindCommand.Execute(); |
92 | 92 | ||
93 | // index the added and possibly modified rows (added rows may also appears as modified rows) | 93 | // index the added and possibly modified rows (added rows may also appears as modified rows) |
94 | transformViewTable = transformViewOutput.Tables["_TransformView"]; | 94 | transformViewTable = transformViewOutput.Tables["_TransformView"]; |
95 | Hashtable modifiedRows = new Hashtable(); | 95 | var modifiedRows = new Hashtable(); |
96 | foreach (Row row in transformViewTable.Rows) | 96 | foreach (var row in transformViewTable.Rows) |
97 | { | 97 | { |
98 | string tableName = (string)row[0]; | 98 | var tableName = (string)row[0]; |
99 | string columnName = (string)row[1]; | 99 | var columnName = (string)row[1]; |
100 | string primaryKeys = (string)row[2]; | 100 | var primaryKeys = (string)row[2]; |
101 | 101 | ||
102 | if ("INSERT" == columnName) | 102 | if ("INSERT" == columnName) |
103 | { | 103 | { |
104 | string index = String.Concat(tableName, ':', primaryKeys); | 104 | var index = String.Concat(tableName, ':', primaryKeys); |
105 | 105 | ||
106 | addedRows.Add(index, null); | 106 | addedRows.Add(index, null); |
107 | } | 107 | } |
108 | else if ("CREATE" != columnName && "DELETE" != columnName && "DROP" != columnName && null != primaryKeys) // modified row | 108 | else if ("CREATE" != columnName && "DELETE" != columnName && "DROP" != columnName && null != primaryKeys) // modified row |
109 | { | 109 | { |
110 | string index = String.Concat(tableName, ':', primaryKeys); | 110 | var index = String.Concat(tableName, ':', primaryKeys); |
111 | 111 | ||
112 | modifiedRows[index] = row; | 112 | modifiedRows[index] = row; |
113 | } | 113 | } |
@@ -116,16 +116,16 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
116 | // create placeholder rows for modified rows to make the transform insert the updated values when its applied | 116 | // create placeholder rows for modified rows to make the transform insert the updated values when its applied |
117 | foreach (Row row in modifiedRows.Values) | 117 | foreach (Row row in modifiedRows.Values) |
118 | { | 118 | { |
119 | string tableName = (string)row[0]; | 119 | var tableName = (string)row[0]; |
120 | string columnName = (string)row[1]; | 120 | var columnName = (string)row[1]; |
121 | string primaryKeys = (string)row[2]; | 121 | var primaryKeys = (string)row[2]; |
122 | 122 | ||
123 | string index = String.Concat(tableName, ':', primaryKeys); | 123 | var index = String.Concat(tableName, ':', primaryKeys); |
124 | 124 | ||
125 | // ignore information for added rows | 125 | // ignore information for added rows |
126 | if (!addedRows.Contains(index)) | 126 | if (!addedRows.ContainsKey(index)) |
127 | { | 127 | { |
128 | Table table = schemaOutput.Tables[tableName]; | 128 | var table = schemaOutput.Tables[tableName]; |
129 | this.CreateRow(table, primaryKeys, true); | 129 | this.CreateRow(table, primaryKeys, true); |
130 | } | 130 | } |
131 | } | 131 | } |
@@ -135,7 +135,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
135 | this.GenerateDatabase(schemaOutput, msiDatabaseFile); | 135 | this.GenerateDatabase(schemaOutput, msiDatabaseFile); |
136 | 136 | ||
137 | // apply the transform to the database and retrieve the modifications | 137 | // apply the transform to the database and retrieve the modifications |
138 | using (Database msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact)) | 138 | using (var msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact)) |
139 | { | 139 | { |
140 | try | 140 | try |
141 | { | 141 | { |
@@ -158,26 +158,26 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
158 | 158 | ||
159 | // unbind the database | 159 | // unbind the database |
160 | var unbindCommand = new UnbindDatabaseCommand(this.Messaging, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true); | 160 | var unbindCommand = new UnbindDatabaseCommand(this.Messaging, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true); |
161 | WindowsInstallerData output = unbindCommand.Execute(); | 161 | var output = unbindCommand.Execute(); |
162 | 162 | ||
163 | // index all the rows to easily find modified rows | 163 | // index all the rows to easily find modified rows |
164 | Hashtable rows = new Hashtable(); | 164 | var rows = new Dictionary<string, Row>(); |
165 | foreach (Table table in output.Tables) | 165 | foreach (var table in output.Tables) |
166 | { | 166 | { |
167 | foreach (Row row in table.Rows) | 167 | foreach (var row in table.Rows) |
168 | { | 168 | { |
169 | rows.Add(String.Concat(table.Name, ':', row.GetPrimaryKey('\t', " ")), row); | 169 | rows.Add(String.Concat(table.Name, ':', row.GetPrimaryKey('\t', " ")), row); |
170 | } | 170 | } |
171 | } | 171 | } |
172 | 172 | ||
173 | // process the _TransformView rows into transform rows | 173 | // process the _TransformView rows into transform rows |
174 | foreach (Row row in transformViewTable.Rows) | 174 | foreach (var row in transformViewTable.Rows) |
175 | { | 175 | { |
176 | string tableName = (string)row[0]; | 176 | var tableName = (string)row[0]; |
177 | string columnName = (string)row[1]; | 177 | var columnName = (string)row[1]; |
178 | string primaryKeys = (string)row[2]; | 178 | var primaryKeys = (string)row[2]; |
179 | 179 | ||
180 | Table table = transform.EnsureTable(this.TableDefinitions[tableName]); | 180 | var table = transform.EnsureTable(this.TableDefinitions[tableName]); |
181 | 181 | ||
182 | if ("CREATE" == columnName) // added table | 182 | if ("CREATE" == columnName) // added table |
183 | { | 183 | { |
@@ -185,7 +185,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
185 | } | 185 | } |
186 | else if ("DELETE" == columnName) // deleted row | 186 | else if ("DELETE" == columnName) // deleted row |
187 | { | 187 | { |
188 | Row deletedRow = this.CreateRow(table, primaryKeys, false); | 188 | var deletedRow = this.CreateRow(table, primaryKeys, false); |
189 | deletedRow.Operation = RowOperation.Delete; | 189 | deletedRow.Operation = RowOperation.Delete; |
190 | } | 190 | } |
191 | else if ("DROP" == columnName) // dropped table | 191 | else if ("DROP" == columnName) // dropped table |
@@ -194,24 +194,24 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
194 | } | 194 | } |
195 | else if ("INSERT" == columnName) // added row | 195 | else if ("INSERT" == columnName) // added row |
196 | { | 196 | { |
197 | string index = String.Concat(tableName, ':', primaryKeys); | 197 | var index = String.Concat(tableName, ':', primaryKeys); |
198 | Row addedRow = (Row)rows[index]; | 198 | var addedRow = rows[index]; |
199 | addedRow.Operation = RowOperation.Add; | 199 | addedRow.Operation = RowOperation.Add; |
200 | table.Rows.Add(addedRow); | 200 | table.Rows.Add(addedRow); |
201 | } | 201 | } |
202 | else if (null != primaryKeys) // modified row | 202 | else if (null != primaryKeys) // modified row |
203 | { | 203 | { |
204 | string index = String.Concat(tableName, ':', primaryKeys); | 204 | var index = String.Concat(tableName, ':', primaryKeys); |
205 | 205 | ||
206 | // the _TransformView table includes information for added rows | 206 | // the _TransformView table includes information for added rows |
207 | // that looks like modified rows so it sometimes needs to be ignored | 207 | // that looks like modified rows so it sometimes needs to be ignored |
208 | if (!addedRows.Contains(index)) | 208 | if (!addedRows.ContainsKey(index)) |
209 | { | 209 | { |
210 | Row modifiedRow = (Row)rows[index]; | 210 | var modifiedRow = rows[index]; |
211 | 211 | ||
212 | // mark the field as modified | 212 | // mark the field as modified |
213 | int indexOfModifiedValue = -1; | 213 | var indexOfModifiedValue = -1; |
214 | for (int i = 0; i < modifiedRow.TableDefinition.Columns.Length; ++i) | 214 | for (var i = 0; i < modifiedRow.TableDefinition.Columns.Length; ++i) |
215 | { | 215 | { |
216 | if (columnName.Equals(modifiedRow.TableDefinition.Columns[i].Name, StringComparison.Ordinal)) | 216 | if (columnName.Equals(modifiedRow.TableDefinition.Columns[i].Name, StringComparison.Ordinal)) |
217 | { | 217 | { |
@@ -231,7 +231,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
231 | } | 231 | } |
232 | else // added column | 232 | else // added column |
233 | { | 233 | { |
234 | ColumnDefinition column = table.Definition.Columns.Single(c => c.Name.Equals(columnName, StringComparison.Ordinal)); | 234 | var column = table.Definition.Columns.Single(c => c.Name.Equals(columnName, StringComparison.Ordinal)); |
235 | column.Added = true; | 235 | column.Added = true; |
236 | } | 236 | } |
237 | } | 237 | } |
@@ -240,21 +240,6 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
240 | return transform; | 240 | return transform; |
241 | } | 241 | } |
242 | 242 | ||
243 | private void GenerateDatabase(WindowsInstallerData output, string databaseFile) | ||
244 | { | ||
245 | var command = new GenerateDatabaseCommand(); | ||
246 | command.Extensions = Array.Empty<IFileSystemExtension>(); | ||
247 | command.Output = output; | ||
248 | command.OutputPath = databaseFile; | ||
249 | command.KeepAddedColumns = true; | ||
250 | command.UseSubDirectory = false; | ||
251 | command.SuppressAddingValidationRows = true; | ||
252 | command.TableDefinitions = this.TableDefinitions; | ||
253 | command.IntermediateFolder = this.IntermediateFolder; | ||
254 | command.Codepage = -1; | ||
255 | command.Execute(); | ||
256 | } | ||
257 | |||
258 | /// <summary> | 243 | /// <summary> |
259 | /// Create a deleted or modified row. | 244 | /// Create a deleted or modified row. |
260 | /// </summary> | 245 | /// </summary> |
@@ -264,14 +249,14 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
264 | /// <returns>The new row.</returns> | 249 | /// <returns>The new row.</returns> |
265 | private Row CreateRow(Table table, string primaryKeys, bool setRequiredFields) | 250 | private Row CreateRow(Table table, string primaryKeys, bool setRequiredFields) |
266 | { | 251 | { |
267 | Row row = table.CreateRow(null); | 252 | var row = table.CreateRow(null); |
268 | 253 | ||
269 | string[] primaryKeyParts = primaryKeys.Split('\t'); | 254 | var primaryKeyParts = primaryKeys.Split('\t'); |
270 | int primaryKeyPartIndex = 0; | 255 | var primaryKeyPartIndex = 0; |
271 | 256 | ||
272 | for (int i = 0; i < table.Definition.Columns.Length; i++) | 257 | for (var i = 0; i < table.Definition.Columns.Length; i++) |
273 | { | 258 | { |
274 | ColumnDefinition columnDefinition = table.Definition.Columns[i]; | 259 | var columnDefinition = table.Definition.Columns[i]; |
275 | 260 | ||
276 | if (columnDefinition.PrimaryKey) | 261 | if (columnDefinition.PrimaryKey) |
277 | { | 262 | { |
@@ -294,8 +279,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
294 | { | 279 | { |
295 | if (null == this.EmptyFile) | 280 | if (null == this.EmptyFile) |
296 | { | 281 | { |
297 | this.EmptyFile = Path.GetTempFileName() + ".empty"; | 282 | this.EmptyFile = Path.Combine(this.IntermediateFolder, ".empty"); |
298 | using (FileStream fileStream = File.Create(this.EmptyFile)) | 283 | using (var fileStream = File.Create(this.EmptyFile)) |
299 | { | 284 | { |
300 | } | 285 | } |
301 | } | 286 | } |
@@ -311,5 +296,11 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
311 | 296 | ||
312 | return row; | 297 | return row; |
313 | } | 298 | } |
299 | |||
300 | private void GenerateDatabase(WindowsInstallerData output, string databaseFile) | ||
301 | { | ||
302 | var command = new GenerateDatabaseCommand(this.Messaging, null, null, output, databaseFile, this.TableDefinitions, this.IntermediateFolder, codepage: -1, keepAddedColumns: true, suppressAddingValidationRows: true, useSubdirectory: false); | ||
303 | command.Execute(); | ||
304 | } | ||
314 | } | 305 | } |
315 | } | 306 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs b/src/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs index 173404d7..f9cf4492 100644 --- a/src/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs +++ b/src/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs | |||
@@ -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 | 2 | ||
3 | namespace WixToolset.Core.WindowsInstaller | 3 | namespace WixToolset.Core.WindowsInstaller |
4 | { | 4 | { |
5 | using System; | 5 | using System; |
6 | using System.IO; | 6 | using System.IO; |
7 | using WixToolset.Extensibility; | 7 | using WixToolset.Extensibility; |
8 | using WixToolset.Extensibility.Data; | ||
9 | 8 | ||
10 | internal class WindowsInstallerBackendFactory : IBackendFactory | 9 | internal class WindowsInstallerBackendFactory : IBackendFactory |
11 | { | 10 | { |
@@ -16,7 +15,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
16 | outputType = Path.GetExtension(outputFile); | 15 | outputType = Path.GetExtension(outputFile); |
17 | } | 16 | } |
18 | 17 | ||
19 | switch (outputType.ToLowerInvariant()) | 18 | switch (outputType?.ToLowerInvariant()) |
20 | { | 19 | { |
21 | case "module": | 20 | case "module": |
22 | case ".msm": | 21 | case ".msm": |
diff --git a/src/WixToolset.Core/Bind/FileFacade.cs b/src/WixToolset.Core/Bind/FileFacade.cs index d631a3b5..7bfdb9bb 100644 --- a/src/WixToolset.Core/Bind/FileFacade.cs +++ b/src/WixToolset.Core/Bind/FileFacade.cs | |||
@@ -2,32 +2,146 @@ | |||
2 | 2 | ||
3 | namespace WixToolset.Core.Bind | 3 | namespace WixToolset.Core.Bind |
4 | { | 4 | { |
5 | using System; | ||
5 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
7 | using WixToolset.Data; | ||
6 | using WixToolset.Data.Tuples; | 8 | using WixToolset.Data.Tuples; |
9 | using WixToolset.Data.WindowsInstaller; | ||
10 | using WixToolset.Data.WindowsInstaller.Rows; | ||
7 | 11 | ||
8 | public class FileFacade | 12 | public class FileFacade |
9 | { | 13 | { |
10 | public FileFacade(FileTuple file, AssemblyTuple assembly) | 14 | public FileFacade(FileTuple file, AssemblyTuple assembly) |
11 | { | 15 | { |
12 | this.File = file; | 16 | this.FileTuple = file; |
13 | this.Assembly = assembly; | 17 | this.AssemblyTuple = assembly; |
14 | } | 18 | } |
15 | 19 | ||
16 | public FileFacade(bool fromModule, FileTuple file) | 20 | public FileFacade(bool fromModule, FileTuple file) |
17 | { | 21 | { |
18 | this.FromModule = fromModule; | 22 | this.FromModule = fromModule; |
19 | this.File = file; | 23 | this.FileTuple = file; |
24 | } | ||
25 | |||
26 | internal FileFacade(FileRow row) | ||
27 | { | ||
28 | this.FromTransform = true; | ||
29 | this.FileRow = row; | ||
20 | } | 30 | } |
21 | 31 | ||
22 | public bool FromModule { get; } | 32 | public bool FromModule { get; } |
23 | 33 | ||
24 | public FileTuple File { get; } | 34 | public bool FromTransform { get; } |
35 | |||
36 | private FileRow FileRow { get; } | ||
37 | |||
38 | private FileTuple FileTuple { get; } | ||
39 | |||
40 | private AssemblyTuple AssemblyTuple { get; } | ||
41 | |||
42 | public string Id => this.FileRow == null ? this.FileTuple.Id.Id : this.FileRow.File; | ||
43 | |||
44 | public Identifier Identifier => this.FileRow == null ? this.FileTuple.Id : throw new NotImplementedException(); | ||
45 | |||
46 | public string ComponentRef => this.FileRow == null ? this.FileTuple.ComponentRef : this.FileRow.Component; | ||
47 | |||
48 | public int DiskId | ||
49 | { | ||
50 | get => this.FileRow == null ? this.FileTuple.DiskId ?? 0 : this.FileRow.DiskId; | ||
51 | set | ||
52 | { | ||
53 | if (this.FileRow == null) | ||
54 | { | ||
55 | this.FileTuple.DiskId = value; | ||
56 | } | ||
57 | else | ||
58 | { | ||
59 | this.FileRow.DiskId = value; | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | |||
64 | public string FileName => this.FileRow == null ? this.FileTuple.Name : this.FileRow.FileName; | ||
65 | |||
66 | public int FileSize | ||
67 | { | ||
68 | get => this.FileRow == null ? this.FileTuple.FileSize : this.FileRow.FileSize; | ||
69 | set | ||
70 | { | ||
71 | if (this.FileRow == null) | ||
72 | { | ||
73 | this.FileTuple.FileSize = value; | ||
74 | } | ||
75 | else | ||
76 | { | ||
77 | this.FileRow.FileSize = value; | ||
78 | } | ||
79 | } | ||
80 | } | ||
81 | |||
82 | public string Language | ||
83 | { | ||
84 | get => this.FileRow == null ? this.FileTuple.Language : this.FileRow.Language; | ||
85 | set | ||
86 | { | ||
87 | if (this.FileRow == null) | ||
88 | { | ||
89 | this.FileTuple.Language = value; | ||
90 | } | ||
91 | else | ||
92 | { | ||
93 | this.FileRow.Language = value; | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | |||
98 | public int? PatchGroup => this.FileRow == null ? this.FileTuple.PatchGroup : null; | ||
99 | |||
100 | public int Sequence | ||
101 | { | ||
102 | get => this.FileRow == null ? this.FileTuple.Sequence : this.FileRow.Sequence; | ||
103 | set | ||
104 | { | ||
105 | if (this.FileRow == null) | ||
106 | { | ||
107 | this.FileTuple.Sequence = value; | ||
108 | } | ||
109 | else | ||
110 | { | ||
111 | this.FileRow.Sequence = value; | ||
112 | } | ||
113 | } | ||
114 | } | ||
115 | |||
116 | public SourceLineNumber SourceLineNumber => this.FileRow == null ? this.FileTuple.SourceLineNumbers : this.FileRow.SourceLineNumbers; | ||
117 | |||
118 | public string SourcePath => this.FileRow == null ? this.FileTuple.Source.Path : this.FileRow.Source; | ||
119 | |||
120 | public bool Compressed => this.FileRow == null ? (this.FileTuple.Attributes & FileTupleAttributes.Compressed) == FileTupleAttributes.Compressed : (this.FileRow.Attributes & WindowsInstallerConstants.MsidbFileAttributesCompressed) == WindowsInstallerConstants.MsidbFileAttributesCompressed; | ||
121 | |||
122 | public bool Uncompressed => this.FileRow == null ? (this.FileTuple.Attributes & FileTupleAttributes.Uncompressed) == FileTupleAttributes.Uncompressed : (this.FileRow.Attributes & WindowsInstallerConstants.MsidbFileAttributesNoncompressed) == WindowsInstallerConstants.MsidbFileAttributesNoncompressed; | ||
123 | |||
124 | public string Version | ||
125 | { | ||
126 | get => this.FileRow == null ? this.FileTuple.Version : this.FileRow.Version; | ||
127 | set | ||
128 | { | ||
129 | if (this.FileRow == null) | ||
130 | { | ||
131 | this.FileTuple.Version = value; | ||
132 | } | ||
133 | else | ||
134 | { | ||
135 | this.FileRow.Version = value; | ||
136 | } | ||
137 | } | ||
138 | } | ||
25 | 139 | ||
26 | public AssemblyTuple Assembly { get; } | 140 | public AssemblyType? AssemblyType => this.FileRow == null ? this.AssemblyTuple?.Type : throw new NotImplementedException(); |
27 | 141 | ||
28 | public int DiskId => this.File.DiskId ?? 0; | 142 | public string AssemblyApplicationFileRef => this.FileRow == null ? this.AssemblyTuple?.ApplicationFileRef : throw new NotImplementedException(); |
29 | 143 | ||
30 | public bool Uncompressed => (this.File.Attributes & FileTupleAttributes.Uncompressed) == FileTupleAttributes.Uncompressed; | 144 | public string AssemblyManifestFileRef => this.FileRow == null ? this.AssemblyTuple?.ManifestFileRef : throw new NotImplementedException(); |
31 | 145 | ||
32 | /// <summary> | 146 | /// <summary> |
33 | /// Gets the set of MsiAssemblyName rows created for this file. | 147 | /// Gets the set of MsiAssemblyName rows created for this file. |
diff --git a/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs b/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs index 19a26915..5cb2524d 100644 --- a/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs +++ b/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs | |||
@@ -89,7 +89,7 @@ namespace WixToolset.Core.Bind | |||
89 | { | 89 | { |
90 | var objectField = field.AsPath(); | 90 | var objectField = field.AsPath(); |
91 | 91 | ||
92 | #if REVISIT_FOR_PATCHING | 92 | #if TODO_PATCHING |
93 | // Skip file resolution if the file is to be deleted. | 93 | // Skip file resolution if the file is to be deleted. |
94 | if (RowOperation.Delete == tuple.Operation) | 94 | if (RowOperation.Delete == tuple.Operation) |
95 | { | 95 | { |
@@ -111,7 +111,7 @@ namespace WixToolset.Core.Bind | |||
111 | { | 111 | { |
112 | if (!this.BuildingPatch) // Normal binding for non-Patch scenario such as link (light.exe) | 112 | if (!this.BuildingPatch) // Normal binding for non-Patch scenario such as link (light.exe) |
113 | { | 113 | { |
114 | #if REVISIT_FOR_PATCHING | 114 | #if TODO_PATCHING |
115 | // keep a copy of the un-resolved data for future replay. This will be saved into wixpdb file | 115 | // keep a copy of the un-resolved data for future replay. This will be saved into wixpdb file |
116 | if (null == objectField.UnresolvedData) | 116 | if (null == objectField.UnresolvedData) |
117 | { | 117 | { |
@@ -129,7 +129,7 @@ namespace WixToolset.Core.Bind | |||
129 | var value = fileResolver.ResolveFile(objectField.Path, tuple.Definition, tuple.SourceLineNumbers, BindStage.Normal); | 129 | var value = fileResolver.ResolveFile(objectField.Path, tuple.Definition, tuple.SourceLineNumbers, BindStage.Normal); |
130 | field.Set(value); | 130 | field.Set(value); |
131 | } | 131 | } |
132 | #if REVISIT_FOR_PATCHING | 132 | #if TODO_PATCHING |
133 | else // Re-base binding path scenario caused by pyro.exe -bt -bu | 133 | else // Re-base binding path scenario caused by pyro.exe -bt -bu |
134 | { | 134 | { |
135 | // by default, use the resolved Data for file lookup | 135 | // by default, use the resolved Data for file lookup |
@@ -158,7 +158,7 @@ namespace WixToolset.Core.Bind | |||
158 | } | 158 | } |
159 | } | 159 | } |
160 | 160 | ||
161 | #if REVISIT_FOR_PATCHING | 161 | #if TODO_PATCHING |
162 | if (null != objectField.PreviousData) | 162 | if (null != objectField.PreviousData) |
163 | { | 163 | { |
164 | objectField.PreviousData = this.BindVariableResolver.ResolveVariables(tuple.SourceLineNumbers, objectField.PreviousData, false, out isDefault); | 164 | objectField.PreviousData = this.BindVariableResolver.ResolveVariables(tuple.SourceLineNumbers, objectField.PreviousData, false, out isDefault); |
diff --git a/src/WixToolset.Core/Compiler.cs b/src/WixToolset.Core/Compiler.cs index 0c3d4c9c..cee64911 100644 --- a/src/WixToolset.Core/Compiler.cs +++ b/src/WixToolset.Core/Compiler.cs | |||
@@ -7878,7 +7878,7 @@ namespace WixToolset.Core | |||
7878 | if (patch) | 7878 | if (patch) |
7879 | { | 7879 | { |
7880 | // /Patch/PatchProperty goes directly into MsiPatchMetadata table | 7880 | // /Patch/PatchProperty goes directly into MsiPatchMetadata table |
7881 | this.Core.AddTuple(new MsiPatchMetadataTuple(sourceLineNumbers) | 7881 | this.Core.AddTuple(new MsiPatchMetadataTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, company, name)) |
7882 | { | 7882 | { |
7883 | Company = company, | 7883 | Company = company, |
7884 | Property = name, | 7884 | Property = name, |
@@ -8130,12 +8130,15 @@ namespace WixToolset.Core | |||
8130 | /// </summary> | 8130 | /// </summary> |
8131 | /// <param name="node">The element to parse.</param> | 8131 | /// <param name="node">The element to parse.</param> |
8132 | /// <param name="diskId">Media index from parent element.</param> | 8132 | /// <param name="diskId">Media index from parent element.</param> |
8133 | private void ParsePatchBaselineElement(XElement node, int diskId) | 8133 | private void ParsePatchBaselineElement(XElement node, int? diskId) |
8134 | { | 8134 | { |
8135 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | 8135 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); |
8136 | Identifier id = null; | 8136 | Identifier id = null; |
8137 | var parsedValidate = false; | 8137 | var parsedValidate = false; |
8138 | var validationFlags = TransformFlags.PatchTransformDefault; | 8138 | var validationFlags = TransformFlags.PatchTransformDefault; |
8139 | string baselineFile = null; | ||
8140 | string updateFile = null; | ||
8141 | string transformFile = null; | ||
8139 | 8142 | ||
8140 | foreach (var attrib in node.Attributes()) | 8143 | foreach (var attrib in node.Attributes()) |
8141 | { | 8144 | { |
@@ -8146,6 +8149,18 @@ namespace WixToolset.Core | |||
8146 | case "Id": | 8149 | case "Id": |
8147 | id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); | 8150 | id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); |
8148 | break; | 8151 | break; |
8152 | case "DiskId": | ||
8153 | diskId = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int16.MaxValue); | ||
8154 | break; | ||
8155 | case "BaselineFile": | ||
8156 | baselineFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
8157 | break; | ||
8158 | case "UpdateFile": | ||
8159 | updateFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
8160 | break; | ||
8161 | case "TransformFile": | ||
8162 | transformFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); | ||
8163 | break; | ||
8149 | default: | 8164 | default: |
8150 | this.Core.UnexpectedAttribute(node, attrib); | 8165 | this.Core.UnexpectedAttribute(node, attrib); |
8151 | break; | 8166 | break; |
@@ -8167,6 +8182,28 @@ namespace WixToolset.Core | |||
8167 | this.Core.Write(ErrorMessages.IdentifierTooLongError(sourceLineNumbers, node.Name.LocalName, "Id", id.Id, 27)); | 8182 | this.Core.Write(ErrorMessages.IdentifierTooLongError(sourceLineNumbers, node.Name.LocalName, "Id", id.Id, 27)); |
8168 | } | 8183 | } |
8169 | 8184 | ||
8185 | if (!String.IsNullOrEmpty(baselineFile) || !String.IsNullOrEmpty(updateFile)) | ||
8186 | { | ||
8187 | if (String.IsNullOrEmpty(baselineFile)) | ||
8188 | { | ||
8189 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "BaselineFile", "UpdateFile")); | ||
8190 | } | ||
8191 | |||
8192 | if (String.IsNullOrEmpty(updateFile)) | ||
8193 | { | ||
8194 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "UpdateFile", "BaselineFile")); | ||
8195 | } | ||
8196 | |||
8197 | if (!String.IsNullOrEmpty(transformFile)) | ||
8198 | { | ||
8199 | this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "TransformFile", !String.IsNullOrEmpty(baselineFile) ? "BaselineFile" : "UpdateFile")); | ||
8200 | } | ||
8201 | } | ||
8202 | else if (String.IsNullOrEmpty(transformFile)) | ||
8203 | { | ||
8204 | this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "BaselineFile", "TransformFile", true)); | ||
8205 | } | ||
8206 | |||
8170 | foreach (var child in node.Elements()) | 8207 | foreach (var child in node.Elements()) |
8171 | { | 8208 | { |
8172 | if (CompilerCore.WixNamespace == child.Name.Namespace) | 8209 | if (CompilerCore.WixNamespace == child.Name.Namespace) |
@@ -8200,8 +8237,11 @@ namespace WixToolset.Core | |||
8200 | { | 8237 | { |
8201 | var tuple = new WixPatchBaselineTuple(sourceLineNumbers, id) | 8238 | var tuple = new WixPatchBaselineTuple(sourceLineNumbers, id) |
8202 | { | 8239 | { |
8203 | DiskId = diskId, | 8240 | DiskId = diskId ?? 1, |
8204 | ValidationFlags = validationFlags | 8241 | ValidationFlags = validationFlags, |
8242 | BaselineFile = new IntermediateFieldPathValue { Path = baselineFile }, | ||
8243 | UpdateFile = new IntermediateFieldPathValue { Path = updateFile }, | ||
8244 | TransformFile = new IntermediateFieldPathValue { Path = transformFile } | ||
8205 | }; | 8245 | }; |
8206 | 8246 | ||
8207 | this.Core.AddTuple(tuple); | 8247 | this.Core.AddTuple(tuple); |
diff --git a/src/WixToolset.Core/Compiler_2.cs b/src/WixToolset.Core/Compiler_2.cs index 4b6839f1..89d4b6da 100644 --- a/src/WixToolset.Core/Compiler_2.cs +++ b/src/WixToolset.Core/Compiler_2.cs | |||
@@ -1030,12 +1030,6 @@ namespace WixToolset.Core | |||
1030 | 1030 | ||
1031 | this.Core.AddTuple(new SummaryInformationTuple(sourceLineNumbers) | 1031 | this.Core.AddTuple(new SummaryInformationTuple(sourceLineNumbers) |
1032 | { | 1032 | { |
1033 | PropertyId = SumaryInformationType.WindowsInstallerVersion, | ||
1034 | Value = msiVersion.ToString(CultureInfo.InvariantCulture) | ||
1035 | }); | ||
1036 | |||
1037 | this.Core.AddTuple(new SummaryInformationTuple(sourceLineNumbers) | ||
1038 | { | ||
1039 | PropertyId = SumaryInformationType.Security, | 1033 | PropertyId = SumaryInformationType.Security, |
1040 | Value = YesNoDefaultType.No == security ? "0" : YesNoDefaultType.Yes == security ? "4" : "2" | 1034 | Value = YesNoDefaultType.No == security ? "0" : YesNoDefaultType.Yes == security ? "4" : "2" |
1041 | }); | 1035 | }); |
diff --git a/src/WixToolset.Core/Compiler_Patch.cs b/src/WixToolset.Core/Compiler_Patch.cs index 42951543..f8d05132 100644 --- a/src/WixToolset.Core/Compiler_Patch.cs +++ b/src/WixToolset.Core/Compiler_Patch.cs | |||
@@ -197,9 +197,8 @@ namespace WixToolset.Core | |||
197 | 197 | ||
198 | if (!this.Core.EncounteredError) | 198 | if (!this.Core.EncounteredError) |
199 | { | 199 | { |
200 | var tuple = new WixPatchIdTuple(sourceLineNumbers) | 200 | var tuple = new WixPatchIdTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, patchId)) |
201 | { | 201 | { |
202 | ProductCode = patchId, | ||
203 | ClientPatchId = clientPatchId, | 202 | ClientPatchId = clientPatchId, |
204 | OptimizePatchSizeForLargeFiles = optimizePatchSizeForLargeFiles, | 203 | OptimizePatchSizeForLargeFiles = optimizePatchSizeForLargeFiles, |
205 | ApiPatchingSymbolFlags = apiPatchingSymbolFlags | 204 | ApiPatchingSymbolFlags = apiPatchingSymbolFlags |
diff --git a/src/WixToolset.Core/Compiler_UI.cs b/src/WixToolset.Core/Compiler_UI.cs index 30bb7ab6..8a425fd4 100644 --- a/src/WixToolset.Core/Compiler_UI.cs +++ b/src/WixToolset.Core/Compiler_UI.cs | |||
@@ -470,7 +470,7 @@ namespace WixToolset.Core | |||
470 | string defaultControl = null; | 470 | string defaultControl = null; |
471 | string cancelControl = null; | 471 | string cancelControl = null; |
472 | 472 | ||
473 | this.ParseControlElement(child, id.Id, TupleDefinitionType.BBControl, ref lastTabRow, ref firstControl, ref defaultControl, ref cancelControl, false); | 473 | this.ParseControlElement(child, id.Id, TupleDefinitionType.BBControl, ref lastTabRow, ref firstControl, ref defaultControl, ref cancelControl); |
474 | break; | 474 | break; |
475 | default: | 475 | default: |
476 | this.Core.UnexpectedElement(node, child); | 476 | this.Core.UnexpectedElement(node, child); |
@@ -966,7 +966,7 @@ namespace WixToolset.Core | |||
966 | switch (child.Name.LocalName) | 966 | switch (child.Name.LocalName) |
967 | { | 967 | { |
968 | case "Control": | 968 | case "Control": |
969 | this.ParseControlElement(child, id.Id, TupleDefinitionType.Control, ref lastTabRow, ref firstControl, ref defaultControl, ref cancelControl, trackDiskSpace); | 969 | this.ParseControlElement(child, id.Id, TupleDefinitionType.Control, ref lastTabRow, ref firstControl, ref defaultControl, ref cancelControl); |
970 | break; | 970 | break; |
971 | default: | 971 | default: |
972 | this.Core.UnexpectedElement(node, child); | 972 | this.Core.UnexpectedElement(node, child); |
@@ -1032,7 +1032,7 @@ namespace WixToolset.Core | |||
1032 | /// <param name="defaultControl">Name of the default control.</param> | 1032 | /// <param name="defaultControl">Name of the default control.</param> |
1033 | /// <param name="cancelControl">Name of the candle control.</param> | 1033 | /// <param name="cancelControl">Name of the candle control.</param> |
1034 | /// <param name="trackDiskSpace">True if the containing dialog tracks disk space.</param> | 1034 | /// <param name="trackDiskSpace">True if the containing dialog tracks disk space.</param> |
1035 | private void ParseControlElement(XElement node, string dialog, TupleDefinitionType tupleType, ref IntermediateTuple lastTabTuple, ref string firstControl, ref string defaultControl, ref string cancelControl, bool trackDiskSpace) | 1035 | private void ParseControlElement(XElement node, string dialog, TupleDefinitionType tupleType, ref IntermediateTuple lastTabTuple, ref string firstControl, ref string defaultControl, ref string cancelControl) |
1036 | { | 1036 | { |
1037 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | 1037 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); |
1038 | Identifier controlId = null; | 1038 | Identifier controlId = null; |
diff --git a/src/WixToolset.Core/Resolver.cs b/src/WixToolset.Core/Resolver.cs index cbae3742..54bde848 100644 --- a/src/WixToolset.Core/Resolver.cs +++ b/src/WixToolset.Core/Resolver.cs | |||
@@ -92,7 +92,7 @@ namespace WixToolset.Core | |||
92 | delayedFields = command.DelayedFields; | 92 | delayedFields = command.DelayedFields; |
93 | } | 93 | } |
94 | 94 | ||
95 | #if REVISIT_FOR_PATCHING | 95 | #if TODO_PATCHING |
96 | if (context.IntermediateRepresentation.SubStorages != null) | 96 | if (context.IntermediateRepresentation.SubStorages != null) |
97 | { | 97 | { |
98 | foreach (SubStorage transform in context.IntermediateRepresentation.SubStorages) | 98 | foreach (SubStorage transform in context.IntermediateRepresentation.SubStorages) |
diff --git a/src/test/WixToolsetTest.CoreIntegration/PatchFixture.cs b/src/test/WixToolsetTest.CoreIntegration/PatchFixture.cs new file mode 100644 index 00000000..584f86fe --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/PatchFixture.cs | |||
@@ -0,0 +1,112 @@ | |||
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.ComponentModel; | ||
6 | using System.IO; | ||
7 | using System.Linq; | ||
8 | using System.Runtime.InteropServices; | ||
9 | using System.Text; | ||
10 | using System.Xml.Linq; | ||
11 | using WixBuildTools.TestSupport; | ||
12 | using WixToolset.Core.TestPackage; | ||
13 | using Xunit; | ||
14 | |||
15 | public class PatchFixture | ||
16 | { | ||
17 | private static readonly XNamespace PatchNamespace = "http://www.microsoft.com/msi/patch_applicability.xsd"; | ||
18 | |||
19 | [Fact(Skip = "Skip until patches have files in them")] | ||
20 | public void CanBuildSimplePatch() | ||
21 | { | ||
22 | var folder = TestData.Get(@"TestData\PatchSingle"); | ||
23 | |||
24 | using (var fs = new DisposableFileSystem()) | ||
25 | { | ||
26 | var tempFolder = fs.GetFolder(); | ||
27 | |||
28 | var baselinePdb = BuildMsi("Baseline.msi", folder, tempFolder, "1.0.0", "1.0.0", "1.0.0"); | ||
29 | var update1Pdb = BuildMsi("Update.msi", folder, tempFolder, "1.0.1", "1.0.1", "1.0.1"); | ||
30 | var patchPdb = BuildMsp("Patch1.msp", folder, tempFolder, "1.0.1"); | ||
31 | var baselinePath = Path.ChangeExtension(baselinePdb, ".msp"); | ||
32 | var patchPath = Path.ChangeExtension(patchPdb, ".msp"); | ||
33 | |||
34 | Assert.True(File.Exists(baselinePdb)); | ||
35 | Assert.True(File.Exists(update1Pdb)); | ||
36 | |||
37 | var doc = GetExtractPatchXml(patchPath); | ||
38 | Assert.Equal("{7D326855-E790-4A94-8611-5351F8321FCA}", doc.Root.Element(PatchNamespace + "TargetProductCode").Value); | ||
39 | |||
40 | var names = Query.GetSubStorageNames(patchPath); | ||
41 | Assert.Equal(new[] { "#RTM.1", "RTM.1" }, names); | ||
42 | |||
43 | var cab = Path.Combine(tempFolder, "foo.cab"); | ||
44 | Query.ExtractStream(patchPath, "foo.cab", cab); | ||
45 | Assert.True(File.Exists(cab)); | ||
46 | |||
47 | var files = Query.GetCabinetFiles(cab); | ||
48 | Assert.Equal(new[] { "a", "b" }, files.Select(f => f.ArchiveName).ToArray()); // This test may not be quite correct, yet. | ||
49 | } | ||
50 | } | ||
51 | |||
52 | private static string BuildMsi(string outputName, string sourceFolder, string baseFolder, string defineV, string defineA, string defineB) | ||
53 | { | ||
54 | var outputPath = Path.Combine(baseFolder, Path.Combine("bin", outputName)); | ||
55 | |||
56 | var result = WixRunner.Execute(new[] | ||
57 | { | ||
58 | "build", | ||
59 | Path.Combine(sourceFolder, @"Package.wxs"), | ||
60 | "-d", "V=" + defineV, | ||
61 | "-d", "A=" + defineA, | ||
62 | "-d", "B=" + defineB, | ||
63 | "-bindpath", Path.Combine(sourceFolder, ".data"), | ||
64 | "-intermediateFolder", Path.Combine(baseFolder, "obj"), | ||
65 | "-o", outputPath | ||
66 | }); | ||
67 | |||
68 | result.AssertSuccess(); | ||
69 | |||
70 | return Path.ChangeExtension(outputPath, ".wixpdb"); | ||
71 | } | ||
72 | |||
73 | private static string BuildMsp(string outputName, string sourceFolder, string baseFolder, string defineV) | ||
74 | { | ||
75 | var outputPath = Path.Combine(baseFolder, Path.Combine("bin", outputName)); | ||
76 | |||
77 | var result = WixRunner.Execute(new[] | ||
78 | { | ||
79 | "build", | ||
80 | Path.Combine(sourceFolder, @"Patch.wxs"), | ||
81 | "-d", "V=" + defineV, | ||
82 | "-bindpath", Path.Combine(baseFolder, "bin"), | ||
83 | "-intermediateFolder", Path.Combine(baseFolder, "obj"), | ||
84 | "-o", outputPath | ||
85 | }); | ||
86 | |||
87 | result.AssertSuccess(); | ||
88 | |||
89 | return Path.ChangeExtension(outputPath, ".wixpdb"); | ||
90 | } | ||
91 | |||
92 | private static XDocument GetExtractPatchXml(string path) | ||
93 | { | ||
94 | var buffer = new StringBuilder(65535); | ||
95 | var size = buffer.Capacity; | ||
96 | |||
97 | var er = MsiExtractPatchXMLData(path, 0, buffer, ref size); | ||
98 | if (er != 0) | ||
99 | { | ||
100 | throw new Win32Exception(er); | ||
101 | } | ||
102 | |||
103 | return XDocument.Parse(buffer.ToString()); | ||
104 | } | ||
105 | |||
106 | [DllImport("msi.dll", EntryPoint = "MsiExtractPatchXMLDataW", CharSet = CharSet.Unicode, ExactSpelling = true)] | ||
107 | private static extern int MsiExtractPatchXMLData(string szPatchPath, int dwReserved, StringBuilder szXMLData, ref int pcchXMLData); | ||
108 | |||
109 | [DllImport("msi.dll", EntryPoint = "MsiApplyPatchW", CharSet = CharSet.Unicode, ExactSpelling = true)] | ||
110 | private static extern int MsiApplyPatch(string szPatchPackage, string szInstallPackage, int eInstallType, string szCommandLine); | ||
111 | } | ||
112 | } | ||
diff --git a/src/test/WixToolsetTest.CoreIntegration/PreprocessorFixture.cs b/src/test/WixToolsetTest.CoreIntegration/PreprocessorFixture.cs index 4e48cbe1..b1a4c607 100644 --- a/src/test/WixToolsetTest.CoreIntegration/PreprocessorFixture.cs +++ b/src/test/WixToolsetTest.CoreIntegration/PreprocessorFixture.cs | |||
@@ -68,34 +68,6 @@ namespace WixToolsetTest.CoreIntegration | |||
68 | } | 68 | } |
69 | 69 | ||
70 | [Fact] | 70 | [Fact] |
71 | public void WixVersionVariablesWork() | ||
72 | { | ||
73 | var folder = TestData.Get(@"TestData\Variables"); | ||
74 | |||
75 | using (var fs = new DisposableFileSystem()) | ||
76 | { | ||
77 | var baseFolder = fs.GetFolder(); | ||
78 | var intermediateFolder = Path.Combine(baseFolder, "obj"); | ||
79 | |||
80 | var result = WixRunner.Execute(new[] | ||
81 | { | ||
82 | "build", | ||
83 | Path.Combine(folder, "Package.wxs"), | ||
84 | Path.Combine(folder, "PackageComponents.wxs"), | ||
85 | "-loc", Path.Combine(folder, "Package.en-us.wxl"), | ||
86 | "-bindpath", Path.Combine(folder, "data"), | ||
87 | "-intermediateFolder", intermediateFolder, | ||
88 | "-o", Path.Combine(baseFolder, @"bin\test.msi") | ||
89 | }); | ||
90 | |||
91 | result.AssertSuccess(); | ||
92 | |||
93 | var warning = result.Messages.Where(message => message.Id == (int)WarningMessages.Ids.PreprocessorWarning); | ||
94 | Assert.Single(warning); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | [Fact] | ||
99 | public void ForEachLoopsWork() | 71 | public void ForEachLoopsWork() |
100 | { | 72 | { |
101 | var folder = TestData.Get(@"TestData\ForEach"); | 73 | var folder = TestData.Get(@"TestData\ForEach"); |
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.0.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.0.txt new file mode 100644 index 00000000..6fd385bd --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.0.txt | |||
@@ -0,0 +1 @@ | |||
This is A v1.0.0 | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.1.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.1.txt new file mode 100644 index 00000000..b1f0bc01 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.1.txt | |||
@@ -0,0 +1 @@ | |||
This ia A v1.0.1 | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.0.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.0.txt new file mode 100644 index 00000000..ece55fec --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.0.txt | |||
@@ -0,0 +1 @@ | |||
This is B v1.0.0 | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.1.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.1.txt new file mode 100644 index 00000000..cf3372fd --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.1.txt | |||
@@ -0,0 +1 @@ | |||
This ia B v1.0.1 | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Package.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Package.wxs new file mode 100644 index 00000000..2657797f --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Package.wxs | |||
@@ -0,0 +1,31 @@ | |||
1 | <Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'> | ||
2 | <Product Id='e703bf17-4765-444c-91fd-88550fa681d4' Name='~Test Package' | ||
3 | Version='$(var.V)' Manufacturer='Example Corporation' Language='1033' | ||
4 | UpgradeCode='e703bf17-4765-444c-91fd-88550fa681d4'> | ||
5 | <Package InstallScope='perMachine' /> | ||
6 | |||
7 | <MajorUpgrade DowngradeErrorMessage='Newer version already installed.' /> | ||
8 | <MediaTemplate /> | ||
9 | |||
10 | <Directory Id='TARGETDIR' Name='SourceDir'> | ||
11 | <Directory Id='ProgramFilesFolder'> | ||
12 | <Directory Id='INSTALLFOLDER' Name='~Test App' /> | ||
13 | </Directory> | ||
14 | </Directory> | ||
15 | |||
16 | <Feature Id='Main'> | ||
17 | <ComponentGroupRef Id='Components' /> | ||
18 | </Feature> | ||
19 | </Product> | ||
20 | |||
21 | <Fragment> | ||
22 | <ComponentGroup Id="Components" Directory='INSTALLFOLDER'> | ||
23 | <Component Id='A'> | ||
24 | <File Name='a.txt' Source='Av$(var.A).txt' /> | ||
25 | </Component> | ||
26 | <Component Id='B'> | ||
27 | <File Name='b.txt' Source='Bv$(var.B).txt' /> | ||
28 | </Component> | ||
29 | </ComponentGroup> | ||
30 | </Fragment> | ||
31 | </Wix> | ||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Patch.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Patch.wxs new file mode 100644 index 00000000..7c3cff7e --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Patch.wxs | |||
@@ -0,0 +1,23 @@ | |||
1 | <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> | ||
2 | <Patch | ||
3 | AllowRemoval="yes" | ||
4 | Manufacturer="FireGiant" | ||
5 | MoreInfoURL="http://www.example.com/" | ||
6 | DisplayName="~Test Patch v$(var.V)" | ||
7 | Description="~Test Small Update Patch v($var.V)" | ||
8 | Classification="Update" | ||
9 | > | ||
10 | |||
11 | <Media Id="1" Cabinet="foo.cab"> | ||
12 | <PatchBaseline Id="RTM"/> | ||
13 | </Media> | ||
14 | |||
15 | <PatchFamilyRef Id='SamplePatchFamily'/> | ||
16 | </Patch> | ||
17 | |||
18 | <Fragment> | ||
19 | <PatchFamily Id='SamplePatchFamily' Version='$(var.V)' Supersede='yes'> | ||
20 | <ComponentRef Id="A"/> | ||
21 | </PatchFamily> | ||
22 | </Fragment> | ||
23 | </Wix> | ||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Av1.0.0.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Av1.0.0.txt new file mode 100644 index 00000000..6fd385bd --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Av1.0.0.txt | |||
@@ -0,0 +1 @@ | |||
This is A v1.0.0 | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Av1.0.1.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Av1.0.1.txt new file mode 100644 index 00000000..b1f0bc01 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Av1.0.1.txt | |||
@@ -0,0 +1 @@ | |||
This ia A v1.0.1 | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Bv1.0.0.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Bv1.0.0.txt new file mode 100644 index 00000000..ece55fec --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Bv1.0.0.txt | |||
@@ -0,0 +1 @@ | |||
This is B v1.0.0 | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Bv1.0.1.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Bv1.0.1.txt new file mode 100644 index 00000000..cf3372fd --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Bv1.0.1.txt | |||
@@ -0,0 +1 @@ | |||
This ia B v1.0.1 | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/Package.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/Package.wxs new file mode 100644 index 00000000..ee133ba3 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/Package.wxs | |||
@@ -0,0 +1,31 @@ | |||
1 | <Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'> | ||
2 | <Product Id='7d326855-e790-4a94-8611-5351f8321fca' Name='~Test Package' | ||
3 | Version='$(var.V)' Manufacturer='Example Corporation' Language='1033' | ||
4 | UpgradeCode='7d326855-e790-4a94-8611-5351f8321fca'> | ||
5 | <Package InstallScope='perMachine' Compressed='yes' /> | ||
6 | |||
7 | <MajorUpgrade DowngradeErrorMessage='Newer version already installed.' /> | ||
8 | <MediaTemplate EmbedCab='yes' /> | ||
9 | |||
10 | <Directory Id='TARGETDIR' Name='SourceDir'> | ||
11 | <Directory Id='ProgramFilesFolder'> | ||
12 | <Directory Id='INSTALLFOLDER' Name='~Test App' /> | ||
13 | </Directory> | ||
14 | </Directory> | ||
15 | |||
16 | <Feature Id='Main'> | ||
17 | <ComponentGroupRef Id='Components' /> | ||
18 | </Feature> | ||
19 | </Product> | ||
20 | |||
21 | <Fragment> | ||
22 | <ComponentGroup Id="Components" Directory='INSTALLFOLDER'> | ||
23 | <Component> | ||
24 | <File Id='a.txt' Name='a.txt' Source='Av$(var.A).txt' /> | ||
25 | </Component> | ||
26 | <Component> | ||
27 | <File Id='b.txt' Name='b.txt' Source='Bv$(var.B).txt' /> | ||
28 | </Component> | ||
29 | </ComponentGroup> | ||
30 | </Fragment> | ||
31 | </Wix> | ||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/Patch.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/Patch.wxs new file mode 100644 index 00000000..52e87f64 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/Patch.wxs | |||
@@ -0,0 +1,16 @@ | |||
1 | <Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'> | ||
2 | <Patch | ||
3 | AllowRemoval="yes" | ||
4 | DisplayName="~Test Patch v$(var.V)" | ||
5 | Description="~Test Small Update Patch v$(var.V)" | ||
6 | MoreInfoURL="http://www.example.com/" | ||
7 | Manufacturer="Example Corporation" | ||
8 | Classification="Update"> | ||
9 | |||
10 | <Media Id="1" Cabinet="foo.cab"> | ||
11 | <PatchBaseline Id="RTM" BaselineFile="Baseline.wixpdb" UpdateFile="Update.wixpdb" /> | ||
12 | </Media> | ||
13 | |||
14 | <PatchFamily Id='SequenceFamily' Version='$(var.V)' /> | ||
15 | </Patch> | ||
16 | </Wix> | ||
diff --git a/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj b/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj index 0330adf6..b17a27ff 100644 --- a/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj +++ b/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj | |||
@@ -1,4 +1,4 @@ | |||
1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
2 | <!-- 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 | <!-- 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. --> |
3 | 3 | ||
4 | <Project Sdk="Microsoft.NET.Sdk"> | 4 | <Project Sdk="Microsoft.NET.Sdk"> |
@@ -41,6 +41,18 @@ | |||
41 | <Content Include="TestData\Font\TrueType.wxs" CopyToOutputDirectory="PreserveNewest" /> | 41 | <Content Include="TestData\Font\TrueType.wxs" CopyToOutputDirectory="PreserveNewest" /> |
42 | <Content Include="TestData\Icon\SampleIcon.wxs" CopyToOutputDirectory="PreserveNewest" /> | 42 | <Content Include="TestData\Icon\SampleIcon.wxs" CopyToOutputDirectory="PreserveNewest" /> |
43 | <Content Include="TestData\LockPermissions\EmptyPermissions.wxs" CopyToOutputDirectory="PreserveNewest" /> | 43 | <Content Include="TestData\LockPermissions\EmptyPermissions.wxs" CopyToOutputDirectory="PreserveNewest" /> |
44 | <Content Include="TestData\PatchFamilyFilter\.data\Av1.0.0.txt" CopyToOutputDirectory="PreserveNewest" /> | ||
45 | <Content Include="TestData\PatchFamilyFilter\.data\Av1.0.1.txt" CopyToOutputDirectory="PreserveNewest" /> | ||
46 | <Content Include="TestData\PatchFamilyFilter\.data\Bv1.0.0.txt" CopyToOutputDirectory="PreserveNewest" /> | ||
47 | <Content Include="TestData\PatchFamilyFilter\.data\Bv1.0.1.txt" CopyToOutputDirectory="PreserveNewest" /> | ||
48 | <Content Include="TestData\PatchFamilyFilter\Package.wxs" CopyToOutputDirectory="PreserveNewest" /> | ||
49 | <Content Include="TestData\PatchFamilyFilter\Patch.wxs" CopyToOutputDirectory="PreserveNewest" /> | ||
50 | <Content Include="TestData\PatchSingle\.data\Av1.0.0.txt" CopyToOutputDirectory="PreserveNewest" /> | ||
51 | <Content Include="TestData\PatchSingle\.data\Av1.0.1.txt" CopyToOutputDirectory="PreserveNewest" /> | ||
52 | <Content Include="TestData\PatchSingle\.data\Bv1.0.0.txt" CopyToOutputDirectory="PreserveNewest" /> | ||
53 | <Content Include="TestData\PatchSingle\.data\Bv1.0.1.txt" CopyToOutputDirectory="PreserveNewest" /> | ||
54 | <Content Include="TestData\PatchSingle\Package.wxs" CopyToOutputDirectory="PreserveNewest" /> | ||
55 | <Content Include="TestData\PatchSingle\Patch.wxs" CopyToOutputDirectory="PreserveNewest" /> | ||
44 | <Content Include="TestData\SameFileFolders\data\a\test.txt" CopyToOutputDirectory="PreserveNewest" /> | 56 | <Content Include="TestData\SameFileFolders\data\a\test.txt" CopyToOutputDirectory="PreserveNewest" /> |
45 | <Content Include="TestData\SameFileFolders\data\c\test.txt" CopyToOutputDirectory="PreserveNewest" /> | 57 | <Content Include="TestData\SameFileFolders\data\c\test.txt" CopyToOutputDirectory="PreserveNewest" /> |
46 | <Content Include="TestData\SameFileFolders\data\b\test.txt" CopyToOutputDirectory="PreserveNewest" /> | 58 | <Content Include="TestData\SameFileFolders\data\b\test.txt" CopyToOutputDirectory="PreserveNewest" /> |