diff options
| author | Rob Mensching <rob@firegiant.com> | 2020-05-30 14:53:05 -0700 |
|---|---|---|
| committer | Rob Mensching <rob@firegiant.com> | 2020-05-30 15:07:21 -0700 |
| commit | d529525a1e331f3ef9ec2707791c99bd78fdd82f (patch) | |
| tree | 1d9fe1f0c0ee9850371c916802eb03ec9dc37a87 | |
| parent | 9c54d2fce80983bbee5f0f113b5aa30f22bc8a23 (diff) | |
| download | wix-d529525a1e331f3ef9ec2707791c99bd78fdd82f.tar.gz wix-d529525a1e331f3ef9ec2707791c99bd78fdd82f.tar.bz2 wix-d529525a1e331f3ef9ec2707791c99bd78fdd82f.zip | |
Basic patching support
19 files changed, 779 insertions, 986 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs index 214bf617..a16bafd7 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs | |||
| @@ -147,7 +147,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 147 | { | 147 | { |
| 148 | foreach (MediaRow transformMediaRow in transformMediaTable.Rows) | 148 | foreach (MediaRow transformMediaRow in transformMediaTable.Rows) |
| 149 | { | 149 | { |
| 150 | if (mediaTuple.LastSequence < transformMediaRow.LastSequence) | 150 | if (!mediaTuple.LastSequence.HasValue || mediaTuple.LastSequence < transformMediaRow.LastSequence) |
| 151 | { | 151 | { |
| 152 | // The Binder will pre-increment the sequence. | 152 | // The Binder will pre-increment the sequence. |
| 153 | mediaTuple.LastSequence = transformMediaRow.LastSequence; | 153 | mediaTuple.LastSequence = transformMediaRow.LastSequence; |
| @@ -156,14 +156,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | // Use the Media/@DiskId if greater than the last sequence for backward compatibility. | 158 | // Use the Media/@DiskId if greater than the last sequence for backward compatibility. |
| 159 | if (mediaTuple.LastSequence < mediaTuple.DiskId) | 159 | if (!mediaTuple.LastSequence.HasValue || mediaTuple.LastSequence < mediaTuple.DiskId) |
| 160 | { | 160 | { |
| 161 | mediaTuple.LastSequence = mediaTuple.DiskId; | 161 | mediaTuple.LastSequence = mediaTuple.DiskId; |
| 162 | } | 162 | } |
| 163 | 163 | ||
| 164 | // Ignore media table in the transform. | 164 | // Ignore media table in the transform. |
| 165 | mainTransform.Transform.Tables.Remove("Media"); | 165 | mainTransform.Transform.Tables.Remove("Media"); |
| 166 | mainTransform.Transform.Tables.Remove("WixMedia"); | ||
| 167 | mainTransform.Transform.Tables.Remove("MsiDigitalSignature"); | 166 | mainTransform.Transform.Tables.Remove("MsiDigitalSignature"); |
| 168 | 167 | ||
| 169 | var pairedTransform = this.BuildPairedTransform(summaryInfo, patchMetadata, patchIdTuple, mainTransform.Transform, mediaTuple, baselineTuple, out var productCode); | 168 | var pairedTransform = this.BuildPairedTransform(summaryInfo, patchMetadata, patchIdTuple, mainTransform.Transform, mediaTuple, baselineTuple, out var productCode); |
| @@ -767,15 +766,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 767 | if (transform.TryGetTable("File", out var fileTable)) | 766 | if (transform.TryGetTable("File", out var fileTable)) |
| 768 | { | 767 | { |
| 769 | var componentWithChangedKeyPath = new Dictionary<string, string>(); | 768 | var componentWithChangedKeyPath = new Dictionary<string, string>(); |
| 770 | foreach (var row in fileTable.Rows) | 769 | foreach (FileRow row in fileTable.Rows) |
| 771 | { | 770 | { |
| 772 | if (RowOperation.None == row.Operation) | 771 | if (RowOperation.None == row.Operation) |
| 773 | { | 772 | { |
| 774 | continue; | 773 | continue; |
| 775 | } | 774 | } |
| 776 | 775 | ||
| 777 | var fileId = row.FieldAsString(0); | 776 | var fileId = row.File; |
| 778 | var componentId = row.FieldAsString(1); | 777 | var componentId = row.Component; |
| 779 | 778 | ||
| 780 | // If this file is the keypath of a component | 779 | // If this file is the keypath of a component |
| 781 | if (componentKeyPath.TryGetValue(componentId, out var keyPath) && keyPath.Equals(fileId, StringComparison.Ordinal)) | 780 | if (componentKeyPath.TryGetValue(componentId, out var keyPath) && keyPath.Equals(fileId, StringComparison.Ordinal)) |
| @@ -972,7 +971,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 972 | 971 | ||
| 973 | if (row.Operation == RowOperation.None) | 972 | if (row.Operation == RowOperation.None) |
| 974 | { | 973 | { |
| 975 | // ignore the rows without section id. | 974 | // Ignore the rows without section id. |
| 976 | if (isSectionIdEmpty) | 975 | if (isSectionIdEmpty) |
| 977 | { | 976 | { |
| 978 | currentTable.Rows.RemoveAt(i); | 977 | currentTable.Rows.RemoveAt(i); |
| @@ -1157,47 +1156,33 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 1157 | return null; | 1156 | return null; |
| 1158 | } | 1157 | } |
| 1159 | 1158 | ||
| 1160 | // copy File table | 1159 | // Copy File table |
| 1161 | if (mainTransform.Tables.TryGetTable("File", out var mainFileTable) && 0 < mainFileTable.Rows.Count) | 1160 | if (mainTransform.Tables.TryGetTable("File", out var mainFileTable) && 0 < mainFileTable.Rows.Count) |
| 1162 | { | 1161 | { |
| 1163 | #if TODO_PATCHING | ||
| 1164 | // We require file source information. | ||
| 1165 | var mainWixFileTable = mainTransform.Tables["WixFile"]; | ||
| 1166 | if (null == mainWixFileTable) | ||
| 1167 | { | ||
| 1168 | this.Messaging.Write(ErrorMessages.AdminImageRequired(productCode)); | ||
| 1169 | return null; | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | var mainFileRows = new RowDictionary<FileRow>(mainFileTable); | ||
| 1173 | |||
| 1174 | var pairedFileTable = pairedTransform.EnsureTable(mainFileTable.Definition); | 1162 | var pairedFileTable = pairedTransform.EnsureTable(mainFileTable.Definition); |
| 1175 | { | ||
| 1176 | var mainFileRow = mainFileRows[mainWixFileRow.File]; | ||
| 1177 | 1163 | ||
| 1178 | // set File.Sequence to non null to satisfy transform bind | 1164 | foreach (FileRow mainFileRow in mainFileTable.Rows) |
| 1165 | { | ||
| 1166 | // Set File.Sequence to non null to satisfy transform bind. | ||
| 1179 | mainFileRow.Sequence = 1; | 1167 | mainFileRow.Sequence = 1; |
| 1180 | 1168 | ||
| 1181 | // delete's don't need rows in the paired transform | 1169 | // Delete's don't need rows in the paired transform. |
| 1182 | if (mainFileRow.Operation == RowOperation.Delete) | 1170 | if (mainFileRow.Operation == RowOperation.Delete) |
| 1183 | { | 1171 | { |
| 1184 | continue; | 1172 | continue; |
| 1185 | } | 1173 | } |
| 1186 | 1174 | ||
| 1187 | var pairedFileRow = (FileRow)pairedFileTable.CreateRow(null); | 1175 | var pairedFileRow = (FileRow)pairedFileTable.CreateRow(mainFileRow.SourceLineNumbers); |
| 1188 | pairedFileRow.Operation = RowOperation.Modify; | 1176 | pairedFileRow.Operation = RowOperation.Modify; |
| 1189 | for (var i = 0; i < mainFileRow.Fields.Length; i++) | 1177 | mainFileRow.CopyTo(pairedFileRow); |
| 1190 | { | ||
| 1191 | pairedFileRow[i] = mainFileRow[i]; | ||
| 1192 | } | ||
| 1193 | 1178 | ||
| 1194 | // override authored media for patch bind | 1179 | // Override authored media for patch bind. |
| 1195 | mainWixFileRow.DiskId = mediaTuple.DiskId; | 1180 | mainFileRow.DiskId = mediaTuple.DiskId; |
| 1196 | 1181 | ||
| 1197 | // suppress any change to File.Sequence to avoid bloat | 1182 | // Suppress any change to File.Sequence to avoid bloat. |
| 1198 | mainFileRow.Fields[7].Modified = false; | 1183 | mainFileRow.Fields[7].Modified = false; |
| 1199 | 1184 | ||
| 1200 | // force File row to appear in the transform | 1185 | // Force File row to appear in the transform. |
| 1201 | switch (mainFileRow.Operation) | 1186 | switch (mainFileRow.Operation) |
| 1202 | { | 1187 | { |
| 1203 | case RowOperation.Modify: | 1188 | case RowOperation.Modify: |
| @@ -1211,19 +1196,18 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 1211 | break; | 1196 | break; |
| 1212 | } | 1197 | } |
| 1213 | } | 1198 | } |
| 1214 | #endif | ||
| 1215 | } | 1199 | } |
| 1216 | 1200 | ||
| 1217 | // Add Media row to pairedTransform | 1201 | // Add Media row to pairedTransform |
| 1218 | var pairedMediaTable = pairedTransform.EnsureTable(this.tableDefinitions["Media"]); | 1202 | var pairedMediaTable = pairedTransform.EnsureTable(this.tableDefinitions["Media"]); |
| 1219 | var pairedMediaRow = pairedMediaTable.CreateRow(mediaTuple.SourceLineNumbers); | 1203 | var pairedMediaRow = (MediaRow)pairedMediaTable.CreateRow(mediaTuple.SourceLineNumbers); |
| 1220 | pairedMediaRow.Operation = RowOperation.Add; | 1204 | pairedMediaRow.Operation = RowOperation.Add; |
| 1221 | pairedMediaRow[0] = mediaTuple.DiskId; | 1205 | pairedMediaRow.DiskId = mediaTuple.DiskId; |
| 1222 | pairedMediaRow[1] = mediaTuple.LastSequence ?? 0; | 1206 | pairedMediaRow.LastSequence = mediaTuple.LastSequence ?? 0; |
| 1223 | pairedMediaRow[2] = mediaTuple.DiskPrompt; | 1207 | pairedMediaRow.DiskPrompt = mediaTuple.DiskPrompt; |
| 1224 | pairedMediaRow[3] = mediaTuple.Cabinet; | 1208 | pairedMediaRow.Cabinet = mediaTuple.Cabinet; |
| 1225 | pairedMediaRow[4] = mediaTuple.VolumeLabel; | 1209 | pairedMediaRow.VolumeLabel = mediaTuple.VolumeLabel; |
| 1226 | pairedMediaRow[5] = mediaTuple.Source; | 1210 | pairedMediaRow.Source = mediaTuple.Source; |
| 1227 | 1211 | ||
| 1228 | // Add PatchPackage for this Media | 1212 | // Add PatchPackage for this Media |
| 1229 | var pairedPackageTable = pairedTransform.EnsureTable(this.tableDefinitions["PatchPackage"]); | 1213 | var pairedPackageTable = pairedTransform.EnsureTable(this.tableDefinitions["PatchPackage"]); |
| @@ -1283,7 +1267,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 1283 | { | 1267 | { |
| 1284 | var patchTargetTuples = tuples.OfType<WixPatchTargetTuple>().ToList(); | 1268 | var patchTargetTuples = tuples.OfType<WixPatchTargetTuple>().ToList(); |
| 1285 | 1269 | ||
| 1286 | if (patchTargetTuples.Count > 0) | 1270 | if (patchTargetTuples.Any()) |
| 1287 | { | 1271 | { |
| 1288 | var targets = new SortedSet<string>(); | 1272 | var targets = new SortedSet<string>(); |
| 1289 | var replace = true; | 1273 | var replace = true; |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs index 8e901d30..d9d246f5 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs | |||
| @@ -43,7 +43,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 43 | this.DefaultCompressionLevel = context.DefaultCompressionLevel; | 43 | this.DefaultCompressionLevel = context.DefaultCompressionLevel; |
| 44 | this.DelayedFields = context.DelayedFields; | 44 | this.DelayedFields = context.DelayedFields; |
| 45 | this.ExpectedEmbeddedFiles = context.ExpectedEmbeddedFiles; | 45 | this.ExpectedEmbeddedFiles = context.ExpectedEmbeddedFiles; |
| 46 | this.FileSystemExtensions = context.FileSystemExtensions; | 46 | this.FileSystemManager = new FileSystemManager(context.FileSystemExtensions); |
| 47 | this.Intermediate = context.IntermediateRepresentation; | 47 | this.Intermediate = context.IntermediateRepresentation; |
| 48 | this.IntermediateFolder = context.IntermediateFolder; | 48 | this.IntermediateFolder = context.IntermediateFolder; |
| 49 | this.OutputPath = context.OutputPath; | 49 | this.OutputPath = context.OutputPath; |
| @@ -79,7 +79,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 79 | 79 | ||
| 80 | public IEnumerable<IExpectedExtractFile> ExpectedEmbeddedFiles { get; } | 80 | public IEnumerable<IExpectedExtractFile> ExpectedEmbeddedFiles { get; } |
| 81 | 81 | ||
| 82 | public IEnumerable<IFileSystemExtension> FileSystemExtensions { get; } | 82 | public FileSystemManager FileSystemManager { get; } |
| 83 | 83 | ||
| 84 | public bool DeltaBinaryPatch { get; set; } | 84 | public bool DeltaBinaryPatch { get; set; } |
| 85 | 85 | ||
| @@ -116,7 +116,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 116 | var trackedFiles = new List<ITrackedFile>(); | 116 | var trackedFiles = new List<ITrackedFile>(); |
| 117 | 117 | ||
| 118 | var containsMergeModules = false; | 118 | var containsMergeModules = false; |
| 119 | var suppressedTableNames = new HashSet<string>(); | ||
| 120 | 119 | ||
| 121 | // If there are any fields to resolve later, create the cache to populate during bind. | 120 | // If there are any fields to resolve later, create the cache to populate during bind. |
| 122 | var variableCache = this.DelayedFields.Any() ? new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) : null; | 121 | var variableCache = this.DelayedFields.Any() ? new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) : null; |
| @@ -226,7 +225,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 226 | return null; | 225 | return null; |
| 227 | } | 226 | } |
| 228 | 227 | ||
| 229 | WindowsInstallerData output; | ||
| 230 | this.Intermediate.UpdateLevel(Data.WindowsInstaller.IntermediateLevels.FullyBound); | 228 | this.Intermediate.UpdateLevel(Data.WindowsInstaller.IntermediateLevels.FullyBound); |
| 231 | this.Messaging.Write(VerboseMessages.UpdatingFileInformation()); | 229 | this.Messaging.Write(VerboseMessages.UpdatingFileInformation()); |
| 232 | 230 | ||
| @@ -268,14 +266,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 268 | } | 266 | } |
| 269 | else if (SectionType.Patch == section.Type) | 267 | else if (SectionType.Patch == section.Type) |
| 270 | { | 268 | { |
| 271 | // Merge transform data into the output object. | 269 | var command = new GetFileFacadesFromTransforms(this.Messaging, this.FileSystemManager, this.SubStorages); |
| 272 | //IEnumerable<FileFacade> filesFromTransform = this.CopyFromTransformData(this.Output); | ||
| 273 | |||
| 274 | //var command = new CopyTransformDataCommand(this.Messaging, /*output*/this.SubStorages, tableDefinitions, copyOutFileRows: true); | ||
| 275 | //command.Output = output; | ||
| 276 | //command.TableDefinitions = this.TableDefinitions; | ||
| 277 | //command.CopyOutFileRows = true; | ||
| 278 | var command = new GetFileFacadesFromTransforms(this.Messaging, this.SubStorages, tableDefinitions); | ||
| 279 | command.Execute(); | 270 | command.Execute(); |
| 280 | var filesFromTransforms = command.FileFacades; | 271 | var filesFromTransforms = command.FileFacades; |
| 281 | 272 | ||
| @@ -338,6 +329,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 338 | command.Execute(); | 329 | command.Execute(); |
| 339 | } | 330 | } |
| 340 | 331 | ||
| 332 | // Update file sequence. | ||
| 333 | { | ||
| 334 | var command = new UpdateMediaSequencesCommand(section, fileFacades); | ||
| 335 | command.Execute(); | ||
| 336 | } | ||
| 337 | |||
| 341 | // stop processing if an error previously occurred | 338 | // stop processing if an error previously occurred |
| 342 | if (this.Messaging.EncounteredError) | 339 | if (this.Messaging.EncounteredError) |
| 343 | { | 340 | { |
| @@ -345,6 +342,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 345 | } | 342 | } |
| 346 | 343 | ||
| 347 | // Time to create the output object. Try to put as much above here as possible, updating the IR is better. | 344 | // Time to create the output object. Try to put as much above here as possible, updating the IR is better. |
| 345 | WindowsInstallerData output; | ||
| 348 | { | 346 | { |
| 349 | var command = new CreateOutputFromIRCommand(this.Messaging, section, tableDefinitions, this.BackendExtensions, this.WindowsInstallerBackendHelper); | 347 | var command = new CreateOutputFromIRCommand(this.Messaging, section, tableDefinitions, this.BackendExtensions, this.WindowsInstallerBackendHelper); |
| 350 | command.Execute(); | 348 | command.Execute(); |
| @@ -352,14 +350,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 352 | output = command.Output; | 350 | output = command.Output; |
| 353 | } | 351 | } |
| 354 | 352 | ||
| 355 | // Update file sequence. | ||
| 356 | { | ||
| 357 | var command = new UpdateMediaSequencesCommand(output, fileFacades); | ||
| 358 | command.Execute(); | ||
| 359 | } | ||
| 360 | |||
| 361 | // Modularize identifiers. | 353 | // Modularize identifiers. |
| 362 | if (OutputType.Module == output.Type) | 354 | if (output.Type == OutputType.Module) |
| 363 | { | 355 | { |
| 364 | var command = new ModularizeCommand(output, modularizationSuffix, section.Tuples.OfType<WixSuppressModularizationTuple>()); | 356 | var command = new ModularizeCommand(output, modularizationSuffix, section.Tuples.OfType<WixSuppressModularizationTuple>()); |
| 365 | command.Execute(); | 357 | command.Execute(); |
| @@ -457,18 +449,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 457 | trackedFiles.AddRange(command.TrackedFiles); | 449 | trackedFiles.AddRange(command.TrackedFiles); |
| 458 | } | 450 | } |
| 459 | 451 | ||
| 460 | #if DELETE | 452 | if (output.Type == OutputType.Patch) |
| 461 | if (OutputType.Patch == output.Type) | 453 | { |
| 462 | { | 454 | // Copy output data back into the transforms. |
| 463 | // Copy output data back into the transforms. | 455 | var command = new UpdateTransformsWithFileFacades(this.Messaging, output, this.SubStorages, tableDefinitions, fileFacades); |
| 464 | #if TODO_PATCHING | 456 | command.Execute(); |
| 465 | var command = new CopyTransformDataCommand(this.Messaging, output, tableDefinitions, copyOutFileRows: false); | 457 | } |
| 466 | command.Execute(); | ||
| 467 | |||
| 468 | this.CopyToTransformData(this.Output); | ||
| 469 | #endif | ||
| 470 | } | ||
| 471 | #endif | ||
| 472 | 458 | ||
| 473 | // stop processing if an error previously occurred | 459 | // stop processing if an error previously occurred |
| 474 | if (this.Messaging.EncounteredError) | 460 | if (this.Messaging.EncounteredError) |
| @@ -483,8 +469,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 483 | var trackMsi = this.BackendHelper.TrackFile(this.OutputPath, TrackedFileType.Final); | 469 | var trackMsi = this.BackendHelper.TrackFile(this.OutputPath, TrackedFileType.Final); |
| 484 | trackedFiles.Add(trackMsi); | 470 | trackedFiles.Add(trackMsi); |
| 485 | 471 | ||
| 486 | var temporaryFiles = this.GenerateDatabase(output, tableDefinitions, trackMsi.Path, false, false); | 472 | var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystemManager, output, trackMsi.Path, tableDefinitions, this.IntermediateFolder, this.Codepage, keepAddedColumns: false, this.SuppressAddingValidationRows, useSubdirectory: false); |
| 487 | trackedFiles.AddRange(temporaryFiles); | 473 | command.Execute(); |
| 474 | |||
| 475 | trackedFiles.AddRange(command.GeneratedTemporaryFiles); | ||
| 488 | } | 476 | } |
| 489 | 477 | ||
| 490 | // Stop processing if an error previously occurred. | 478 | // Stop processing if an error previously occurred. |
| @@ -498,31 +486,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 498 | { | 486 | { |
| 499 | this.Messaging.Write(VerboseMessages.MergingModules()); | 487 | this.Messaging.Write(VerboseMessages.MergingModules()); |
| 500 | 488 | ||
| 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))) | ||
| 505 | { | ||
| 506 | var sequenceTableName = (sequence == SequenceTable.AdvertiseExecuteSequence) ? "AdvtExecuteSequence" : sequence.ToString(); | ||
| 507 | var sequenceTable = output.Tables[sequenceTableName]; | ||
| 508 | |||
| 509 | if (null == sequenceTable) | ||
| 510 | { | ||
| 511 | sequenceTable = output.EnsureTable(tableDefinitions[sequenceTableName]); | ||
| 512 | } | ||
| 513 | |||
| 514 | if (0 == sequenceTable.Rows.Count) | ||
| 515 | { | ||
| 516 | suppressedTableNames.Add(sequenceTableName); | ||
| 517 | } | ||
| 518 | } | ||
| 519 | |||
| 520 | var command = new MergeModulesCommand(this.Messaging); | 489 | var command = new MergeModulesCommand(this.Messaging); |
| 521 | command.FileFacades = fileFacades; | 490 | command.FileFacades = fileFacades; |
| 522 | command.IntermediateFolder = this.IntermediateFolder; | 491 | command.IntermediateFolder = this.IntermediateFolder; |
| 523 | command.Output = output; | 492 | command.Output = output; |
| 524 | command.OutputPath = this.OutputPath; | 493 | command.OutputPath = this.OutputPath; |
| 525 | command.SuppressedTableNames = suppressedTableNames; | 494 | command.TableDefinitions = tableDefinitions; |
| 526 | command.Execute(); | 495 | command.Execute(); |
| 527 | } | 496 | } |
| 528 | 497 | ||
| @@ -607,38 +576,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 607 | return wixout; | 576 | return wixout; |
| 608 | } | 577 | } |
| 609 | 578 | ||
| 610 | #if TODO_PATCHING | ||
| 611 | /// <summary> | ||
| 612 | /// Copy file data between transform substorages and the patch output object | ||
| 613 | /// </summary> | ||
| 614 | /// <param name="output">The output to bind.</param> | ||
| 615 | /// <param name="allFileRows">True if copying from transform to patch, false the other way.</param> | ||
| 616 | private IEnumerable<FileFacade> CopyFromTransformData(Output output) | ||
| 617 | { | ||
| 618 | var command = new CopyTransformDataCommand(); | ||
| 619 | command.CopyOutFileRows = true; | ||
| 620 | command.Output = output; | ||
| 621 | command.TableDefinitions = this.TableDefinitions; | ||
| 622 | command.Execute(); | ||
| 623 | |||
| 624 | return command.FileFacades; | ||
| 625 | } | ||
| 626 | |||
| 627 | /// <summary> | ||
| 628 | /// Copy file data between transform substorages and the patch output object | ||
| 629 | /// </summary> | ||
| 630 | /// <param name="output">The output to bind.</param> | ||
| 631 | /// <param name="allFileRows">True if copying from transform to patch, false the other way.</param> | ||
| 632 | private void CopyToTransformData(Output output) | ||
| 633 | { | ||
| 634 | var command = new CopyTransformDataCommand(); | ||
| 635 | command.CopyOutFileRows = false; | ||
| 636 | command.Output = output; | ||
| 637 | command.TableDefinitions = this.TableDefinitions; | ||
| 638 | command.Execute(); | ||
| 639 | } | ||
| 640 | #endif | ||
| 641 | |||
| 642 | /// <summary> | 579 | /// <summary> |
| 643 | /// Validate that there are no duplicate GUIDs in the output. | 580 | /// Validate that there are no duplicate GUIDs in the output. |
| 644 | /// </summary> | 581 | /// </summary> |
| @@ -650,7 +587,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 650 | { | 587 | { |
| 651 | if (output.TryGetTable("Component", out var componentTable)) | 588 | if (output.TryGetTable("Component", out var componentTable)) |
| 652 | { | 589 | { |
| 653 | Dictionary<string, bool> componentGuidConditions = new Dictionary<string, bool>(componentTable.Rows.Count); | 590 | var componentGuidConditions = new Dictionary<string, bool>(componentTable.Rows.Count); |
| 654 | 591 | ||
| 655 | foreach (Data.WindowsInstaller.Rows.ComponentRow row in componentTable.Rows) | 592 | foreach (Data.WindowsInstaller.Rows.ComponentRow row in componentTable.Rows) |
| 656 | { | 593 | { |
| @@ -658,12 +595,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 658 | // there's already an error that prevented it from being replaced with a real GUID. | 595 | // there's already an error that prevented it from being replaced with a real GUID. |
| 659 | if (!String.IsNullOrEmpty(row.Guid) && "*" != row.Guid) | 596 | if (!String.IsNullOrEmpty(row.Guid) && "*" != row.Guid) |
| 660 | { | 597 | { |
| 661 | bool thisComponentHasCondition = !String.IsNullOrEmpty(row.Condition); | 598 | var thisComponentHasCondition = !String.IsNullOrEmpty(row.Condition); |
| 662 | bool allComponentsHaveConditions = thisComponentHasCondition; | 599 | var allComponentsHaveConditions = thisComponentHasCondition; |
| 663 | 600 | ||
| 664 | if (componentGuidConditions.ContainsKey(row.Guid)) | 601 | if (componentGuidConditions.ContainsKey(row.Guid)) |
| 665 | { | 602 | { |
| 666 | allComponentsHaveConditions = componentGuidConditions[row.Guid] && thisComponentHasCondition; | 603 | allComponentsHaveConditions = thisComponentHasCondition && componentGuidConditions[row.Guid]; |
| 667 | 604 | ||
| 668 | if (allComponentsHaveConditions) | 605 | if (allComponentsHaveConditions) |
| 669 | { | 606 | { |
| @@ -728,20 +665,5 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 728 | 665 | ||
| 729 | return layout; | 666 | return layout; |
| 730 | } | 667 | } |
| 731 | |||
| 732 | /// <summary> | ||
| 733 | /// Creates the MSI/MSM/PCP database. | ||
| 734 | /// </summary> | ||
| 735 | /// <param name="output">Output to create database for.</param> | ||
| 736 | /// <param name="databaseFile">The database file to create.</param> | ||
| 737 | /// <param name="keepAddedColumns">Whether to keep columns added in a transform.</param> | ||
| 738 | /// <param name="useSubdirectory">Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files.</param> | ||
| 739 | private IEnumerable<ITrackedFile> GenerateDatabase(WindowsInstallerData output, TableDefinitionCollection tableDefinitions, string databaseFile, bool keepAddedColumns, bool useSubdirectory) | ||
| 740 | { | ||
| 741 | var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystemExtensions, output, databaseFile, tableDefinitions, this.IntermediateFolder, this.Codepage, keepAddedColumns, this.SuppressAddingValidationRows, useSubdirectory); | ||
| 742 | command.Execute(); | ||
| 743 | |||
| 744 | return command.GeneratedTemporaryFiles; | ||
| 745 | } | ||
| 746 | } | 668 | } |
| 747 | } | 669 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs index ffe26249..ac98c82d 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs | |||
| @@ -3,23 +3,21 @@ | |||
| 3 | namespace WixToolset.Core.WindowsInstaller.Bind | 3 | namespace WixToolset.Core.WindowsInstaller.Bind |
| 4 | { | 4 | { |
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections.Generic; | ||
| 7 | using System.Globalization; | 6 | using System.Globalization; |
| 8 | using System.IO; | 7 | using System.IO; |
| 9 | using WixToolset.Core.WindowsInstaller.Msi; | 8 | using WixToolset.Core.WindowsInstaller.Msi; |
| 10 | using WixToolset.Data; | 9 | using WixToolset.Data; |
| 11 | using WixToolset.Data.Tuples; | 10 | using WixToolset.Data.Tuples; |
| 12 | using WixToolset.Data.WindowsInstaller; | 11 | using WixToolset.Data.WindowsInstaller; |
| 13 | using WixToolset.Extensibility; | ||
| 14 | using WixToolset.Extensibility.Services; | 12 | using WixToolset.Extensibility.Services; |
| 15 | 13 | ||
| 16 | internal class BindTransformCommand | 14 | internal class BindTransformCommand |
| 17 | { | 15 | { |
| 18 | public BindTransformCommand(IMessaging messaging, IBackendHelper backendHelper, IEnumerable<IFileSystemExtension> extensions, string intermediateFolder, WindowsInstallerData transform, string outputPath, TableDefinitionCollection tableDefinitions) | 16 | public BindTransformCommand(IMessaging messaging, IBackendHelper backendHelper, FileSystemManager fileSystemManager, string intermediateFolder, WindowsInstallerData transform, string outputPath, TableDefinitionCollection tableDefinitions) |
| 19 | { | 17 | { |
| 20 | this.Messaging = messaging; | 18 | this.Messaging = messaging; |
| 21 | this.BackendHelper = backendHelper; | 19 | this.BackendHelper = backendHelper; |
| 22 | this.Extensions = extensions; | 20 | this.FileSystemManager = fileSystemManager; |
| 23 | this.IntermediateFolder = intermediateFolder; | 21 | this.IntermediateFolder = intermediateFolder; |
| 24 | this.Transform = transform; | 22 | this.Transform = transform; |
| 25 | this.OutputPath = outputPath; | 23 | this.OutputPath = outputPath; |
| @@ -30,7 +28,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 30 | 28 | ||
| 31 | private IBackendHelper BackendHelper { get; } | 29 | private IBackendHelper BackendHelper { get; } |
| 32 | 30 | ||
| 33 | private IEnumerable<IFileSystemExtension> Extensions { get; } | 31 | private FileSystemManager FileSystemManager { get; } |
| 34 | 32 | ||
| 35 | private TableDefinitionCollection TableDefinitions { get; } | 33 | private TableDefinitionCollection TableDefinitions { get; } |
| 36 | 34 | ||
| @@ -353,7 +351,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 353 | targetRow[i] = emptyFile; | 351 | targetRow[i] = emptyFile; |
| 354 | modifiedRow = true; | 352 | modifiedRow = true; |
| 355 | } | 353 | } |
| 356 | else if (!this.CompareFiles(objectField.PreviousData, (string)objectField.Data)) | 354 | else if (!this.FileSystemManager.CompareFiles(objectField.PreviousData, (string)objectField.Data)) |
| 357 | { | 355 | { |
| 358 | targetRow[i] = objectField.PreviousData; | 356 | targetRow[i] = objectField.PreviousData; |
| 359 | modifiedRow = true; | 357 | modifiedRow = true; |
| @@ -438,29 +436,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 438 | } | 436 | } |
| 439 | } | 437 | } |
| 440 | 438 | ||
| 441 | private bool CompareFiles(string targetFile, string updatedFile) | ||
| 442 | { | ||
| 443 | bool? compared = null; | ||
| 444 | foreach (var extension in this.Extensions) | ||
| 445 | { | ||
| 446 | compared = extension.CompareFiles(targetFile, updatedFile); | ||
| 447 | if (compared.HasValue) | ||
| 448 | { | ||
| 449 | break; | ||
| 450 | } | ||
| 451 | } | ||
| 452 | |||
| 453 | if (!compared.HasValue) | ||
| 454 | { | ||
| 455 | throw new InvalidOperationException(); // TODO: something needs to be said here that none of the binder file managers returned a result. | ||
| 456 | } | ||
| 457 | |||
| 458 | return compared.Value; | ||
| 459 | } | ||
| 460 | |||
| 461 | private void GenerateDatabase(WindowsInstallerData output, string outputPath, bool keepAddedColumns) | 439 | private void GenerateDatabase(WindowsInstallerData output, string outputPath, bool keepAddedColumns) |
| 462 | { | 440 | { |
| 463 | var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.Extensions, output, outputPath, this.TableDefinitions, this.IntermediateFolder, codepage: -1, keepAddedColumns, suppressAddingValidationRows: true, useSubdirectory: true ); | 441 | var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystemManager, output, outputPath, this.TableDefinitions, this.IntermediateFolder, codepage: -1, keepAddedColumns, suppressAddingValidationRows: true, useSubdirectory: true); |
| 464 | command.Execute(); | 442 | command.Execute(); |
| 465 | } | 443 | } |
| 466 | } | 444 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs index f5ac00e6..c54e9c53 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 TODO_PATCHING | 36 | #if TODO_PATCHING_DELTA |
| 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/CreateOutputFromIRCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs index 0cbb81d8..ffc4e84d 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs | |||
| @@ -499,7 +499,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 499 | row.FileSize = tuple.FileSize; | 499 | row.FileSize = tuple.FileSize; |
| 500 | row.Version = tuple.Version; | 500 | row.Version = tuple.Version; |
| 501 | row.Language = tuple.Language; | 501 | row.Language = tuple.Language; |
| 502 | row.DiskId = tuple.DiskId ?? 1; // TODO: is 0 the correct thing to default here | 502 | row.DiskId = tuple.DiskId ?? 1; // TODO: is 1 the correct thing to default here |
| 503 | row.Sequence = tuple.Sequence; | ||
| 503 | row.Source = tuple.Source.Path; | 504 | row.Source = tuple.Source.Path; |
| 504 | 505 | ||
| 505 | var attributes = (tuple.Attributes & FileTupleAttributes.Checksum) == FileTupleAttributes.Checksum ? WindowsInstallerConstants.MsidbFileAttributesChecksum : 0; | 506 | var attributes = (tuple.Attributes & FileTupleAttributes.Checksum) == FileTupleAttributes.Checksum ? WindowsInstallerConstants.MsidbFileAttributesChecksum : 0; |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs index 854d973e..f65f885b 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs | |||
| @@ -45,7 +45,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 45 | var baselineData = this.GetData(tuple.BaselineFile.Path); | 45 | var baselineData = this.GetData(tuple.BaselineFile.Path); |
| 46 | var updateData = this.GetData(tuple.UpdateFile.Path); | 46 | var updateData = this.GetData(tuple.UpdateFile.Path); |
| 47 | 47 | ||
| 48 | var command = new GenerateTransformCommand(this.Messaging, baselineData, updateData, false); | 48 | var command = new GenerateTransformCommand(this.Messaging, baselineData, updateData, preserveUnchangedRows: true, showPedanticMessages: false); |
| 49 | transform = command.Execute(); | 49 | transform = command.Execute(); |
| 50 | } | 50 | } |
| 51 | else | 51 | else |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/FileSystemManager.cs b/src/WixToolset.Core.WindowsInstaller/Bind/FileSystemManager.cs new file mode 100644 index 00000000..75477271 --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/FileSystemManager.cs | |||
| @@ -0,0 +1,71 @@ | |||
| 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.Collections.Generic; | ||
| 6 | using System.IO; | ||
| 7 | using WixToolset.Extensibility; | ||
| 8 | |||
| 9 | internal class FileSystemManager | ||
| 10 | { | ||
| 11 | public FileSystemManager(IEnumerable<IFileSystemExtension> fileSystemExtensions) | ||
| 12 | { | ||
| 13 | this.Extensions = fileSystemExtensions; | ||
| 14 | } | ||
| 15 | |||
| 16 | private IEnumerable<IFileSystemExtension> Extensions { get; } | ||
| 17 | |||
| 18 | public bool CompareFiles(string firstPath, string secondPath) | ||
| 19 | { | ||
| 20 | foreach (var extension in this.Extensions) | ||
| 21 | { | ||
| 22 | var compared = extension.CompareFiles(firstPath, secondPath); | ||
| 23 | if (compared.HasValue) | ||
| 24 | { | ||
| 25 | return compared.Value; | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | return BuiltinCompareFiles(firstPath, secondPath); | ||
| 30 | } | ||
| 31 | |||
| 32 | private static bool BuiltinCompareFiles(string firstPath, string secondPath) | ||
| 33 | { | ||
| 34 | using (var firstStream = File.OpenRead(firstPath)) | ||
| 35 | using (var secondStream = File.OpenRead(secondPath)) | ||
| 36 | { | ||
| 37 | if (firstStream.Length != secondStream.Length) | ||
| 38 | { | ||
| 39 | return false; | ||
| 40 | } | ||
| 41 | |||
| 42 | // Using a larger buffer than the default buffer of 4 * 1024 used by FileStream.ReadByte improves performance. | ||
| 43 | // The buffer size is based on user feedback. Based on performance results, a better buffer size may be determined. | ||
| 44 | var firstBuffer = new byte[16 * 1024]; | ||
| 45 | var secondBuffer = new byte[16 * 1024]; | ||
| 46 | |||
| 47 | var firstReadLength = 0; | ||
| 48 | do | ||
| 49 | { | ||
| 50 | firstReadLength = firstStream.Read(firstBuffer, 0, firstBuffer.Length); | ||
| 51 | var secondReadLength = secondStream.Read(secondBuffer, 0, secondBuffer.Length); | ||
| 52 | |||
| 53 | if (firstReadLength != secondReadLength) | ||
| 54 | { | ||
| 55 | return false; | ||
| 56 | } | ||
| 57 | |||
| 58 | for (var i = 0; i < firstReadLength; ++i) | ||
| 59 | { | ||
| 60 | if (firstBuffer[i] != secondBuffer[i]) | ||
| 61 | { | ||
| 62 | return false; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | } while (0 < firstReadLength); | ||
| 66 | } | ||
| 67 | |||
| 68 | return true; | ||
| 69 | } | ||
| 70 | } | ||
| 71 | } | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs index ed3b6f01..eff94e80 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs | |||
| @@ -10,17 +10,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 10 | using WixToolset.Core.WindowsInstaller.Msi; | 10 | using WixToolset.Core.WindowsInstaller.Msi; |
| 11 | using WixToolset.Data; | 11 | using WixToolset.Data; |
| 12 | using WixToolset.Data.WindowsInstaller; | 12 | using WixToolset.Data.WindowsInstaller; |
| 13 | using WixToolset.Extensibility; | ||
| 14 | using WixToolset.Extensibility.Data; | 13 | using WixToolset.Extensibility.Data; |
| 15 | using WixToolset.Extensibility.Services; | 14 | using WixToolset.Extensibility.Services; |
| 16 | 15 | ||
| 17 | internal class GenerateDatabaseCommand | 16 | internal class GenerateDatabaseCommand |
| 18 | { | 17 | { |
| 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) | 18 | public GenerateDatabaseCommand(IMessaging messaging, IBackendHelper backendHelper, FileSystemManager fileSystemManager, WindowsInstallerData data, string outputPath, TableDefinitionCollection tableDefinitions, string intermediateFolder, int codepage, bool keepAddedColumns, bool suppressAddingValidationRows, bool useSubdirectory) |
| 20 | { | 19 | { |
| 21 | this.Messaging = messaging; | 20 | this.Messaging = messaging; |
| 22 | this.BackendHelper = backendHelper; | 21 | this.BackendHelper = backendHelper; |
| 23 | this.Extensions = fileSystemExtensions; | 22 | this.FileSystemManager = fileSystemManager; |
| 24 | this.Data = data; | 23 | this.Data = data; |
| 25 | this.OutputPath = outputPath; | 24 | this.OutputPath = outputPath; |
| 26 | this.TableDefinitions = tableDefinitions; | 25 | this.TableDefinitions = tableDefinitions; |
| @@ -35,7 +34,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 35 | 34 | ||
| 36 | private IBackendHelper BackendHelper { get; } | 35 | private IBackendHelper BackendHelper { get; } |
| 37 | 36 | ||
| 38 | private IEnumerable<IFileSystemExtension> Extensions { get; } | 37 | private FileSystemManager FileSystemManager { get; } |
| 39 | 38 | ||
| 40 | /// <summary> | 39 | /// <summary> |
| 41 | /// Whether to keep columns added in a transform. | 40 | /// Whether to keep columns added in a transform. |
| @@ -371,7 +370,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 371 | var transformFile = Path.Combine(this.IntermediateFolder, String.Concat(subStorage.Name, ".mst")); | 370 | var transformFile = Path.Combine(this.IntermediateFolder, String.Concat(subStorage.Name, ".mst")); |
| 372 | 371 | ||
| 373 | // Bind the transform. | 372 | // Bind the transform. |
| 374 | var command = new BindTransformCommand(this.Messaging, this.BackendHelper, this.Extensions, this.IntermediateFolder, subStorage.Data, transformFile, this.TableDefinitions); | 373 | var command = new BindTransformCommand(this.Messaging, this.BackendHelper, this.FileSystemManager, this.IntermediateFolder, subStorage.Data, transformFile, this.TableDefinitions); |
| 375 | command.Execute(); | 374 | command.Execute(); |
| 376 | 375 | ||
| 377 | if (this.Messaging.EncounteredError) | 376 | if (this.Messaging.EncounteredError) |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs index 8a7dd702..201a890c 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs | |||
| @@ -10,7 +10,6 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 10 | using WixToolset.Data.Tuples; | 10 | using WixToolset.Data.Tuples; |
| 11 | using WixToolset.Data.WindowsInstaller; | 11 | using WixToolset.Data.WindowsInstaller; |
| 12 | using WixToolset.Data.WindowsInstaller.Rows; | 12 | using WixToolset.Data.WindowsInstaller.Rows; |
| 13 | using WixToolset.Extensibility; | ||
| 14 | using WixToolset.Extensibility.Services; | 13 | using WixToolset.Extensibility.Services; |
| 15 | 14 | ||
| 16 | /// <summary> | 15 | /// <summary> |
| @@ -25,11 +24,12 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 25 | /// <summary> | 24 | /// <summary> |
| 26 | /// Instantiates a new Differ class. | 25 | /// Instantiates a new Differ class. |
| 27 | /// </summary> | 26 | /// </summary> |
| 28 | public GenerateTransformCommand(IMessaging messaging, WindowsInstallerData targetOutput, WindowsInstallerData updatedOutput, bool showPedanticMessages) | 27 | public GenerateTransformCommand(IMessaging messaging, WindowsInstallerData targetOutput, WindowsInstallerData updatedOutput, bool preserveUnchangedRows, bool showPedanticMessages) |
| 29 | { | 28 | { |
| 30 | this.messaging = messaging; | 29 | this.messaging = messaging; |
| 31 | this.TargetOutput = targetOutput; | 30 | this.TargetOutput = targetOutput; |
| 32 | this.UpdatedOutput = updatedOutput; | 31 | this.UpdatedOutput = updatedOutput; |
| 32 | this.PreserveUnchangedRows = preserveUnchangedRows; | ||
| 33 | this.ShowPedanticMessages = showPedanticMessages; | 33 | this.ShowPedanticMessages = showPedanticMessages; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| @@ -111,10 +111,10 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 111 | } | 111 | } |
| 112 | else if (TableOperation.None == operation) | 112 | else if (TableOperation.None == operation) |
| 113 | { | 113 | { |
| 114 | var modified = transform.EnsureTable(updatedTable.Definition); | 114 | var modifiedTable = transform.EnsureTable(updatedTable.Definition); |
| 115 | foreach (var row in rows) | 115 | foreach (var row in rows) |
| 116 | { | 116 | { |
| 117 | modified.Rows.Add(row); | 117 | modifiedTable.Rows.Add(row); |
| 118 | } | 118 | } |
| 119 | } | 119 | } |
| 120 | } | 120 | } |
| @@ -242,10 +242,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 242 | { | 242 | { |
| 243 | var columnDefinition = updatedRow.Fields[i].Column; | 243 | var columnDefinition = updatedRow.Fields[i].Column; |
| 244 | 244 | ||
| 245 | if (columnDefinition.Unreal) | 245 | if (!columnDefinition.PrimaryKey) |
| 246 | { | ||
| 247 | } | ||
| 248 | else if (!columnDefinition.PrimaryKey) | ||
| 249 | { | 246 | { |
| 250 | var modified = false; | 247 | var modified = false; |
| 251 | 248 | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs index 2844f797..55171da4 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs | |||
| @@ -26,28 +26,26 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 26 | var facades = new List<FileFacade>(); | 26 | var facades = new List<FileFacade>(); |
| 27 | 27 | ||
| 28 | var assemblyFile = this.Section.Tuples.OfType<AssemblyTuple>().ToDictionary(t => t.Id.Id); | 28 | var assemblyFile = this.Section.Tuples.OfType<AssemblyTuple>().ToDictionary(t => t.Id.Id); |
| 29 | //var wixFiles = this.Section.Tuples.OfType<WixFileTuple>().ToDictionary(t => t.Id.Id); | ||
| 30 | //var deltaPatchFiles = this.Section.Tuples.OfType<WixDeltaPatchFileTuple>().ToDictionary(t => t.Id.Id); | 29 | //var deltaPatchFiles = this.Section.Tuples.OfType<WixDeltaPatchFileTuple>().ToDictionary(t => t.Id.Id); |
| 31 | 30 | ||
| 32 | foreach (var file in this.Section.Tuples.OfType<FileTuple>()) | 31 | foreach (var file in this.Section.Tuples.OfType<FileTuple>()) |
| 33 | { | 32 | { |
| 34 | //var wixFile = wixFiles[file.Id.Id]; | 33 | assemblyFile.TryGetValue(file.Id.Id, out var assembly); |
| 35 | 34 | ||
| 36 | //deltaPatchFiles.TryGetValue(file.Id.Id, out var deltaPatchFile); | 35 | //deltaPatchFiles.TryGetValue(file.Id.Id, out var deltaPatchFile); |
| 37 | 36 | ||
| 38 | //facades.Add(new FileFacade(file, wixFile, deltaPatchFile)); | ||
| 39 | |||
| 40 | assemblyFile.TryGetValue(file.Id.Id, out var assembly); | ||
| 41 | |||
| 42 | facades.Add(new FileFacade(file, assembly)); | 37 | facades.Add(new FileFacade(file, assembly)); |
| 38 | //facades.Add(new FileFacade(file, wixFile, deltaPatchFile)); | ||
| 43 | } | 39 | } |
| 44 | 40 | ||
| 45 | //this.ResolveDeltaPatchSymbolPaths(deltaPatchFiles, facades); | 41 | #if TODO_PATCHING_DELTA |
| 42 | this.ResolveDeltaPatchSymbolPaths(deltaPatchFiles, facades); | ||
| 43 | #endif | ||
| 46 | 44 | ||
| 47 | this.FileFacades = facades; | 45 | this.FileFacades = facades; |
| 48 | } | 46 | } |
| 49 | 47 | ||
| 50 | #if FIX_THIS | 48 | #if TODO_PATCHING_DELTA |
| 51 | /// <summary> | 49 | /// <summary> |
| 52 | /// Merge data from the WixPatchSymbolPaths rows into the WixDeltaPatchFile rows. | 50 | /// Merge data from the WixPatchSymbolPaths rows into the WixDeltaPatchFile rows. |
| 53 | /// </summary> | 51 | /// </summary> |
| @@ -132,7 +130,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 132 | file.SymbolPaths = String.Concat(file.SymbolPaths, ";", row.SymbolPaths); | 130 | file.SymbolPaths = String.Concat(file.SymbolPaths, ";", row.SymbolPaths); |
| 133 | } | 131 | } |
| 134 | 132 | ||
| 135 | #if TODO_PATCHING | ||
| 136 | Field field = row.Fields[2]; | 133 | Field field = row.Fields[2]; |
| 137 | if (null != field.PreviousData) | 134 | if (null != field.PreviousData) |
| 138 | { | 135 | { |
| @@ -145,7 +142,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 145 | file.PreviousSymbols = String.Concat(file.PreviousSymbols, ";", field.PreviousData); | 142 | file.PreviousSymbols = String.Concat(file.PreviousSymbols, ";", field.PreviousData); |
| 146 | } | 143 | } |
| 147 | } | 144 | } |
| 148 | #endif | ||
| 149 | } | 145 | } |
| 150 | #endif | 146 | #endif |
| 151 | } | 147 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs index 9818f01a..99bf7101 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs | |||
| @@ -4,12 +4,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 4 | { | 4 | { |
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using System.Diagnostics; | ||
| 8 | using System.IO; | ||
| 9 | using System.Linq; | 7 | using System.Linq; |
| 10 | using WixToolset.Core.Bind; | 8 | using WixToolset.Core.Bind; |
| 11 | using WixToolset.Data; | 9 | using WixToolset.Data; |
| 12 | using WixToolset.Data.Tuples; | ||
| 13 | using WixToolset.Data.WindowsInstaller; | 10 | using WixToolset.Data.WindowsInstaller; |
| 14 | using WixToolset.Data.WindowsInstaller.Rows; | 11 | using WixToolset.Data.WindowsInstaller.Rows; |
| 15 | using WixToolset.Extensibility; | 12 | using WixToolset.Extensibility; |
| @@ -17,90 +14,39 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 17 | 14 | ||
| 18 | internal class GetFileFacadesFromTransforms | 15 | internal class GetFileFacadesFromTransforms |
| 19 | { | 16 | { |
| 20 | public GetFileFacadesFromTransforms(IMessaging messaging, IEnumerable<SubStorage> subStorages, TableDefinitionCollection tableDefinitions) | 17 | public GetFileFacadesFromTransforms(IMessaging messaging, FileSystemManager fileSystemManager, IEnumerable<SubStorage> subStorages) |
| 21 | { | 18 | { |
| 22 | this.Messaging = messaging; | 19 | this.Messaging = messaging; |
| 20 | this.FileSystemManager = fileSystemManager; | ||
| 23 | this.SubStorages = subStorages; | 21 | this.SubStorages = subStorages; |
| 24 | this.TableDefinitions = tableDefinitions; | ||
| 25 | } | 22 | } |
| 26 | 23 | ||
| 27 | public IEnumerable<IFileSystemExtension> Extensions { get; } | ||
| 28 | |||
| 29 | private IMessaging Messaging { get; } | 24 | private IMessaging Messaging { get; } |
| 30 | 25 | ||
| 31 | public IEnumerable<SubStorage> SubStorages { get; } | 26 | private FileSystemManager FileSystemManager { get; } |
| 32 | 27 | ||
| 33 | private TableDefinitionCollection TableDefinitions { get; } | 28 | private IEnumerable<SubStorage> SubStorages { get; } |
| 34 | 29 | ||
| 35 | public IEnumerable<FileFacade> FileFacades { get; private set; } | 30 | public IEnumerable<FileFacade> FileFacades { get; private set; } |
| 36 | 31 | ||
| 37 | public void Execute() | 32 | public void Execute() |
| 38 | { | 33 | { |
| 39 | var allFileRows = new List<FileFacade>(); | 34 | var allFileRows = new List<FileFacade>(); |
| 40 | var copyToPatch = (allFileRows != null); | ||
| 41 | #if TODO_PATCHING | ||
| 42 | var patchMediaRows = new RowDictionary<MediaRow>(); | ||
| 43 | 35 | ||
| 44 | var patchMediaFileRows = new Dictionary<int, RowDictionary<WixFileRow>>(); | 36 | var patchMediaRows = new RowDictionary<MediaRow>(); |
| 45 | 37 | ||
| 46 | var patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]); | 38 | var patchMediaFileRows = new Dictionary<int, RowDictionary<FileRow>>(); |
| 47 | var patchFileTable = this.Output.EnsureTable(this.TableDefinitions["WixFile"]); | ||
| 48 | 39 | ||
| 49 | if (copyFromPatch) | 40 | //var patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]); |
| 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 | 41 | ||
| 68 | // Index paired transforms by name without their "#" prefix. | 42 | // 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); | 43 | var pairedTransforms = this.SubStorages.Where(s => s.Name.StartsWith("#")).ToDictionary(s => s.Name, s => s.Data); |
| 70 | 44 | ||
| 71 | foreach (var substorage in this.SubStorages) | 45 | // Enumerate through main transforms. |
| 46 | foreach (var substorage in this.SubStorages.Where(s => !s.Name.StartsWith("#"))) | ||
| 72 | { | 47 | { |
| 73 | if (substorage.Name.StartsWith("#")) | ||
| 74 | { | ||
| 75 | // Skip paired transforms. | ||
| 76 | continue; | ||
| 77 | } | ||
| 78 | |||
| 79 | var mainTransform = substorage.Data; | 48 | 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"]; | 49 | 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 | 50 | ||
| 105 | if (null == mainFileTable) | 51 | if (null == mainFileTable) |
| 106 | { | 52 | { |
| @@ -108,74 +54,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 108 | } | 54 | } |
| 109 | 55 | ||
| 110 | // Index File table of pairedTransform | 56 | // Index File table of pairedTransform |
| 57 | var pairedTransform = pairedTransforms["#" + substorage.Name]; | ||
| 111 | var pairedFileRows = new RowDictionary<FileRow>(pairedTransform.Tables["File"]); | 58 | var pairedFileRows = new RowDictionary<FileRow>(pairedTransform.Tables["File"]); |
| 112 | 59 | ||
| 113 | foreach (FileRow mainFileRow in mainFileTable.Rows) | 60 | foreach (FileRow mainFileRow in mainFileTable.Rows.Where(f => f.Operation != RowOperation.Delete)) |
| 114 | { | 61 | { |
| 115 | if (RowOperation.Delete == mainFileRow.Operation) | 62 | var mainFileId = mainFileRow.File; |
| 116 | { | ||
| 117 | continue; | ||
| 118 | } | ||
| 119 | else if (RowOperation.None == mainFileRow.Operation) | ||
| 120 | { | ||
| 121 | continue; | ||
| 122 | } | ||
| 123 | 63 | ||
| 124 | var mainWixFileRow = mainWixFiles.Get(mainFileRow.File); | 64 | // We need compare the underlying files and include all file changes. |
| 65 | var objectField = (ObjectField)mainFileRow.Fields[9]; | ||
| 66 | var pairedFileRow = pairedFileRows.Get(mainFileId); | ||
| 125 | 67 | ||
| 126 | if (copyToPatch) // when copying to the patch, we need compare the underlying files and include all file changes. | 68 | // If the file is new, we always need to add it to the patch. |
| 69 | if (mainFileRow.Operation == RowOperation.Add) | ||
| 127 | { | 70 | { |
| 128 | var objectField = (ObjectField)mainWixFileRow.Fields[6]; | 71 | if (null != pairedFileRow) // RowOperation.Add |
| 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 | { | 72 | { |
| 180 | // Always patch-added, but never non-compressed. | 73 | // Always patch-added, but never non-compressed. |
| 181 | pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded; | 74 | pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded; |
| @@ -184,402 +77,93 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 184 | pairedFileRow.Operation = RowOperation.Add; | 77 | pairedFileRow.Operation = RowOperation.Add; |
| 185 | } | 78 | } |
| 186 | } | 79 | } |
| 187 | 80 | else | |
| 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 | { | 81 | { |
| 201 | if (null == patchFileRow) | 82 | // If PreviousData doesn't exist, target and upgrade layout point to the same location. No need to compare. |
| 202 | { | 83 | if (null == objectField.PreviousData) |
| 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 | { | 84 | { |
| 215 | // TODO: confirm the rest of data is identical? | 85 | if (mainFileRow.Operation == RowOperation.None) |
| 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 | { | 86 | { |
| 220 | this.Messaging.Write(ErrorMessages.SameFileIdDifferentSource(mainFileRow.SourceLineNumbers, fileId, patchFileRow.Source, mainWixFileRow.Source)); | 87 | continue; |
| 221 | } | 88 | } |
| 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 | } | 89 | } |
| 436 | else | 90 | else |
| 437 | { | 91 | { |
| 438 | sequence = (duplicateFilesSequence + installFilesSequence) / 2; | 92 | // TODO: should this entire condition be placed in the binder file manager? |
| 439 | if (installFilesSequence == sequence || duplicateFilesSequence == sequence) | 93 | if (/*(0 == (PatchAttributeType.Ignore & mainWixFileRow.PatchAttributes)) &&*/ |
| 94 | !this.FileSystemManager.CompareFiles(objectField.PreviousData.ToString(), objectField.Data.ToString())) | ||
| 440 | { | 95 | { |
| 441 | throw new WixException(ErrorMessages.InsertSequenceNoSpace(mainFileRow.SourceLineNumbers, tableName, "InstallFiles", "DuplicateFiles", patchFilesActionTuple.Action)); | 96 | // If the file is different, we need to mark the mainFileRow and pairedFileRow as modified. |
| 97 | mainFileRow.Operation = RowOperation.Modify; | ||
| 98 | if (null != pairedFileRow) | ||
| 99 | { | ||
| 100 | // Always patch-added, but never non-compressed. | ||
| 101 | pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded; | ||
| 102 | pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed; | ||
| 103 | pairedFileRow.Fields[6].Modified = true; | ||
| 104 | pairedFileRow.Operation = RowOperation.Modify; | ||
| 105 | } | ||
| 106 | } | ||
| 107 | else | ||
| 108 | { | ||
| 109 | // The File is same. We need mark all the attributes as unchanged. | ||
| 110 | mainFileRow.Operation = RowOperation.None; | ||
| 111 | foreach (var field in mainFileRow.Fields) | ||
| 112 | { | ||
| 113 | field.Modified = false; | ||
| 114 | } | ||
| 115 | |||
| 116 | if (null != pairedFileRow) | ||
| 117 | { | ||
| 118 | pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesPatchAdded; | ||
| 119 | pairedFileRow.Fields[6].Modified = false; | ||
| 120 | pairedFileRow.Operation = RowOperation.None; | ||
| 121 | } | ||
| 122 | continue; | ||
| 442 | } | 123 | } |
| 443 | } | 124 | } |
| 444 | } | 125 | } |
| 445 | else | ||
| 446 | { | ||
| 447 | sequence = installFilesSequence + 1; | ||
| 448 | } | ||
| 449 | } | ||
| 450 | 126 | ||
| 451 | var sequenceTable = pairedTransform.EnsureTable(this.TableDefinitions[tableName]); | 127 | // index patch files by diskId+fileId |
| 452 | if (0 == sequenceTable.Rows.Count) | 128 | var diskId = mainFileRow.DiskId; |
| 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 | 129 | ||
| 465 | /// <summary> | 130 | if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows)) |
| 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 | { | 131 | { |
| 481 | case "PatchFiles": | 132 | mediaFileRows = new RowDictionary<FileRow>(); |
| 482 | hasPatchFilesAction = true; | 133 | patchMediaFileRows.Add(diskId, mediaFileRows); |
| 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 | } | 134 | } |
| 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 | 135 | ||
| 512 | var componentKeyPath = new Dictionary<string, string>(componentTable.Rows.Count); | 136 | var patchFileRow = mediaFileRows.Get(mainFileId); |
| 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 | 137 | ||
| 524 | var componentWithChangedKeyPath = new Dictionary<string, string>(); | 138 | if (null == patchFileRow) |
| 525 | var componentWithNonKeyPathChanged = new Dictionary<string, string>(); | 139 | { |
| 526 | // Verify changes in the file table, now that file diffing has occurred | 140 | //patchFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); |
| 527 | foreach (FileRow row in fileTable.Rows) | 141 | patchFileRow = (FileRow)mainFileRow.TableDefinition.CreateRow(mainFileRow.SourceLineNumbers); |
| 528 | { | 142 | mainFileRow.CopyTo(patchFileRow); |
| 529 | if (RowOperation.Modify != row.Operation) | ||
| 530 | { | ||
| 531 | continue; | ||
| 532 | } | ||
| 533 | 143 | ||
| 534 | var fileId = row.FieldAsString(0); | 144 | mediaFileRows.Add(patchFileRow); |
| 535 | var componentId = row.FieldAsString(1); | ||
| 536 | 145 | ||
| 537 | // If this file is the keypath of a component | 146 | allFileRows.Add(new FileFacade(patchFileRow)); // TODO: should we be passing along delta information? Probably, right? |
| 538 | if (componentKeyPath.ContainsValue(fileId)) | ||
| 539 | { | ||
| 540 | if (!componentWithChangedKeyPath.ContainsKey(componentId)) | ||
| 541 | { | ||
| 542 | componentWithChangedKeyPath.Add(componentId, fileId); | ||
| 543 | } | 147 | } |
| 544 | } | 148 | else |
| 545 | else | ||
| 546 | { | ||
| 547 | if (!componentWithNonKeyPathChanged.ContainsKey(componentId)) | ||
| 548 | { | 149 | { |
| 549 | componentWithNonKeyPathChanged.Add(componentId, fileId); | 150 | // TODO: confirm the rest of data is identical? |
| 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 | 151 | ||
| 564 | private bool CompareFiles(string targetFile, string updatedFile) | 152 | // make sure Source is same. Otherwise we are silently ignoring a file. |
| 565 | { | 153 | if (0 != String.Compare(patchFileRow.Source, mainFileRow.Source, StringComparison.OrdinalIgnoreCase)) |
| 566 | bool? compared = null; | 154 | { |
| 567 | foreach (var extension in this.Extensions) | 155 | this.Messaging.Write(ErrorMessages.SameFileIdDifferentSource(mainFileRow.SourceLineNumbers, mainFileId, patchFileRow.Source, mainFileRow.Source)); |
| 568 | { | 156 | } |
| 569 | compared = extension.CompareFiles(targetFile, updatedFile); | ||
| 570 | 157 | ||
| 571 | if (compared.HasValue) | 158 | #if TODO_PATCHING_DELTA |
| 572 | { | 159 | // capture the previous file versions (and associated data) from this targeted instance of the baseline into the current filerow. |
| 573 | break; | 160 | patchFileRow.AppendPreviousDataFrom(mainFileRow); |
| 161 | #endif | ||
| 162 | } | ||
| 574 | } | 163 | } |
| 575 | } | 164 | } |
| 576 | 165 | ||
| 577 | if (!compared.HasValue) | 166 | this.FileFacades = allFileRows; |
| 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 | } | 167 | } |
| 584 | } | 168 | } |
| 585 | } | 169 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs index cd6170d0..bddcccb7 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs | |||
| @@ -12,6 +12,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 12 | using WixToolset.Core.Native; | 12 | using WixToolset.Core.Native; |
| 13 | using WixToolset.Core.WindowsInstaller.Msi; | 13 | using WixToolset.Core.WindowsInstaller.Msi; |
| 14 | using WixToolset.Data; | 14 | using WixToolset.Data; |
| 15 | using WixToolset.Data.Tuples; | ||
| 15 | using WixToolset.Data.WindowsInstaller; | 16 | using WixToolset.Data.WindowsInstaller; |
| 16 | using WixToolset.Data.WindowsInstaller.Rows; | 17 | using WixToolset.Data.WindowsInstaller.Rows; |
| 17 | using WixToolset.Extensibility.Services; | 18 | using WixToolset.Extensibility.Services; |
| @@ -34,7 +35,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 34 | 35 | ||
| 35 | public string OutputPath { private get; set; } | 36 | public string OutputPath { private get; set; } |
| 36 | 37 | ||
| 37 | public IEnumerable<string> SuppressedTableNames { private get; set; } | 38 | public TableDefinitionCollection TableDefinitions { private get; set; } |
| 38 | 39 | ||
| 39 | public string IntermediateFolder { private get; set; } | 40 | public string IntermediateFolder { private get; set; } |
| 40 | 41 | ||
| @@ -49,6 +50,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 49 | return; | 50 | return; |
| 50 | } | 51 | } |
| 51 | 52 | ||
| 53 | var suppressedTableNames = this.AddBackSuppresedSequenceTables(); | ||
| 54 | |||
| 52 | IMsmMerge2 merge = null; | 55 | IMsmMerge2 merge = null; |
| 53 | bool commit = true; | 56 | bool commit = true; |
| 54 | bool logOpen = false; | 57 | bool logOpen = false; |
| @@ -212,9 +215,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 212 | return; | 215 | return; |
| 213 | } | 216 | } |
| 214 | 217 | ||
| 215 | using (Database db = new Database(this.OutputPath, OpenDatabase.Direct)) | 218 | using (var db = new Database(this.OutputPath, OpenDatabase.Direct)) |
| 216 | { | 219 | { |
| 217 | Table suppressActionTable = this.Output.Tables["WixSuppressAction"]; | 220 | var suppressActionTable = this.Output.Tables["WixSuppressAction"]; |
| 218 | 221 | ||
| 219 | // suppress individual actions | 222 | // suppress individual actions |
| 220 | if (null != suppressActionTable) | 223 | if (null != suppressActionTable) |
| @@ -239,40 +242,38 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 239 | } | 242 | } |
| 240 | 243 | ||
| 241 | // query for merge module actions in suppressed sequences and drop them | 244 | // query for merge module actions in suppressed sequences and drop them |
| 242 | foreach (string tableName in this.SuppressedTableNames) | 245 | foreach (var tableName in suppressedTableNames) |
| 243 | { | 246 | { |
| 244 | if (!db.TableExists(tableName)) | 247 | if (!db.TableExists(tableName)) |
| 245 | { | 248 | { |
| 246 | continue; | 249 | continue; |
| 247 | } | 250 | } |
| 248 | 251 | ||
| 249 | using (View view = db.OpenExecuteView(String.Concat("SELECT `Action` FROM ", tableName))) | 252 | using (var view = db.OpenExecuteView(String.Concat("SELECT `Action` FROM ", tableName))) |
| 250 | { | 253 | { |
| 251 | foreach (Record resultRecord in view.Records) | 254 | foreach (var resultRecord in view.Records) |
| 252 | { | 255 | { |
| 253 | this.Messaging.Write(WarningMessages.SuppressMergedAction(resultRecord.GetString(1), tableName)); | 256 | this.Messaging.Write(WarningMessages.SuppressMergedAction(resultRecord.GetString(1), tableName)); |
| 254 | } | 257 | } |
| 255 | } | 258 | } |
| 256 | 259 | ||
| 257 | // drop suppressed sequences | 260 | // drop suppressed sequences |
| 258 | using (View view = db.OpenExecuteView(String.Concat("DROP TABLE ", tableName))) | 261 | using (var view = db.OpenExecuteView(String.Concat("DROP TABLE ", tableName))) |
| 259 | { | 262 | { |
| 260 | } | 263 | } |
| 261 | 264 | ||
| 262 | // delete the validation rows | 265 | // delete the validation rows |
| 263 | using (View view = db.OpenView(String.Concat("DELETE FROM _Validation WHERE `Table` = ?"))) | 266 | using (var view = db.OpenView(String.Concat("DELETE FROM _Validation WHERE `Table` = ?"))) |
| 267 | using (var record = new Record(1)) | ||
| 264 | { | 268 | { |
| 265 | using (Record record = new Record(1)) | 269 | record.SetString(1, tableName); |
| 266 | { | 270 | view.Execute(record); |
| 267 | record.SetString(1, tableName); | ||
| 268 | view.Execute(record); | ||
| 269 | } | ||
| 270 | } | 271 | } |
| 271 | } | 272 | } |
| 272 | 273 | ||
| 273 | // now update the Attributes column for the files from the Merge Modules | 274 | // now update the Attributes column for the files from the Merge Modules |
| 274 | this.Messaging.Write(VerboseMessages.ResequencingMergeModuleFiles()); | 275 | this.Messaging.Write(VerboseMessages.ResequencingMergeModuleFiles()); |
| 275 | using (View view = db.OpenView("SELECT `Sequence`, `Attributes` FROM `File` WHERE `File`=?")) | 276 | using (var view = db.OpenView("SELECT `Sequence`, `Attributes` FROM `File` WHERE `File`=?")) |
| 276 | { | 277 | { |
| 277 | foreach (var file in this.FileFacades) | 278 | foreach (var file in this.FileFacades) |
| 278 | { | 279 | { |
| @@ -281,13 +282,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 281 | continue; | 282 | continue; |
| 282 | } | 283 | } |
| 283 | 284 | ||
| 284 | using (Record record = new Record(1)) | 285 | using (var record = new Record(1)) |
| 285 | { | 286 | { |
| 286 | record.SetString(1, file.Id); | 287 | record.SetString(1, file.Id); |
| 287 | view.Execute(record); | 288 | view.Execute(record); |
| 288 | } | 289 | } |
| 289 | 290 | ||
| 290 | using (Record recordUpdate = view.Fetch()) | 291 | using (var recordUpdate = view.Fetch()) |
| 291 | { | 292 | { |
| 292 | if (null == recordUpdate) | 293 | if (null == recordUpdate) |
| 293 | { | 294 | { |
| @@ -332,5 +333,31 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 332 | db.Commit(); | 333 | db.Commit(); |
| 333 | } | 334 | } |
| 334 | } | 335 | } |
| 336 | |||
| 337 | private IEnumerable<string> AddBackSuppresedSequenceTables() | ||
| 338 | { | ||
| 339 | // Add back possibly suppressed sequence tables since all sequence tables must be present | ||
| 340 | // for the merge process to work. We'll drop the suppressed sequence tables again as | ||
| 341 | // necessary. | ||
| 342 | var suppressedTableNames = new HashSet<string>(); | ||
| 343 | |||
| 344 | foreach (SequenceTable sequence in Enum.GetValues(typeof(SequenceTable))) | ||
| 345 | { | ||
| 346 | var sequenceTableName = (sequence == SequenceTable.AdvertiseExecuteSequence) ? "AdvtExecuteSequence" : sequence.ToString(); | ||
| 347 | var sequenceTable = this.Output.Tables[sequenceTableName]; | ||
| 348 | |||
| 349 | if (null == sequenceTable) | ||
| 350 | { | ||
| 351 | sequenceTable = this.Output.EnsureTable(this.TableDefinitions[sequenceTableName]); | ||
| 352 | } | ||
| 353 | |||
| 354 | if (0 == sequenceTable.Rows.Count) | ||
| 355 | { | ||
| 356 | suppressedTableNames.Add(sequenceTableName); | ||
| 357 | } | ||
| 358 | } | ||
| 359 | |||
| 360 | return suppressedTableNames; | ||
| 361 | } | ||
| 335 | } | 362 | } |
| 336 | } | 363 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/PatchTransform.cs b/src/WixToolset.Core.WindowsInstaller/Bind/PatchTransform.cs index 5ada29de..4d849753 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/PatchTransform.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/PatchTransform.cs | |||
| @@ -2,14 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | namespace WixToolset.Core.WindowsInstaller.Bind | 3 | namespace WixToolset.Core.WindowsInstaller.Bind |
| 4 | { | 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; | 5 | using WixToolset.Data.WindowsInstaller; |
| 12 | using WixToolset.Extensibility; | ||
| 13 | 6 | ||
| 14 | internal class PatchTransform | 7 | internal class PatchTransform |
| 15 | { | 8 | { |
| @@ -22,225 +15,5 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 22 | public string Baseline { get; } | 15 | public string Baseline { get; } |
| 23 | 16 | ||
| 24 | public WindowsInstallerData Transform { get; } | 17 | 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 | } | 18 | } |
| 246 | } | 19 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs index 4ca5ec48..63a8b3d9 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs | |||
| @@ -39,20 +39,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 39 | 39 | ||
| 40 | public void Execute() | 40 | public void Execute() |
| 41 | { | 41 | { |
| 42 | var assemblyNameTuples = this.Section.Tuples.OfType<MsiAssemblyNameTuple>().ToDictionary(t => t.Id.Id); | ||
| 43 | |||
| 42 | foreach (var file in this.UpdateFileFacades) | 44 | foreach (var file in this.UpdateFileFacades) |
| 43 | { | 45 | { |
| 44 | this.UpdateFileFacade(file); | 46 | this.UpdateFileFacade(file, assemblyNameTuples); |
| 45 | } | 47 | } |
| 46 | } | 48 | } |
| 47 | 49 | ||
| 48 | private void UpdateFileFacade(FileFacade facade) | 50 | private void UpdateFileFacade(FileFacade facade, Dictionary<string, MsiAssemblyNameTuple> assemblyNameTuples) |
| 49 | { | 51 | { |
| 50 | var assemblyNameTuples = new Dictionary<string, MsiAssemblyNameTuple>(); | ||
| 51 | foreach (var assemblyTuple in this.Section.Tuples.OfType<MsiAssemblyNameTuple>()) | ||
| 52 | { | ||
| 53 | assemblyNameTuples.Add(assemblyTuple.ComponentRef + "/" + assemblyTuple.Name, assemblyTuple); | ||
| 54 | } | ||
| 55 | |||
| 56 | FileInfo fileInfo = null; | 52 | FileInfo fileInfo = null; |
| 57 | try | 53 | try |
| 58 | { | 54 | { |
| @@ -335,7 +331,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 335 | var lookup = String.Concat(facade.ComponentRef, "/", name); | 331 | var lookup = String.Concat(facade.ComponentRef, "/", name); |
| 336 | if (!assemblyNameTuples.TryGetValue(lookup, out var assemblyNameTuple)) | 332 | if (!assemblyNameTuples.TryGetValue(lookup, out var assemblyNameTuple)) |
| 337 | { | 333 | { |
| 338 | assemblyNameTuple = this.Section.AddTuple(new MsiAssemblyNameTuple(facade.SourceLineNumber) | 334 | assemblyNameTuple = this.Section.AddTuple(new MsiAssemblyNameTuple(facade.SourceLineNumber, new Identifier(AccessModifier.Private, facade.ComponentRef, name)) |
| 339 | { | 335 | { |
| 340 | ComponentRef = facade.ComponentRef, | 336 | ComponentRef = facade.ComponentRef, |
| 341 | Name = name, | 337 | Name = name, |
| @@ -348,6 +344,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 348 | } | 344 | } |
| 349 | 345 | ||
| 350 | facade.AssemblyNames.Add(assemblyNameTuple); | 346 | facade.AssemblyNames.Add(assemblyNameTuple); |
| 347 | |||
| 348 | assemblyNameTuples.Add(assemblyNameTuple.Id.Id, assemblyNameTuple); | ||
| 351 | } | 349 | } |
| 352 | 350 | ||
| 353 | assemblyNameTuple.Value = value; | 351 | assemblyNameTuple.Value = value; |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs index 5d18a230..9aab7b98 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs | |||
| @@ -6,62 +6,59 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 6 | using System.Linq; | 6 | using System.Linq; |
| 7 | using WixToolset.Core.Bind; | 7 | using WixToolset.Core.Bind; |
| 8 | using WixToolset.Data; | 8 | using WixToolset.Data; |
| 9 | using WixToolset.Data.WindowsInstaller; | 9 | using WixToolset.Data.Tuples; |
| 10 | using WixToolset.Data.WindowsInstaller.Rows; | ||
| 11 | 10 | ||
| 12 | internal class UpdateMediaSequencesCommand | 11 | internal class UpdateMediaSequencesCommand |
| 13 | { | 12 | { |
| 14 | public UpdateMediaSequencesCommand(WindowsInstallerData output, List<FileFacade> fileFacades) | 13 | public UpdateMediaSequencesCommand(IntermediateSection section, List<FileFacade> fileFacades) |
| 15 | { | 14 | { |
| 16 | this.Output = output; | 15 | this.Section = section; |
| 17 | this.FileFacades = fileFacades; | 16 | this.FileFacades = fileFacades; |
| 18 | } | 17 | } |
| 19 | 18 | ||
| 20 | private WindowsInstallerData Output { get; } | 19 | private IntermediateSection Section { get; } |
| 21 | 20 | ||
| 22 | private List<FileFacade> FileFacades { get; } | 21 | private List<FileFacade> FileFacades { get; } |
| 23 | 22 | ||
| 24 | public void Execute() | 23 | public void Execute() |
| 25 | { | 24 | { |
| 26 | var fileRows = new RowDictionary<FileRow>(this.Output.Tables["File"]); | 25 | var mediaRows = this.Section.Tuples.OfType<MediaTuple>().ToDictionary(t => t.DiskId); |
| 27 | var mediaRows = new RowDictionary<MediaRow>(this.Output.Tables["Media"]); | ||
| 28 | 26 | ||
| 29 | // Calculate sequence numbers and media disk id layout for all file media information objects. | 27 | // Calculate sequence numbers and media disk id layout for all file media information objects. |
| 30 | if (OutputType.Module == this.Output.Type) | 28 | if (SectionType.Module == this.Section.Type) |
| 31 | { | 29 | { |
| 32 | var lastSequence = 0; | 30 | var lastSequence = 0; |
| 33 | 31 | ||
| 34 | // Order by Component to group the files by directory. | 32 | // Order by Component to group the files by directory. |
| 35 | var optimized = this.OptimizedFileFacades(); | 33 | var optimized = this.OptimizedFileFacades(); |
| 36 | foreach (var fileId in optimized.Select(f => f.Id)) | 34 | foreach (var facade in optimized) |
| 37 | { | 35 | { |
| 38 | var fileRow = fileRows.Get(fileId); | 36 | facade.Sequence = ++lastSequence; |
| 39 | fileRow.Sequence = ++lastSequence; | ||
| 40 | } | 37 | } |
| 41 | } | 38 | } |
| 42 | else | 39 | else |
| 43 | { | 40 | { |
| 44 | var lastSequence = 0; | 41 | var lastSequence = 0; |
| 45 | MediaRow mediaRow = null; | 42 | MediaTuple mediaTuple = null; |
| 46 | var patchGroups = new Dictionary<int, List<FileFacade>>(); | 43 | var patchGroups = new Dictionary<int, List<FileFacade>>(); |
| 47 | 44 | ||
| 48 | // sequence the non-patch-added files | 45 | // sequence the non-patch-added files |
| 49 | var optimized = this.OptimizedFileFacades(); | 46 | var optimized = this.OptimizedFileFacades(); |
| 50 | foreach (var facade in optimized) | 47 | foreach (var facade in optimized) |
| 51 | { | 48 | { |
| 52 | if (null == mediaRow) | 49 | if (null == mediaTuple) |
| 53 | { | 50 | { |
| 54 | mediaRow = mediaRows.Get(facade.DiskId); | 51 | mediaTuple = mediaRows[facade.DiskId]; |
| 55 | if (OutputType.Patch == this.Output.Type) | 52 | if (SectionType.Patch == this.Section.Type) |
| 56 | { | 53 | { |
| 57 | // patch Media cannot start at zero | 54 | // patch Media cannot start at zero |
| 58 | lastSequence = mediaRow.LastSequence; | 55 | lastSequence = mediaTuple.LastSequence ?? 1; |
| 59 | } | 56 | } |
| 60 | } | 57 | } |
| 61 | else if (mediaRow.DiskId != facade.DiskId) | 58 | else if (mediaTuple.DiskId != facade.DiskId) |
| 62 | { | 59 | { |
| 63 | mediaRow.LastSequence = lastSequence; | 60 | mediaTuple.LastSequence = lastSequence; |
| 64 | mediaRow = mediaRows.Get(facade.DiskId); | 61 | mediaTuple = mediaRows[facade.DiskId]; |
| 65 | } | 62 | } |
| 66 | 63 | ||
| 67 | if (facade.PatchGroup.HasValue) | 64 | if (facade.PatchGroup.HasValue) |
| @@ -76,15 +73,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 76 | } | 73 | } |
| 77 | else if (!facade.FromModule) | 74 | else if (!facade.FromModule) |
| 78 | { | 75 | { |
| 79 | var fileRow = fileRows.Get(facade.Id); | 76 | facade.Sequence = ++lastSequence; |
| 80 | fileRow.Sequence = ++lastSequence; | ||
| 81 | } | 77 | } |
| 82 | } | 78 | } |
| 83 | 79 | ||
| 84 | if (null != mediaRow) | 80 | if (null != mediaTuple) |
| 85 | { | 81 | { |
| 86 | mediaRow.LastSequence = lastSequence; | 82 | mediaTuple.LastSequence = lastSequence; |
| 87 | mediaRow = null; | 83 | mediaTuple = null; |
| 88 | } | 84 | } |
| 89 | 85 | ||
| 90 | // sequence the patch-added files | 86 | // sequence the patch-added files |
| @@ -92,24 +88,23 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 92 | { | 88 | { |
| 93 | foreach (var facade in patchGroup) | 89 | foreach (var facade in patchGroup) |
| 94 | { | 90 | { |
| 95 | if (null == mediaRow) | 91 | if (null == mediaTuple) |
| 96 | { | 92 | { |
| 97 | mediaRow = mediaRows.Get(facade.DiskId); | 93 | mediaTuple = mediaRows[facade.DiskId]; |
| 98 | } | 94 | } |
| 99 | else if (mediaRow.DiskId != facade.DiskId) | 95 | else if (mediaTuple.DiskId != facade.DiskId) |
| 100 | { | 96 | { |
| 101 | mediaRow.LastSequence = lastSequence; | 97 | mediaTuple.LastSequence = lastSequence; |
| 102 | mediaRow = mediaRows.Get(facade.DiskId); | 98 | mediaTuple = mediaRows[facade.DiskId]; |
| 103 | } | 99 | } |
| 104 | 100 | ||
| 105 | var fileRow = fileRows.Get(facade.Id); | 101 | facade.Sequence = ++lastSequence; |
| 106 | fileRow.Sequence = ++lastSequence; | ||
| 107 | } | 102 | } |
| 108 | } | 103 | } |
| 109 | 104 | ||
| 110 | if (null != mediaRow) | 105 | if (null != mediaTuple) |
| 111 | { | 106 | { |
| 112 | mediaRow.LastSequence = lastSequence; | 107 | mediaTuple.LastSequence = lastSequence; |
| 113 | } | 108 | } |
| 114 | } | 109 | } |
| 115 | } | 110 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateTransformsWithFileFacades.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateTransformsWithFileFacades.cs new file mode 100644 index 00000000..af2e8f85 --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateTransformsWithFileFacades.cs | |||
| @@ -0,0 +1,453 @@ | |||
| 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.Services; | ||
| 16 | |||
| 17 | internal class UpdateTransformsWithFileFacades | ||
| 18 | { | ||
| 19 | public UpdateTransformsWithFileFacades(IMessaging messaging, WindowsInstallerData output, IEnumerable<SubStorage> subStorages, TableDefinitionCollection tableDefinitions, IEnumerable<FileFacade> fileFacades) | ||
| 20 | { | ||
| 21 | this.Messaging = messaging; | ||
| 22 | this.Output = output; | ||
| 23 | this.SubStorages = subStorages; | ||
| 24 | this.TableDefinitions = tableDefinitions; | ||
| 25 | this.FileFacades = fileFacades; | ||
| 26 | } | ||
| 27 | |||
| 28 | private IMessaging Messaging { get; } | ||
| 29 | |||
| 30 | private WindowsInstallerData Output { get; } | ||
| 31 | |||
| 32 | private IEnumerable<SubStorage> SubStorages { get; } | ||
| 33 | |||
| 34 | private TableDefinitionCollection TableDefinitions { get; } | ||
| 35 | |||
| 36 | private IEnumerable<FileFacade> FileFacades { get; } | ||
| 37 | |||
| 38 | public void Execute() | ||
| 39 | { | ||
| 40 | var fileFacadesByDiskId = new Dictionary<int, Dictionary<string, FileFacade>>(); | ||
| 41 | |||
| 42 | // Index patch file facades by diskId+fileId. | ||
| 43 | foreach (var facade in this.FileFacades) | ||
| 44 | { | ||
| 45 | if (!fileFacadesByDiskId.TryGetValue(facade.DiskId, out var mediaFacades)) | ||
| 46 | { | ||
| 47 | mediaFacades = new Dictionary<string, FileFacade>(); | ||
| 48 | fileFacadesByDiskId.Add(facade.DiskId, mediaFacades); | ||
| 49 | } | ||
| 50 | |||
| 51 | mediaFacades.Add(facade.Id, facade); | ||
| 52 | } | ||
| 53 | |||
| 54 | var patchMediaRows = new RowDictionary<MediaRow>(this.Output.Tables["Media"]); | ||
| 55 | |||
| 56 | // Index paired transforms by name without the "#" prefix. | ||
| 57 | var pairedTransforms = this.SubStorages.Where(s => s.Name.StartsWith("#")).ToDictionary(s => s.Name, s => s.Data); | ||
| 58 | |||
| 59 | // Copy File bind data into substorages | ||
| 60 | foreach (var substorage in this.SubStorages.Where(s => !s.Name.StartsWith("#"))) | ||
| 61 | { | ||
| 62 | var mainTransform = substorage.Data; | ||
| 63 | |||
| 64 | var mainMsiFileHashIndex = new RowDictionary<Row>(mainTransform.Tables["MsiFileHash"]); | ||
| 65 | |||
| 66 | var pairedTransform = pairedTransforms["#" + substorage.Name]; | ||
| 67 | |||
| 68 | // Copy Media.LastSequence. | ||
| 69 | var pairedMediaTable = pairedTransform.Tables["Media"]; | ||
| 70 | foreach (MediaRow pairedMediaRow in pairedMediaTable.Rows) | ||
| 71 | { | ||
| 72 | var patchMediaRow = patchMediaRows.Get(pairedMediaRow.DiskId); | ||
| 73 | pairedMediaRow.LastSequence = patchMediaRow.LastSequence; | ||
| 74 | } | ||
| 75 | |||
| 76 | // Validate file row changes for keypath-related issues | ||
| 77 | this.ValidateFileRowChanges(mainTransform); | ||
| 78 | |||
| 79 | // Index File table of pairedTransform | ||
| 80 | var pairedFileRows = new RowDictionary<FileRow>(pairedTransform.Tables["File"]); | ||
| 81 | |||
| 82 | var mainFileTable = mainTransform.Tables["File"]; | ||
| 83 | if (null != mainFileTable) | ||
| 84 | { | ||
| 85 | // Remove the MsiFileHash table because it will be updated later with the final file hash for each file | ||
| 86 | mainTransform.Tables.Remove("MsiFileHash"); | ||
| 87 | |||
| 88 | foreach (FileRow mainFileRow in mainFileTable.Rows) | ||
| 89 | { | ||
| 90 | if (RowOperation.Delete == mainFileRow.Operation) | ||
| 91 | { | ||
| 92 | continue; | ||
| 93 | } | ||
| 94 | else if (RowOperation.None == mainFileRow.Operation) | ||
| 95 | { | ||
| 96 | continue; | ||
| 97 | } | ||
| 98 | |||
| 99 | // Index patch files by diskId+fileId | ||
| 100 | if (!fileFacadesByDiskId.TryGetValue(mainFileRow.DiskId, out var mediaFacades)) | ||
| 101 | { | ||
| 102 | mediaFacades = new Dictionary<string, FileFacade>(); | ||
| 103 | fileFacadesByDiskId.Add(mainFileRow.DiskId, mediaFacades); | ||
| 104 | } | ||
| 105 | |||
| 106 | // copy data from the patch back to the transform | ||
| 107 | if (mediaFacades.TryGetValue(mainFileRow.File, out var facade)) | ||
| 108 | { | ||
| 109 | var patchFileRow = facade.GetFileRow(); | ||
| 110 | var pairedFileRow = pairedFileRows.Get(mainFileRow.File); | ||
| 111 | |||
| 112 | for (var i = 0; i < patchFileRow.Fields.Length; i++) | ||
| 113 | { | ||
| 114 | var patchValue = patchFileRow.FieldAsString(i) ?? String.Empty; | ||
| 115 | var mainValue = mainFileRow.FieldAsString(i) ?? String.Empty; | ||
| 116 | |||
| 117 | if (1 == i) | ||
| 118 | { | ||
| 119 | // File.Component_ changes should not come from the shared file rows | ||
| 120 | // that contain the file information as each individual transform might | ||
| 121 | // have different changes (or no changes at all). | ||
| 122 | } | ||
| 123 | else if (6 == i) // File.Attributes should not changed for binary deltas | ||
| 124 | { | ||
| 125 | #if TODO_PATCHING_DELTA | ||
| 126 | if (null != patchFileRow.Patch) | ||
| 127 | { | ||
| 128 | // File.Attribute should not change for binary deltas | ||
| 129 | pairedFileRow.Attributes = mainFileRow.Attributes; | ||
| 130 | mainFileRow.Fields[i].Modified = false; | ||
| 131 | } | ||
| 132 | #endif | ||
| 133 | } | ||
| 134 | else if (7 == i) // File.Sequence is updated in pairedTransform, not mainTransform | ||
| 135 | { | ||
| 136 | // file sequence is updated in Patch table instead of File table for delta patches | ||
| 137 | #if TODO_PATCHING_DELTA | ||
| 138 | if (null != patchFileRow.Patch) | ||
| 139 | { | ||
| 140 | pairedFileRow.Fields[i].Modified = false; | ||
| 141 | } | ||
| 142 | else | ||
| 143 | #endif | ||
| 144 | { | ||
| 145 | pairedFileRow[i] = patchFileRow[i]; | ||
| 146 | pairedFileRow.Fields[i].Modified = true; | ||
| 147 | } | ||
| 148 | mainFileRow.Fields[i].Modified = false; | ||
| 149 | } | ||
| 150 | else if (patchValue != mainValue) | ||
| 151 | { | ||
| 152 | mainFileRow[i] = patchFileRow[i]; | ||
| 153 | mainFileRow.Fields[i].Modified = true; | ||
| 154 | if (mainFileRow.Operation == RowOperation.None) | ||
| 155 | { | ||
| 156 | mainFileRow.Operation = RowOperation.Modify; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | // Copy MsiFileHash row for this File. | ||
| 162 | if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out var patchHashRow)) | ||
| 163 | { | ||
| 164 | //patchHashRow = patchFileRow.Hash; | ||
| 165 | throw new NotImplementedException(); | ||
| 166 | } | ||
| 167 | |||
| 168 | if (null != patchHashRow) | ||
| 169 | { | ||
| 170 | var mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]); | ||
| 171 | var mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
| 172 | for (var i = 0; i < patchHashRow.Fields.Length; i++) | ||
| 173 | { | ||
| 174 | mainHashRow[i] = patchHashRow[i]; | ||
| 175 | if (i > 1) | ||
| 176 | { | ||
| 177 | // assume all hash fields have been modified | ||
| 178 | mainHashRow.Fields[i].Modified = true; | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | // assume the MsiFileHash operation follows the File one | ||
| 183 | mainHashRow.Operation = mainFileRow.Operation; | ||
| 184 | } | ||
| 185 | |||
| 186 | // copy MsiAssemblyName rows for this File | ||
| 187 | #if TODO_PATCHING | ||
| 188 | List<Row> patchAssemblyNameRows = patchFileRow.AssemblyNames; | ||
| 189 | if (null != patchAssemblyNameRows) | ||
| 190 | { | ||
| 191 | var mainAssemblyNameTable = mainTransform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); | ||
| 192 | foreach (var patchAssemblyNameRow in patchAssemblyNameRows) | ||
| 193 | { | ||
| 194 | // Copy if there isn't an identical modified/added row already in the transform. | ||
| 195 | var foundMatchingModifiedRow = false; | ||
| 196 | foreach (var mainAssemblyNameRow in mainAssemblyNameTable.Rows) | ||
| 197 | { | ||
| 198 | if (RowOperation.None != mainAssemblyNameRow.Operation && mainAssemblyNameRow.GetPrimaryKey('/').Equals(patchAssemblyNameRow.GetPrimaryKey('/'))) | ||
| 199 | { | ||
| 200 | foundMatchingModifiedRow = true; | ||
| 201 | break; | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | if (!foundMatchingModifiedRow) | ||
| 206 | { | ||
| 207 | var mainAssemblyNameRow = mainAssemblyNameTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
| 208 | for (var i = 0; i < patchAssemblyNameRow.Fields.Length; i++) | ||
| 209 | { | ||
| 210 | mainAssemblyNameRow[i] = patchAssemblyNameRow[i]; | ||
| 211 | } | ||
| 212 | |||
| 213 | // assume value field has been modified | ||
| 214 | mainAssemblyNameRow.Fields[2].Modified = true; | ||
| 215 | mainAssemblyNameRow.Operation = mainFileRow.Operation; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | } | ||
| 219 | #endif | ||
| 220 | |||
| 221 | // Add patch header for this file | ||
| 222 | #if TODO_PATCHING_DELTA | ||
| 223 | if (null != patchFileRow.Patch) | ||
| 224 | { | ||
| 225 | // Add the PatchFiles action automatically to the AdminExecuteSequence and InstallExecuteSequence tables. | ||
| 226 | this.AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow); | ||
| 227 | this.AddPatchFilesActionToSequenceTable(SequenceTable.InstallExecuteSequence, mainTransform, pairedTransform, mainFileRow); | ||
| 228 | |||
| 229 | // Add to Patch table | ||
| 230 | var patchTable = pairedTransform.EnsureTable(this.TableDefinitions["Patch"]); | ||
| 231 | if (0 == patchTable.Rows.Count) | ||
| 232 | { | ||
| 233 | patchTable.Operation = TableOperation.Add; | ||
| 234 | } | ||
| 235 | |||
| 236 | var patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
| 237 | patchRow[0] = patchFileRow.File; | ||
| 238 | patchRow[1] = patchFileRow.Sequence; | ||
| 239 | |||
| 240 | var patchFile = new FileInfo(patchFileRow.Source); | ||
| 241 | patchRow[2] = (int)patchFile.Length; | ||
| 242 | patchRow[3] = 0 == (PatchAttributeType.AllowIgnoreOnError & patchFileRow.PatchAttributes) ? 0 : 1; | ||
| 243 | |||
| 244 | var streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1]; | ||
| 245 | if (Msi.MsiInterop.MsiMaxStreamNameLength < streamName.Length) | ||
| 246 | { | ||
| 247 | streamName = "_" + Guid.NewGuid().ToString("D").ToUpperInvariant().Replace('-', '_'); | ||
| 248 | |||
| 249 | var patchHeadersTable = pairedTransform.EnsureTable(this.TableDefinitions["MsiPatchHeaders"]); | ||
| 250 | if (0 == patchHeadersTable.Rows.Count) | ||
| 251 | { | ||
| 252 | patchHeadersTable.Operation = TableOperation.Add; | ||
| 253 | } | ||
| 254 | |||
| 255 | var patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
| 256 | patchHeadersRow[0] = streamName; | ||
| 257 | patchHeadersRow[1] = patchFileRow.Patch; | ||
| 258 | patchRow[5] = streamName; | ||
| 259 | patchHeadersRow.Operation = RowOperation.Add; | ||
| 260 | } | ||
| 261 | else | ||
| 262 | { | ||
| 263 | patchRow[4] = patchFileRow.Patch; | ||
| 264 | } | ||
| 265 | patchRow.Operation = RowOperation.Add; | ||
| 266 | } | ||
| 267 | #endif | ||
| 268 | } | ||
| 269 | else | ||
| 270 | { | ||
| 271 | // TODO: throw because all transform rows should have made it into the patch | ||
| 272 | } | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | this.Output.Tables.Remove("Media"); | ||
| 277 | this.Output.Tables.Remove("File"); | ||
| 278 | this.Output.Tables.Remove("MsiFileHash"); | ||
| 279 | this.Output.Tables.Remove("MsiAssemblyName"); | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | /// <summary> | ||
| 284 | /// Adds the PatchFiles action to the sequence table if it does not already exist. | ||
| 285 | /// </summary> | ||
| 286 | /// <param name="table">The sequence table to check or modify.</param> | ||
| 287 | /// <param name="mainTransform">The primary authoring transform.</param> | ||
| 288 | /// <param name="pairedTransform">The secondary patch transform.</param> | ||
| 289 | /// <param name="mainFileRow">The file row that contains information about the patched file.</param> | ||
| 290 | private void AddPatchFilesActionToSequenceTable(SequenceTable table, WindowsInstallerData mainTransform, WindowsInstallerData pairedTransform, Row mainFileRow) | ||
| 291 | { | ||
| 292 | var tableName = table.ToString(); | ||
| 293 | |||
| 294 | // Find/add PatchFiles action (also determine sequence for it). | ||
| 295 | // Search mainTransform first, then pairedTransform (pairedTransform overrides). | ||
| 296 | var hasPatchFilesAction = false; | ||
| 297 | var installFilesSequence = 0; | ||
| 298 | var duplicateFilesSequence = 0; | ||
| 299 | |||
| 300 | TestSequenceTableForPatchFilesAction( | ||
| 301 | mainTransform.Tables[tableName], | ||
| 302 | ref hasPatchFilesAction, | ||
| 303 | ref installFilesSequence, | ||
| 304 | ref duplicateFilesSequence); | ||
| 305 | TestSequenceTableForPatchFilesAction( | ||
| 306 | pairedTransform.Tables[tableName], | ||
| 307 | ref hasPatchFilesAction, | ||
| 308 | ref installFilesSequence, | ||
| 309 | ref duplicateFilesSequence); | ||
| 310 | if (!hasPatchFilesAction) | ||
| 311 | { | ||
| 312 | WindowsInstallerStandard.TryGetStandardAction(tableName, "PatchFiles", out var patchFilesActionTuple); | ||
| 313 | |||
| 314 | var sequence = patchFilesActionTuple.Sequence; | ||
| 315 | |||
| 316 | // Test for default sequence value's appropriateness | ||
| 317 | if (installFilesSequence >= sequence || (0 != duplicateFilesSequence && duplicateFilesSequence <= sequence)) | ||
| 318 | { | ||
| 319 | if (0 != duplicateFilesSequence) | ||
| 320 | { | ||
| 321 | if (duplicateFilesSequence < installFilesSequence) | ||
| 322 | { | ||
| 323 | throw new WixException(ErrorMessages.InsertInvalidSequenceActionOrder(mainFileRow.SourceLineNumbers, tableName, "InstallFiles", "DuplicateFiles", patchFilesActionTuple.Action)); | ||
| 324 | } | ||
| 325 | else | ||
| 326 | { | ||
| 327 | sequence = (duplicateFilesSequence + installFilesSequence) / 2; | ||
| 328 | if (installFilesSequence == sequence || duplicateFilesSequence == sequence) | ||
| 329 | { | ||
| 330 | throw new WixException(ErrorMessages.InsertSequenceNoSpace(mainFileRow.SourceLineNumbers, tableName, "InstallFiles", "DuplicateFiles", patchFilesActionTuple.Action)); | ||
| 331 | } | ||
| 332 | } | ||
| 333 | } | ||
| 334 | else | ||
| 335 | { | ||
| 336 | sequence = installFilesSequence + 1; | ||
| 337 | } | ||
| 338 | } | ||
| 339 | |||
| 340 | var sequenceTable = pairedTransform.EnsureTable(this.TableDefinitions[tableName]); | ||
| 341 | if (0 == sequenceTable.Rows.Count) | ||
| 342 | { | ||
| 343 | sequenceTable.Operation = TableOperation.Add; | ||
| 344 | } | ||
| 345 | |||
| 346 | var patchAction = sequenceTable.CreateRow(null); | ||
| 347 | patchAction[0] = patchFilesActionTuple.Action; | ||
| 348 | patchAction[1] = patchFilesActionTuple.Condition; | ||
| 349 | patchAction[2] = sequence; | ||
| 350 | patchAction.Operation = RowOperation.Add; | ||
| 351 | } | ||
| 352 | } | ||
| 353 | |||
| 354 | /// <summary> | ||
| 355 | /// Tests sequence table for PatchFiles and associated actions | ||
| 356 | /// </summary> | ||
| 357 | /// <param name="sequenceTable">The table to test.</param> | ||
| 358 | /// <param name="hasPatchFilesAction">Set to true if PatchFiles action is found. Left unchanged otherwise.</param> | ||
| 359 | /// <param name="installFilesSequence">Set to sequence value of InstallFiles action if found. Left unchanged otherwise.</param> | ||
| 360 | /// <param name="duplicateFilesSequence">Set to sequence value of DuplicateFiles action if found. Left unchanged otherwise.</param> | ||
| 361 | private static void TestSequenceTableForPatchFilesAction(Table sequenceTable, ref bool hasPatchFilesAction, ref int installFilesSequence, ref int duplicateFilesSequence) | ||
| 362 | { | ||
| 363 | if (null != sequenceTable) | ||
| 364 | { | ||
| 365 | foreach (var row in sequenceTable.Rows) | ||
| 366 | { | ||
| 367 | var actionName = row.FieldAsString(0); | ||
| 368 | switch (actionName) | ||
| 369 | { | ||
| 370 | case "PatchFiles": | ||
| 371 | hasPatchFilesAction = true; | ||
| 372 | break; | ||
| 373 | |||
| 374 | case "InstallFiles": | ||
| 375 | installFilesSequence = row.FieldAsInteger(2); | ||
| 376 | break; | ||
| 377 | |||
| 378 | case "DuplicateFiles": | ||
| 379 | duplicateFilesSequence = row.FieldAsInteger(2); | ||
| 380 | break; | ||
| 381 | } | ||
| 382 | } | ||
| 383 | } | ||
| 384 | } | ||
| 385 | |||
| 386 | /// <summary> | ||
| 387 | /// Signal a warning if a non-keypath file was changed in a patch without also changing the keypath file of the component. | ||
| 388 | /// </summary> | ||
| 389 | /// <param name="output">The output to validate.</param> | ||
| 390 | private void ValidateFileRowChanges(WindowsInstallerData transform) | ||
| 391 | { | ||
| 392 | var componentTable = transform.Tables["Component"]; | ||
| 393 | var fileTable = transform.Tables["File"]; | ||
| 394 | |||
| 395 | // There's no sense validating keypaths if the transform has no component or file table | ||
| 396 | if (componentTable == null || fileTable == null) | ||
| 397 | { | ||
| 398 | return; | ||
| 399 | } | ||
| 400 | |||
| 401 | var componentKeyPath = new Dictionary<string, string>(componentTable.Rows.Count); | ||
| 402 | |||
| 403 | // Index the Component table for non-directory & non-registry key paths. | ||
| 404 | foreach (var row in componentTable.Rows) | ||
| 405 | { | ||
| 406 | var keyPath = row.FieldAsString(5); | ||
| 407 | if (keyPath != null && 0 != (row.FieldAsInteger(3) & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath)) | ||
| 408 | { | ||
| 409 | componentKeyPath.Add(row.FieldAsString(0), keyPath); | ||
| 410 | } | ||
| 411 | } | ||
| 412 | |||
| 413 | var componentWithChangedKeyPath = new Dictionary<string, string>(); | ||
| 414 | var componentWithNonKeyPathChanged = new Dictionary<string, string>(); | ||
| 415 | // Verify changes in the file table, now that file diffing has occurred | ||
| 416 | foreach (FileRow row in fileTable.Rows) | ||
| 417 | { | ||
| 418 | if (RowOperation.Modify != row.Operation) | ||
| 419 | { | ||
| 420 | continue; | ||
| 421 | } | ||
| 422 | |||
| 423 | var fileId = row.FieldAsString(0); | ||
| 424 | var componentId = row.FieldAsString(1); | ||
| 425 | |||
| 426 | // If this file is the keypath of a component | ||
| 427 | if (componentKeyPath.ContainsValue(fileId)) | ||
| 428 | { | ||
| 429 | if (!componentWithChangedKeyPath.ContainsKey(componentId)) | ||
| 430 | { | ||
| 431 | componentWithChangedKeyPath.Add(componentId, fileId); | ||
| 432 | } | ||
| 433 | } | ||
| 434 | else | ||
| 435 | { | ||
| 436 | if (!componentWithNonKeyPathChanged.ContainsKey(componentId)) | ||
| 437 | { | ||
| 438 | componentWithNonKeyPathChanged.Add(componentId, fileId); | ||
| 439 | } | ||
| 440 | } | ||
| 441 | } | ||
| 442 | |||
| 443 | foreach (var componentFile in componentWithNonKeyPathChanged) | ||
| 444 | { | ||
| 445 | // Make sure all changes to non keypath files also had a change in the keypath. | ||
| 446 | if (!componentWithChangedKeyPath.ContainsKey(componentFile.Key) && componentKeyPath.TryGetValue(componentFile.Key, out var keyPath)) | ||
| 447 | { | ||
| 448 | this.Messaging.Write(WarningMessages.UpdateOfNonKeyPathFile(componentFile.Value, componentFile.Key, keyPath)); | ||
| 449 | } | ||
| 450 | } | ||
| 451 | } | ||
| 452 | } | ||
| 453 | } | ||
diff --git a/src/WixToolset.Core/Bind/FileFacade.cs b/src/WixToolset.Core/Bind/FileFacade.cs index 7bfdb9bb..f0ce14ca 100644 --- a/src/WixToolset.Core/Bind/FileFacade.cs +++ b/src/WixToolset.Core/Bind/FileFacade.cs | |||
| @@ -15,18 +15,27 @@ namespace WixToolset.Core.Bind | |||
| 15 | { | 15 | { |
| 16 | this.FileTuple = file; | 16 | this.FileTuple = file; |
| 17 | this.AssemblyTuple = assembly; | 17 | this.AssemblyTuple = assembly; |
| 18 | |||
| 19 | this.Identifier = file.Id; | ||
| 20 | this.ComponentRef = file.ComponentRef; | ||
| 18 | } | 21 | } |
| 19 | 22 | ||
| 20 | public FileFacade(bool fromModule, FileTuple file) | 23 | public FileFacade(bool fromModule, FileTuple file) |
| 21 | { | 24 | { |
| 22 | this.FromModule = fromModule; | 25 | this.FromModule = fromModule; |
| 23 | this.FileTuple = file; | 26 | this.FileTuple = file; |
| 27 | |||
| 28 | this.Identifier = file.Id; | ||
| 29 | this.ComponentRef = file.ComponentRef; | ||
| 24 | } | 30 | } |
| 25 | 31 | ||
| 26 | internal FileFacade(FileRow row) | 32 | public FileFacade(FileRow row) |
| 27 | { | 33 | { |
| 28 | this.FromTransform = true; | 34 | this.FromTransform = true; |
| 29 | this.FileRow = row; | 35 | this.FileRow = row; |
| 36 | |||
| 37 | this.Identifier = new Identifier(AccessModifier.Private, row.File); | ||
| 38 | this.ComponentRef = row.Component; | ||
| 30 | } | 39 | } |
| 31 | 40 | ||
| 32 | public bool FromModule { get; } | 41 | public bool FromModule { get; } |
| @@ -39,11 +48,11 @@ namespace WixToolset.Core.Bind | |||
| 39 | 48 | ||
| 40 | private AssemblyTuple AssemblyTuple { get; } | 49 | private AssemblyTuple AssemblyTuple { get; } |
| 41 | 50 | ||
| 42 | public string Id => this.FileRow == null ? this.FileTuple.Id.Id : this.FileRow.File; | 51 | public string Id => this.Identifier.Id; |
| 43 | 52 | ||
| 44 | public Identifier Identifier => this.FileRow == null ? this.FileTuple.Id : throw new NotImplementedException(); | 53 | public Identifier Identifier { get; } |
| 45 | 54 | ||
| 46 | public string ComponentRef => this.FileRow == null ? this.FileTuple.ComponentRef : this.FileRow.Component; | 55 | public string ComponentRef { get; } |
| 47 | 56 | ||
| 48 | public int DiskId | 57 | public int DiskId |
| 49 | { | 58 | { |
| @@ -137,7 +146,7 @@ namespace WixToolset.Core.Bind | |||
| 137 | } | 146 | } |
| 138 | } | 147 | } |
| 139 | 148 | ||
| 140 | public AssemblyType? AssemblyType => this.FileRow == null ? this.AssemblyTuple?.Type : throw new NotImplementedException(); | 149 | public AssemblyType? AssemblyType => this.FileRow == null ? this.AssemblyTuple?.Type : null; |
| 141 | 150 | ||
| 142 | public string AssemblyApplicationFileRef => this.FileRow == null ? this.AssemblyTuple?.ApplicationFileRef : throw new NotImplementedException(); | 151 | public string AssemblyApplicationFileRef => this.FileRow == null ? this.AssemblyTuple?.ApplicationFileRef : throw new NotImplementedException(); |
| 143 | 152 | ||
| @@ -153,5 +162,10 @@ namespace WixToolset.Core.Bind | |||
| 153 | /// Gets or sets the MsiFileHash row for this file. | 162 | /// Gets or sets the MsiFileHash row for this file. |
| 154 | /// </summary> | 163 | /// </summary> |
| 155 | public MsiFileHashTuple Hash { get; set; } | 164 | public MsiFileHashTuple Hash { get; set; } |
| 165 | |||
| 166 | /// <summary> | ||
| 167 | /// Allows direct access to the underlying FileRow as requried for patching. | ||
| 168 | /// </summary> | ||
| 169 | public FileRow GetFileRow() => this.FileRow ?? throw new NotImplementedException(); | ||
| 156 | } | 170 | } |
| 157 | } | 171 | } |
diff --git a/src/WixToolset.Core/CommandLine/BuildCommand.cs b/src/WixToolset.Core/CommandLine/BuildCommand.cs index 80003392..8392131f 100644 --- a/src/WixToolset.Core/CommandLine/BuildCommand.cs +++ b/src/WixToolset.Core/CommandLine/BuildCommand.cs | |||
| @@ -334,6 +334,7 @@ namespace WixToolset.Core.CommandLine | |||
| 334 | context.DelayedFields = resolveResult.DelayedFields; | 334 | context.DelayedFields = resolveResult.DelayedFields; |
| 335 | context.ExpectedEmbeddedFiles = resolveResult.ExpectedEmbeddedFiles; | 335 | context.ExpectedEmbeddedFiles = resolveResult.ExpectedEmbeddedFiles; |
| 336 | context.Extensions = this.ExtensionManager.GetServices<IBinderExtension>(); | 336 | context.Extensions = this.ExtensionManager.GetServices<IBinderExtension>(); |
| 337 | context.FileSystemExtensions = this.ExtensionManager.GetServices<IFileSystemExtension>(); | ||
| 337 | context.Ices = Array.Empty<string>(); // TODO: set this correctly | 338 | context.Ices = Array.Empty<string>(); // TODO: set this correctly |
| 338 | context.IntermediateFolder = intermediateFolder; | 339 | context.IntermediateFolder = intermediateFolder; |
| 339 | context.IntermediateRepresentation = resolveResult.IntermediateRepresentation; | 340 | context.IntermediateRepresentation = resolveResult.IntermediateRepresentation; |
diff --git a/src/test/WixToolsetTest.CoreIntegration/PatchFixture.cs b/src/test/WixToolsetTest.CoreIntegration/PatchFixture.cs index 584f86fe..3616bcab 100644 --- a/src/test/WixToolsetTest.CoreIntegration/PatchFixture.cs +++ b/src/test/WixToolsetTest.CoreIntegration/PatchFixture.cs | |||
| @@ -16,7 +16,7 @@ namespace WixToolsetTest.CoreIntegration | |||
| 16 | { | 16 | { |
| 17 | private static readonly XNamespace PatchNamespace = "http://www.microsoft.com/msi/patch_applicability.xsd"; | 17 | private static readonly XNamespace PatchNamespace = "http://www.microsoft.com/msi/patch_applicability.xsd"; |
| 18 | 18 | ||
| 19 | [Fact(Skip = "Skip until patches have files in them")] | 19 | [Fact] |
| 20 | public void CanBuildSimplePatch() | 20 | public void CanBuildSimplePatch() |
| 21 | { | 21 | { |
| 22 | var folder = TestData.Get(@"TestData\PatchSingle"); | 22 | var folder = TestData.Get(@"TestData\PatchSingle"); |
| @@ -45,7 +45,7 @@ namespace WixToolsetTest.CoreIntegration | |||
| 45 | Assert.True(File.Exists(cab)); | 45 | Assert.True(File.Exists(cab)); |
| 46 | 46 | ||
| 47 | var files = Query.GetCabinetFiles(cab); | 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. | 48 | Assert.Equal(new[] { "a.txt", "b.txt" }, files.Select(f => f.Name).ToArray()); |
| 49 | } | 49 | } |
| 50 | } | 50 | } |
| 51 | 51 | ||
