From 860676fa5b40a1904478151e9b4934c004e7db63 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 7 Oct 2019 11:18:13 -0700 Subject: Implement Bundle build --- appveyor.cmd | 4 +- src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs | 860 ++++++--------------- .../Bind/ProcessDependencyProvidersCommand.cs | 166 ++++ .../Bind/ProvidesDependency.cs | 110 --- .../Bind/ProvidesDependencyCollection.cs | 64 -- src/WixToolset.Core.Burn/Bind/SearchFacade.cs | 197 +++++ .../Bind/WixComponentSearchInfo.cs | 65 -- src/WixToolset.Core.Burn/Bind/WixFileSearchInfo.cs | 56 -- .../Bind/WixProductSearchInfo.cs | 69 -- .../Bind/WixRegistrySearchInfo.cs | 93 --- src/WixToolset.Core.Burn/Bind/WixSearchInfo.cs | 55 -- src/WixToolset.Core.Burn/BundleBackend.cs | 28 +- .../AutomaticallySlipstreamPatchesCommand.cs | 121 +-- .../Bundles/BundleHashAlgorithm.cs | 30 + src/WixToolset.Core.Burn/Bundles/BurnCommon.cs | 6 +- src/WixToolset.Core.Burn/Bundles/BurnWriter.cs | 10 +- ...CreateBootstrapperApplicationManifestCommand.cs | 312 ++++---- .../Bundles/CreateBundleExeCommand.cs | 171 ++++ .../Bundles/CreateBurnManifestCommand.cs | 399 +++++----- .../Bundles/CreateContainerCommand.cs | 59 +- .../Bundles/CreateNonUXContainers.cs | 134 ++++ .../Bundles/GetPackageFacadesCommand.cs | 78 +- .../OrderPackagesAndRollbackBoundariesCommand.cs | 66 +- src/WixToolset.Core.Burn/Bundles/PackageFacade.cs | 55 +- .../Bundles/ProcessExePackageCommand.cs | 23 +- .../Bundles/ProcessMsiPackageCommand.cs | 425 +++++----- .../Bundles/ProcessMspPackageCommand.cs | 102 +-- .../Bundles/ProcessMsuPackageCommand.cs | 20 +- .../Bundles/ProcessPayloadsCommand.cs | 89 ++- .../Bundles/VerifyPayloadsWithCatalogCommand.cs | 49 +- src/WixToolset.Core.Burn/BurnBackendFactory.cs | 3 +- src/WixToolset.Core.Burn/VerifyInterop.cs | 2 - .../WixToolset.Core.Burn.csproj | 1 + .../Bind/BindDatabaseCommand.cs | 8 +- .../Bind/CalculateComponentGuids.cs | 16 +- .../Bind/CreateCabinetsCommand.cs | 1 - .../Bind/CreateOutputFromIRCommand.cs | 16 + .../Bind/PathResolver.cs | 106 --- .../Bind/ProcessUncompressedFilesCommand.cs | 13 +- .../Bind/ResolvedDirectory.cs | 31 - .../Bind/UpdateFileFacadesCommand.cs | 1 - src/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs | 8 +- src/WixToolset.Core/Bind/FileResolver.cs | 8 +- .../Bind/ResolveDelayedFieldsCommand.cs | 2 +- src/WixToolset.Core/BindContext.cs | 4 +- src/WixToolset.Core/CommandLine/BuildCommand.cs | 11 +- src/WixToolset.Core/Compiler.cs | 2 +- src/WixToolset.Core/Compiler_Bundle.cs | 139 ++-- .../ExtensibilityServices/BackendHelper.cs | 11 +- .../ExtensibilityServices/PathResolver.cs | 91 +++ .../ExtensibilityServices/ResolvedDirectory.cs | 15 + .../WindowsInstallerBackendHelper.cs | 32 +- src/WixToolset.Core/Link/WixGroupingOrdering.cs | 31 +- src/WixToolset.Core/WixToolsetServiceProvider.cs | 4 +- .../BundleFixture.cs | 52 ++ .../TestData/.Data/burn.exe | Bin 0 -> 463360 bytes .../TestData/SimpleBundle/Bundle.en-us.wxl | 10 + .../TestData/SimpleBundle/Bundle.wxs | 11 + .../SimpleBundle/data/MsiPackage/Shared.dll | 1 + .../TestData/SimpleBundle/data/MsiPackage/test.txt | 1 + .../TestData/SimpleBundle/data/fakeba.dll | 1 + .../TestData/SimpleBundle/data/test.msi | Bin 0 -> 32768 bytes .../WixToolsetTest.CoreIntegration.csproj | 7 + 63 files changed, 2322 insertions(+), 2233 deletions(-) create mode 100644 src/WixToolset.Core.Burn/Bind/ProcessDependencyProvidersCommand.cs delete mode 100644 src/WixToolset.Core.Burn/Bind/ProvidesDependency.cs delete mode 100644 src/WixToolset.Core.Burn/Bind/ProvidesDependencyCollection.cs create mode 100644 src/WixToolset.Core.Burn/Bind/SearchFacade.cs delete mode 100644 src/WixToolset.Core.Burn/Bind/WixComponentSearchInfo.cs delete mode 100644 src/WixToolset.Core.Burn/Bind/WixFileSearchInfo.cs delete mode 100644 src/WixToolset.Core.Burn/Bind/WixProductSearchInfo.cs delete mode 100644 src/WixToolset.Core.Burn/Bind/WixRegistrySearchInfo.cs delete mode 100644 src/WixToolset.Core.Burn/Bind/WixSearchInfo.cs create mode 100644 src/WixToolset.Core.Burn/Bundles/BundleHashAlgorithm.cs create mode 100644 src/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs create mode 100644 src/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs delete mode 100644 src/WixToolset.Core.WindowsInstaller/Bind/PathResolver.cs delete mode 100644 src/WixToolset.Core.WindowsInstaller/Bind/ResolvedDirectory.cs create mode 100644 src/WixToolset.Core/ExtensibilityServices/PathResolver.cs create mode 100644 src/WixToolset.Core/ExtensibilityServices/ResolvedDirectory.cs create mode 100644 src/test/WixToolsetTest.CoreIntegration/BundleFixture.cs create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/.Data/burn.exe create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/Bundle.en-us.wxl create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/Bundle.wxs create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/MsiPackage/Shared.dll create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/MsiPackage/test.txt create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/fakeba.dll create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/test.msi diff --git a/appveyor.cmd b/appveyor.cmd index 75d75d99..27374ae5 100644 --- a/appveyor.cmd +++ b/appveyor.cmd @@ -2,13 +2,13 @@ @pushd %~dp0 @set _P=%~dp0build\Release\publish +dotnet test -c Release src\test\WixToolsetTest.CoreIntegration + dotnet pack -c Release src\WixToolset.Core dotnet pack -c Release src\WixToolset.Core.Burn dotnet pack -c Release src\WixToolset.Core.WindowsInstaller dotnet pack -c Release src\WixToolset.Core.TestPackage -dotnet build -c Release src\test\WixToolsetTest.CoreIntegration - @popd @endlocal diff --git a/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs b/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs index 61aa5189..6b4b9d68 100644 --- a/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs +++ b/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs @@ -8,78 +8,51 @@ namespace WixToolset.Core.Burn using System.Globalization; using System.IO; using System.Linq; - using System.Reflection; using WixToolset.Core.Bind; + using WixToolset.Core.Burn.Bind; using WixToolset.Core.Burn.Bundles; using WixToolset.Data; - using WixToolset.Data.Bind; + using WixToolset.Data.Burn; + using WixToolset.Data.Tuples; using WixToolset.Extensibility; using WixToolset.Extensibility.Data; using WixToolset.Extensibility.Services; - // TODO: (4.0) Refactor so that these don't need to be copied. - // Copied verbatim from ext\UtilExtension\wixext\UtilCompiler.cs - [Flags] - internal enum WixFileSearchAttributes - { - Default = 0x001, - MinVersionInclusive = 0x002, - MaxVersionInclusive = 0x004, - MinSizeInclusive = 0x008, - MaxSizeInclusive = 0x010, - MinDateInclusive = 0x020, - MaxDateInclusive = 0x040, - WantVersion = 0x080, - WantExists = 0x100, - IsDirectory = 0x200, - } - - [Flags] - internal enum WixRegistrySearchAttributes - { - Raw = 0x01, - Compatible = 0x02, - ExpandEnvironmentVariables = 0x04, - WantValue = 0x08, - WantExists = 0x10, - Win64 = 0x20, - } - - internal enum WixComponentSearchAttributes - { - KeyPath = 0x1, - State = 0x2, - WantDirectory = 0x4, - } - - [Flags] - internal enum WixProductSearchAttributes - { - Version = 0x1, - Language = 0x2, - State = 0x4, - Assignment = 0x8, - UpgradeCode = 0x10, - } - /// /// Binds a this.bundle. /// internal class BindBundleCommand { - public BindBundleCommand(IBindContext context) + public BindBundleCommand(IBindContext context, IEnumerable backedExtensions) { - //this.TableDefinitions = WindowsInstallerStandard.GetTableDefinitions(); + this.ServiceProvider = context.ServiceProvider; + + this.Messaging = context.ServiceProvider.GetService(); + + this.BackendHelper = context.ServiceProvider.GetService(); + this.BurnStubPath = context.BurnStubPath; + this.DefaultCompressionLevel = context.DefaultCompressionLevel; this.DelayedFields = context.DelayedFields; this.ExpectedEmbeddedFiles = context.ExpectedEmbeddedFiles; + this.IntermediateFolder = context.IntermediateFolder; + this.Output = context.IntermediateRepresentation; + this.OutputPath = context.OutputPath; + this.OutputPdbPath = context.OutputPdbPath; + //this.VariableResolver = context.VariableResolver; - var extensionManager = context.ServiceProvider.GetService(); - - this.BackendExtensions = extensionManager.GetServices(); + this.BackendExtensions = backedExtensions; } - public CompressionLevel DefaultCompressionLevel { private get; set; } + private IServiceProvider ServiceProvider { get; } + + private IMessaging Messaging { get; } + + private IBackendHelper BackendHelper { get; } + + private string BurnStubPath { get; } + + private CompressionLevel? DefaultCompressionLevel { get; } public IEnumerable DelayedFields { get; } @@ -87,17 +60,15 @@ namespace WixToolset.Core.Burn private IEnumerable BackendExtensions { get; } - public Intermediate Output { private get; set; } - - public string OutputPath { private get; set; } + private Intermediate Output { get; } - public string PdbFile { private get; set; } + private string OutputPath { get; } - //public TableDefinitionCollection TableDefinitions { private get; set; } + private string OutputPdbPath { get; } - public string IntermediateFolder { private get; set; } + private string IntermediateFolder { get; } - public IVariableResolver VariableResolver { private get; set; } + private IVariableResolver VariableResolver { get; } public IEnumerable FileTransfers { get; private set; } @@ -105,196 +76,177 @@ namespace WixToolset.Core.Burn public void Execute() { - throw new NotImplementedException(); -#if TODO - this.FileTransfers = Enumerable.Empty(); - this.ContentFilePaths = Enumerable.Empty(); + var section = this.Output.Sections.Single(); + + var fileTransfers = new List(); + var trackedFiles = new List(); // First look for data we expect to find... Chain, WixGroups, etc. // We shouldn't really get past the linker phase if there are // no group items... that means that there's no UX, no Chain, // *and* no Containers! - Table chainPackageTable = this.GetRequiredTable("WixBundlePackage"); + var chainPackageTuples = this.GetRequiredTuples(); - Table wixGroupTable = this.GetRequiredTable("WixGroup"); + var wixGroupTuples = this.GetRequiredTuples(); // Ensure there is one and only one row in the WixBundle table. // The compiler and linker behavior should have colluded to get // this behavior. - WixBundleRow bundleRow = (WixBundleRow)this.GetSingleRowTable("WixBundle"); + var bundleTuple = this.GetSingleTuple(); + + bundleTuple.BundleId = Guid.NewGuid().ToString("B").ToUpperInvariant(); - bundleRow.PerMachine = true; // default to per-machine but the first-per user package wil flip the bundle per-user. + bundleTuple.Attributes |= WixBundleAttributes.PerMachine; // default to per-machine but the first-per user package wil flip the bundle per-user. // Ensure there is one and only one row in the WixBootstrapperApplication table. // The compiler and linker behavior should have colluded to get // this behavior. - Row baRow = this.GetSingleRowTable("WixBootstrapperApplication"); + var bundleApplicationTuple = this.GetSingleTuple(); // Ensure there is one and only one row in the WixChain table. // The compiler and linker behavior should have colluded to get // this behavior. - WixChainRow chainRow = (WixChainRow)this.GetSingleRowTable("WixChain"); + var chainTuple = this.GetSingleTuple(); - if (Messaging.Instance.EncounteredError) + if (this.Messaging.EncounteredError) { return; } // If there are any fields to resolve later, create the cache to populate during bind. - IDictionary variableCache = null; - if (this.DelayedFields.Any()) - { - variableCache = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - } + var variableCache = this.DelayedFields.Any() ? new Dictionary(StringComparer.InvariantCultureIgnoreCase) : null; // TODO: Although the WixSearch tables are defined in the Util extension, // the Bundle Binder has to know all about them. We hope to revisit all // of this in the 4.0 timeframe. - IEnumerable orderedSearches = this.OrderSearches(); + var orderedSearches = this.OrderSearches(section); +#if THIS_SHOULD_BE_DELETED_SINCE_RESOLVE_DOES_THIS_NOW // Extract files that come from cabinet files (this does not extract files from merge modules). { var extractEmbeddedFilesCommand = new ExtractEmbeddedFilesCommand(); extractEmbeddedFilesCommand.FilesWithEmbeddedFiles = ExpectedEmbeddedFiles; extractEmbeddedFilesCommand.Execute(); } +#endif // Get the explicit payloads. - RowDictionary payloads = new RowDictionary(this.Output.Tables["WixBundlePayload"]); + var payloadTuples = section.Tuples.OfType().ToDictionary(t => t.Id.Id); // Update explicitly authored payloads with their parent package and container (as appropriate) // to make it easier to gather the payloads later. - foreach (WixGroupRow row in wixGroupTable.RowsAs()) + foreach (var groupTuple in wixGroupTuples) { - if (ComplexReferenceChildType.Payload == row.ChildType) + if (ComplexReferenceChildType.Payload == groupTuple.ChildType) { - WixBundlePayloadRow payload = payloads.Get(row.ChildId); + var payloadTuple = payloadTuples[groupTuple.ChildId]; - if (ComplexReferenceParentType.Package == row.ParentType) + if (ComplexReferenceParentType.Package == groupTuple.ParentType) { - Debug.Assert(String.IsNullOrEmpty(payload.Package)); - payload.Package = row.ParentId; + Debug.Assert(String.IsNullOrEmpty(payloadTuple.PackageRef)); + payloadTuple.PackageRef = groupTuple.ParentId; } - else if (ComplexReferenceParentType.Container == row.ParentType) + else if (ComplexReferenceParentType.Container == groupTuple.ParentType) { - Debug.Assert(String.IsNullOrEmpty(payload.Container)); - payload.Container = row.ParentId; + Debug.Assert(String.IsNullOrEmpty(payloadTuple.ContainerRef)); + payloadTuple.ContainerRef = groupTuple.ParentId; } - else if (ComplexReferenceParentType.Layout == row.ParentType) + else if (ComplexReferenceParentType.Layout == groupTuple.ParentType) { - payload.LayoutOnly = true; + payloadTuple.LayoutOnly = true; } } } - List fileTransfers = new List(); - string layoutDirectory = Path.GetDirectoryName(this.OutputPath); + var layoutDirectory = Path.GetDirectoryName(this.OutputPath); // Process the explicitly authored payloads. ISet processedPayloads; { - ProcessPayloadsCommand command = new ProcessPayloadsCommand(); - command.Payloads = payloads.Values; - command.DefaultPackaging = bundleRow.DefaultPackagingType; - command.LayoutDirectory = layoutDirectory; + var command = new ProcessPayloadsCommand(this.ServiceProvider, this.BackendHelper, payloadTuples.Values, bundleTuple.DefaultPackagingType, layoutDirectory); command.Execute(); fileTransfers.AddRange(command.FileTransfers); - processedPayloads = new HashSet(payloads.Keys); + processedPayloads = new HashSet(payloadTuples.Keys); } IDictionary facades; { - GetPackageFacadesCommand command = new GetPackageFacadesCommand(); - command.PackageTable = chainPackageTable; - command.ExePackageTable = this.Output.Tables["WixBundleExePackage"]; - command.MsiPackageTable = this.Output.Tables["WixBundleMsiPackage"]; - command.MspPackageTable = this.Output.Tables["WixBundleMspPackage"]; - command.MsuPackageTable = this.Output.Tables["WixBundleMsuPackage"]; + var command = new GetPackageFacadesCommand(chainPackageTuples, section); command.Execute(); facades = command.PackageFacades; } - // Process each package facade. Note this is likely to add payloads and other rows to tables so + // Process each package facade. Note this is likely to add payloads and other tuples so // note that any indexes created above may be out of date now. - foreach (PackageFacade facade in facades.Values) + foreach (var facade in facades.Values) { - switch (facade.Package.Type) + switch (facade.PackageTuple.Type) { - case WixBundlePackageType.Exe: - { - ProcessExePackageCommand command = new ProcessExePackageCommand(); - command.AuthoredPayloads = payloads; - command.Facade = facade; - command.Execute(); - - // ? variableCache.Add(String.Concat("packageManufacturer.", facade.Package.WixChainItemId), facade.ExePackage.Manufacturer); - } - break; + case WixBundlePackageType.Exe: + { + var command = new ProcessExePackageCommand(facade, payloadTuples); + command.Execute(); + } + break; - case WixBundlePackageType.Msi: - { - var command = new ProcessMsiPackageCommand(); - command.AuthoredPayloads = payloads; - command.Facade = facade; - command.BackendExtensions = this.BackendExtensions; - command.MsiFeatureTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleMsiFeature"]); - command.MsiPropertyTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleMsiProperty"]); - command.PayloadTable = this.Output.Tables["WixBundlePayload"]; - command.RelatedPackageTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleRelatedPackage"]); - command.Execute(); - - if (null != variableCache) - { - variableCache.Add(String.Concat("packageLanguage.", facade.Package.WixChainItemId), facade.MsiPackage.ProductLanguage.ToString()); - - if (null != facade.MsiPackage.Manufacturer) - { - variableCache.Add(String.Concat("packageManufacturer.", facade.Package.WixChainItemId), facade.MsiPackage.Manufacturer); - } - } + case WixBundlePackageType.Msi: + { + var command = new ProcessMsiPackageCommand(this.ServiceProvider, this.BackendExtensions, section, facade, payloadTuples); + command.Execute(); - } - break; + if (null != variableCache) + { + var msiPackage = (WixBundleMsiPackageTuple)facade.SpecificPackageTuple; + variableCache.Add(String.Concat("packageLanguage.", facade.PackageId), msiPackage.ProductLanguage.ToString()); - case WixBundlePackageType.Msp: + if (null != msiPackage.Manufacturer) { - ProcessMspPackageCommand command = new ProcessMspPackageCommand(); - command.AuthoredPayloads = payloads; - command.Facade = facade; - command.WixBundlePatchTargetCodeTable = this.Output.EnsureTable(this.TableDefinitions["WixBundlePatchTargetCode"]); - command.Execute(); + variableCache.Add(String.Concat("packageManufacturer.", facade.PackageId), msiPackage.Manufacturer); } - break; + } - case WixBundlePackageType.Msu: - { - ProcessMsuPackageCommand command = new ProcessMsuPackageCommand(); - command.Facade = facade; - command.Execute(); - } - break; + } + break; + + case WixBundlePackageType.Msp: + { + var command = new ProcessMspPackageCommand(this.Messaging, section, facade, payloadTuples); + command.Execute(); + } + break; + + case WixBundlePackageType.Msu: + { + var command = new ProcessMsuPackageCommand(facade, payloadTuples); + command.Execute(); + } + break; } if (null != variableCache) { - BindBundleCommand.PopulatePackageVariableCache(facade.Package, variableCache); + BindBundleCommand.PopulatePackageVariableCache(facade.PackageTuple, variableCache); } } + if (this.Messaging.EncounteredError) + { + return; + } + // Reindex the payloads now that all the payloads (minus the manifest payloads that will be created later) // are present. - payloads = new RowDictionary(this.Output.Tables["WixBundlePayload"]); + payloadTuples = section.Tuples.OfType().ToDictionary(t => t.Id.Id); // Process the payloads that were added by processing the packages. { - ProcessPayloadsCommand command = new ProcessPayloadsCommand(); - command.Payloads = payloads.Values.Where(r => !processedPayloads.Contains(r.Id)).ToList(); - command.DefaultPackaging = bundleRow.DefaultPackagingType; - command.LayoutDirectory = layoutDirectory; + var toProcess = payloadTuples.Values.Where(r => !processedPayloads.Contains(r.Id.Id)).ToList(); + + var command = new ProcessPayloadsCommand(this.ServiceProvider, this.BackendHelper, toProcess, bundleTuple.DefaultPackagingType, layoutDirectory); command.Execute(); fileTransfers.AddRange(command.FileTransfers); @@ -303,45 +255,43 @@ namespace WixToolset.Core.Burn } // Set the package metadata from the payloads now that we have the complete payload information. - ILookup payloadsByPackage = payloads.Values.ToLookup(p => p.Package); - { - foreach (PackageFacade facade in facades.Values) + var payloadsByPackageId = payloadTuples.Values.ToLookup(p => p.PackageRef); + + foreach (var facade in facades.Values) { - facade.Package.Size = 0; + facade.PackageTuple.Size = 0; - IEnumerable packagePayloads = payloadsByPackage[facade.Package.WixChainItemId]; + var packagePayloads = payloadsByPackageId[facade.PackageId]; - foreach (WixBundlePayloadRow payload in packagePayloads) + foreach (var payload in packagePayloads) { - facade.Package.Size += payload.FileSize; + facade.PackageTuple.Size += payload.FileSize; } - if (!facade.Package.InstallSize.HasValue) + if (!facade.PackageTuple.InstallSize.HasValue) { - facade.Package.InstallSize = facade.Package.Size; - + facade.PackageTuple.InstallSize = facade.PackageTuple.Size; } - WixBundlePayloadRow packagePayload = payloads[facade.Package.PackagePayload]; + var packagePayload = payloadTuples[facade.PackageTuple.PayloadRef]; - if (String.IsNullOrEmpty(facade.Package.Description)) + if (String.IsNullOrEmpty(facade.PackageTuple.Description)) { - facade.Package.Description = packagePayload.Description; + facade.PackageTuple.Description = packagePayload.Description; } - if (String.IsNullOrEmpty(facade.Package.DisplayName)) + if (String.IsNullOrEmpty(facade.PackageTuple.DisplayName)) { - facade.Package.DisplayName = packagePayload.DisplayName; + facade.PackageTuple.DisplayName = packagePayload.DisplayName; } } } - // Give the UX payloads their embedded IDs... - int uxPayloadIndex = 0; + var uxPayloadIndex = 0; { - foreach (WixBundlePayloadRow payload in payloads.Values.Where(p => Compiler.BurnUXContainerId == p.Container)) + foreach (var payload in payloadTuples.Values.Where(p => BurnConstants.BurnUXContainerName == p.ContainerRef)) { // In theory, UX payloads could be embedded in the UX CAB, external to the bundle EXE, or even // downloaded. The current engine requires the UX to be fully present before any downloading starts, @@ -349,7 +299,7 @@ namespace WixToolset.Core.Burn // into the temporary UX directory correctly, so we don't allow external either. if (PackagingType.Embedded != payload.Packaging) { - Messaging.Instance.OnMessage(WixWarnings.UxPayloadsOnlySupportEmbedding(payload.SourceLineNumbers, payload.FullFileName)); + this.Messaging.Write(WarningMessages.UxPayloadsOnlySupportEmbedding(payload.SourceLineNumbers, payload.SourceFile.Path)); payload.Packaging = PackagingType.Embedded; } @@ -360,12 +310,12 @@ namespace WixToolset.Core.Burn if (0 == uxPayloadIndex) { // If we didn't get any UX payloads, it's an error! - throw new WixException(WixErrors.MissingBundleInformation("BootstrapperApplication")); + throw new WixException(ErrorMessages.MissingBundleInformation("BootstrapperApplication")); } // Give the embedded payloads without an embedded id yet an embedded id. - int payloadIndex = 0; - foreach (WixBundlePayloadRow payload in payloads.Values) + var payloadIndex = 0; + foreach (var payload in payloadTuples.Values) { Debug.Assert(PackagingType.Unknown != payload.Packaging); @@ -377,38 +327,38 @@ namespace WixToolset.Core.Burn } } + if (this.Messaging.EncounteredError) + { + return; + } + // Determine patches to automatically slipstream. { - AutomaticallySlipstreamPatchesCommand command = new AutomaticallySlipstreamPatchesCommand(); - command.PackageFacades = facades.Values; - command.SlipstreamMspTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleSlipstreamMsp"]); - command.WixBundlePatchTargetCodeTable = this.Output.EnsureTable(this.TableDefinitions["WixBundlePatchTargetCode"]); + var command = new AutomaticallySlipstreamPatchesCommand(section, facades.Values); command.Execute(); } // If catalog files exist, non-embedded payloads should validate with the catalogs. - IEnumerable catalogs = this.Output.Tables["WixBundleCatalog"].RowsAs(); + var catalogs = section.Tuples.OfType().ToList(); - if (catalogs.Any()) + if (catalogs.Count > 0) { - VerifyPayloadsWithCatalogCommand command = new VerifyPayloadsWithCatalogCommand(); - command.Catalogs = catalogs; - command.Payloads = payloads.Values; + var command = new VerifyPayloadsWithCatalogCommand(this.Messaging, catalogs, payloadTuples.Values); command.Execute(); } - if (Messaging.Instance.EncounteredError) + if (this.Messaging.EncounteredError) { return; } IEnumerable orderedFacades; - IEnumerable boundaries; + IEnumerable boundaries; { - OrderPackagesAndRollbackBoundariesCommand command = new OrderPackagesAndRollbackBoundariesCommand(); - command.Boundaries = new RowDictionary(this.Output.Tables["WixBundleRollbackBoundary"]); - command.PackageFacades = facades; - command.WixGroupTable = wixGroupTable; + var groupTuples = section.Tuples.OfType(); + var boundaryTuplesById = section.Tuples.OfType().ToDictionary(b => b.Id.Id); + + var command = new OrderPackagesAndRollbackBoundariesCommand(this.Messaging, groupTuples, boundaryTuplesById, facades); command.Execute(); orderedFacades = command.OrderedPackageFacades; @@ -418,280 +368,122 @@ namespace WixToolset.Core.Burn // Resolve any delayed fields before generating the manifest. if (this.DelayedFields.Any()) { - var resolveDelayedFieldsCommand = new ResolveDelayedFieldsCommand(); - resolveDelayedFieldsCommand.OutputType = this.Output.Type; - resolveDelayedFieldsCommand.DelayedFields = this.DelayedFields; - resolveDelayedFieldsCommand.ModularizationGuid = null; - resolveDelayedFieldsCommand.VariableCache = variableCache; + var resolveDelayedFieldsCommand = new ResolveDelayedFieldsCommand(this.Messaging, this.DelayedFields, variableCache); resolveDelayedFieldsCommand.Execute(); } - // Set the overridable bundle provider key. - this.SetBundleProviderKey(this.Output, bundleRow); + Dictionary dependencyTuplesByKey; + { + var command = new ProcessDependencyProvidersCommand(this.Messaging, section, facades); + command.Execute(); - // Import or generate dependency providers for packages in the manifest. - this.ProcessDependencyProviders(this.Output, facades); + bundleTuple.ProviderKey = command.BundleProviderKey; // set the overridable bundle provider key. + dependencyTuplesByKey = command.DependencyTuplesByKey; + } // Update the bundle per-machine/per-user scope based on the chained packages. - this.ResolveBundleInstallScope(bundleRow, orderedFacades); + this.ResolveBundleInstallScope(section, bundleTuple, orderedFacades); // Generate the core-defined BA manifest tables... { - CreateBootstrapperApplicationManifestCommand command = new CreateBootstrapperApplicationManifestCommand(); - command.BundleRow = bundleRow; - command.ChainPackages = orderedFacades; - command.LastUXPayloadIndex = uxPayloadIndex; - command.MsiFeatures = this.Output.Tables["WixBundleMsiFeature"].RowsAs(); - command.Output = this.Output; - command.Payloads = payloads; - command.TableDefinitions = this.TableDefinitions; - command.TempFilesLocation = this.IntermediateFolder; + var command = new CreateBootstrapperApplicationManifestCommand(section, bundleTuple, orderedFacades, uxPayloadIndex, payloadTuples, this.IntermediateFolder); command.Execute(); - WixBundlePayloadRow baManifestPayload = command.BootstrapperApplicationManifestPayloadRow; - payloads.Add(baManifestPayload); + var baManifestPayload = command.BootstrapperApplicationManifestPayloadRow; + payloadTuples.Add(baManifestPayload.Id.Id, baManifestPayload); } - //foreach (BinderExtension extension in this.Extensions) - //{ - // extension.PostBind(this.Context); - //} +#if TODO + foreach (BinderExtension extension in this.Extensions) + { + extension.PostBind(this.Context); + } +#endif // Create all the containers except the UX container first so the manifest (that goes in the UX container) // can contain all size and hash information about the non-UX containers. - RowDictionary containers = new RowDictionary(this.Output.Tables["WixBundleContainer"]); - - ILookup payloadsByContainer = payloads.Values.ToLookup(p => p.Container); - - int attachedContainerIndex = 1; // count starts at one because UX container is "0". - - IEnumerable uxContainerPayloads = Enumerable.Empty(); - - foreach (WixBundleContainerRow container in containers.Values) + WixBundleContainerTuple uxContainer; + IEnumerable uxPayloads; + IEnumerable containers; { - IEnumerable containerPayloads = payloadsByContainer[container.Id]; - - if (!containerPayloads.Any()) - { - if (container.Id != Compiler.BurnDefaultAttachedContainerId) - { - // TODO: display warning that we're ignoring container that ended up with no paylods in it. - } - } - else if (Compiler.BurnUXContainerId == container.Id) - { - container.WorkingPath = Path.Combine(this.IntermediateFolder, container.Name); - container.AttachedContainerIndex = 0; - - // Gather the list of UX payloads but ensure the BootstrapperApplication Payload is the first - // in the list since that is the Payload that Burn attempts to load. - List uxPayloads = new List(); - - string baPayloadId = baRow.FieldAsString(0); - - foreach (WixBundlePayloadRow uxPayload in containerPayloads) - { - if (uxPayload.Id == baPayloadId) - { - uxPayloads.Insert(0, uxPayload); - } - else - { - uxPayloads.Add(uxPayload); - } - } + var command = new CreateNonUXContainers(this.BackendHelper, section, bundleApplicationTuple, payloadTuples, this.IntermediateFolder, layoutDirectory, this.DefaultCompressionLevel); + command.Execute(); - uxContainerPayloads = uxPayloads; - } - else - { - container.WorkingPath = Path.Combine(this.IntermediateFolder, container.Name); + fileTransfers.AddRange(command.FileTransfers); - // Add detached containers to the list of file transfers. - if (ContainerType.Detached == container.Type) - { - FileTransfer transfer; - if (FileTransfer.TryCreate(container.WorkingPath, Path.Combine(layoutDirectory, container.Name), true, "Container", container.SourceLineNumbers, out transfer)) - { - transfer.Built = true; - fileTransfers.Add(transfer); - } - } - else // update the attached container index. - { - Debug.Assert(ContainerType.Attached == container.Type); + uxContainer = command.UXContainer; + uxPayloads = command.UXContainerPayloads; + containers = command.Containers; + } + + // Create the bundle manifest. + string manifestPath; + { + var executableName = Path.GetFileName(this.OutputPath); - container.AttachedContainerIndex = attachedContainerIndex; - ++attachedContainerIndex; - } + var command = new CreateBurnManifestCommand(this.Messaging, this.BackendExtensions, executableName, section, bundleTuple, containers, chainTuple, orderedFacades, boundaries, uxPayloads, payloadTuples, orderedSearches, catalogs, this.IntermediateFolder); + command.Execute(); - this.CreateContainer(container, containerPayloads, null); - } + manifestPath = command.OutputPath; } - // Create the bundle manifest then UX container. - string manifestPath = Path.Combine(this.IntermediateFolder, "bundle-manifest.xml"); + // Create the UX container. { - var command = new CreateBurnManifestCommand(); - command.BackendExtensions = this.BackendExtensions; - command.Output = this.Output; - - command.BundleInfo = bundleRow; - command.Chain = chainRow; - command.Containers = containers; - command.Catalogs = catalogs; - command.ExecutableName = Path.GetFileName(this.OutputPath); - command.OrderedPackages = orderedFacades; - command.OutputPath = manifestPath; - command.RollbackBoundaries = boundaries; - command.OrderedSearches = orderedSearches; - command.Payloads = payloads; - command.UXContainerPayloads = uxContainerPayloads; + var command = new CreateContainerCommand(manifestPath, uxPayloads, uxContainer.WorkingPath, this.DefaultCompressionLevel); command.Execute(); - } - - WixBundleContainerRow uxContainer = containers[Compiler.BurnUXContainerId]; - this.CreateContainer(uxContainer, uxContainerPayloads, manifestPath); - - // Copy the burn.exe to a writable location then mark it to be moved to its final build location. Note - // that today, the x64 Burn uses the x86 stub. - string stubPlatform = (Platform.X64 == bundleRow.Platform) ? "x86" : bundleRow.Platform.ToString(); - - string stubFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), stubPlatform, "burn.exe"); - string bundleTempPath = Path.Combine(this.IntermediateFolder, Path.GetFileName(this.OutputPath)); - - Messaging.Instance.OnMessage(WixVerboses.GeneratingBundle(bundleTempPath, stubFile)); - string bundleFilename = Path.GetFileName(this.OutputPath); - if ("setup.exe".Equals(bundleFilename, StringComparison.OrdinalIgnoreCase)) - { - Messaging.Instance.OnMessage(WixErrors.InsecureBundleFilename(bundleFilename)); + uxContainer.Hash = command.Hash; + uxContainer.Size = command.Size; } - FileTransfer bundleTransfer; - if (FileTransfer.TryCreate(bundleTempPath, this.OutputPath, true, "Bundle", bundleRow.SourceLineNumbers, out bundleTransfer)) { - bundleTransfer.Built = true; - fileTransfers.Add(bundleTransfer); - } + var command = new CreateBundleExeCommand(this.Messaging, this.BackendHelper, this.IntermediateFolder, this.OutputPath, bundleTuple, uxContainer, containers, this.BurnStubPath); + command.Execute(); - File.Copy(stubFile, bundleTempPath, true); - File.SetAttributes(bundleTempPath, FileAttributes.Normal); + fileTransfers.Add(command.Transfer); + } - this.UpdateBurnResources(bundleTempPath, this.OutputPath, bundleRow); +#if TODO + this.Pdb = new Pdb { Output = output }; - // Update the .wixburn section to point to at the UX and attached container(s) then attach the containers - // if they should be attached. - using (BurnWriter writer = BurnWriter.Open(bundleTempPath)) + if (!String.IsNullOrEmpty(this.OutputPdbPath)) { - FileInfo burnStubFile = new FileInfo(bundleTempPath); - writer.InitializeBundleSectionData(burnStubFile.Length, bundleRow.BundleId); - - // Always attach the UX container first - writer.AppendContainer(uxContainer.WorkingPath, BurnWriter.Container.UX); + var trackPdb = this.BackendHelper.TrackFile(this.OutputPdbPath, TrackedFileType.Final); + trackedFiles.Add(trackPdb); - // Now append all other attached containers - foreach (WixBundleContainerRow container in containers.Values) - { - if (ContainerType.Attached == container.Type) - { - // The container was only created if it had payloads. - if (!String.IsNullOrEmpty(container.WorkingPath) && Compiler.BurnUXContainerId != container.Id) - { - writer.AppendContainer(container.WorkingPath, BurnWriter.Container.Attached); - } - } - } - } - - if (null != this.PdbFile) - { - Pdb pdb = new Pdb(); - pdb.Output = Output; - pdb.Save(this.PdbFile); + this.Pdb.Save(trackPdb.Path); } +#endif +#if TODO // does this need to come back, or do they only need to be in TrackedFiles? + this.ContentFilePaths = payloadTuples.Values.Where(p => p.ContentFile).Select(p => p.FullFileName).ToList(); +#endif this.FileTransfers = fileTransfers; - this.ContentFilePaths = payloads.Values.Where(p => p.ContentFile).Select(p => p.FullFileName).ToList(); - } - - private Table GetRequiredTable(string tableName) - { - Table table = this.Output.Tables[tableName]; - if (null == table || 0 == table.Rows.Count) - { - throw new WixException(WixErrors.MissingBundleInformation(tableName)); - } - - return table; - } - - private Row GetSingleRowTable(string tableName) - { - Table table = this.Output.Tables[tableName]; - if (null == table || 1 != table.Rows.Count) - { - throw new WixException(WixErrors.MissingBundleInformation(tableName)); - } + this.TrackedFiles = trackedFiles; - return table.Rows[0]; + // TODO: Eventually this gets removed + var intermediate = new Intermediate(this.Output.Id, new[] { section }, this.Output.Localizations.ToDictionary(l => l.Culture, StringComparer.OrdinalIgnoreCase), this.Output.EmbedFilePaths); + var trackIntermediate = this.BackendHelper.TrackFile(Path.Combine(this.IntermediateFolder, Path.GetFileName(Path.ChangeExtension(this.OutputPath, "wir"))), TrackedFileType.Intermediate); + intermediate.Save(trackIntermediate.Path); + trackedFiles.Add(trackIntermediate); } - private List OrderSearches() + private IEnumerable OrderSearches(IntermediateSection section) { - Dictionary allSearches = new Dictionary(); - Table wixFileSearchTable = this.Output.Tables["WixFileSearch"]; - if (null != wixFileSearchTable && 0 < wixFileSearchTable.Rows.Count) - { - foreach (Row row in wixFileSearchTable.Rows) - { - WixFileSearchInfo fileSearchInfo = new WixFileSearchInfo(row); - allSearches.Add(fileSearchInfo.Id, fileSearchInfo); - } - } - - Table wixRegistrySearchTable = this.Output.Tables["WixRegistrySearch"]; - if (null != wixRegistrySearchTable && 0 < wixRegistrySearchTable.Rows.Count) - { - foreach (Row row in wixRegistrySearchTable.Rows) - { - WixRegistrySearchInfo registrySearchInfo = new WixRegistrySearchInfo(row); - allSearches.Add(registrySearchInfo.Id, registrySearchInfo); - } - } - - Table wixComponentSearchTable = this.Output.Tables["WixComponentSearch"]; - if (null != wixComponentSearchTable && 0 < wixComponentSearchTable.Rows.Count) - { - foreach (Row row in wixComponentSearchTable.Rows) - { - WixComponentSearchInfo componentSearchInfo = new WixComponentSearchInfo(row); - allSearches.Add(componentSearchInfo.Id, componentSearchInfo); - } - } + var searchesById = section.Tuples + .Where(t => t.Definition.Type == TupleDefinitionType.WixComponentSearch || + t.Definition.Type == TupleDefinitionType.WixFileSearch || + t.Definition.Type == TupleDefinitionType.WixProductSearch || + t.Definition.Type == TupleDefinitionType.WixRegistrySearch) + .ToDictionary(t => t.Id.Id); - Table wixProductSearchTable = this.Output.Tables["WixProductSearch"]; - if (null != wixProductSearchTable && 0 < wixProductSearchTable.Rows.Count) - { - foreach (Row row in wixProductSearchTable.Rows) - { - WixProductSearchInfo productSearchInfo = new WixProductSearchInfo(row); - allSearches.Add(productSearchInfo.Id, productSearchInfo); - } - } + var orderedSearches = new List(searchesById.Keys.Count); - // Merge in the variable/condition info and get the canonical ordering for - // the searches. - List orderedSearches = new List(); - Table wixSearchTable = this.Output.Tables["WixSearch"]; - if (null != wixSearchTable && 0 < wixSearchTable.Rows.Count) + foreach (var searchTuple in section.Tuples.OfType()) { - orderedSearches.Capacity = wixSearchTable.Rows.Count; - foreach (Row row in wixSearchTable.Rows) + if (searchesById.TryGetValue(searchTuple.Id.Id, out var specificSearchTuple)) { - WixSearchInfo searchInfo = allSearches[(string)row[0]]; - searchInfo.AddWixSearchRowInfo(row); - orderedSearches.Add(searchInfo); + orderedSearches.Add(new SearchFacade(searchTuple, specificSearchTuple)); } } @@ -703,9 +495,9 @@ namespace WixToolset.Core.Burn /// /// The package with properties to cache. /// The property cache. - private static void PopulatePackageVariableCache(WixBundlePackageRow package, IDictionary variableCache) + private static void PopulatePackageVariableCache(WixBundlePackageTuple package, IDictionary variableCache) { - string id = package.WixChainItemId; + var id = package.Id.Id; variableCache.Add(String.Concat("packageDescription.", id), package.Description); //variableCache.Add(String.Concat("packageLanguage.", id), package.Language); @@ -714,231 +506,63 @@ namespace WixToolset.Core.Burn variableCache.Add(String.Concat("packageVersion.", id), package.Version); } - private void CreateContainer(WixBundleContainerRow container, IEnumerable containerPayloads, string manifestFile) + private void ResolveBundleInstallScope(IntermediateSection section, WixBundleTuple bundleTuple, IEnumerable facades) { - CreateContainerCommand command = new CreateContainerCommand(); - command.DefaultCompressionLevel = this.DefaultCompressionLevel; - command.Payloads = containerPayloads; - command.ManifestFile = manifestFile; - command.OutputPath = container.WorkingPath; - command.Execute(); - - container.Hash = command.Hash; - container.Size = command.Size; - } + var dependencyTuplesById = section.Tuples.OfType().ToDictionary(t => t.Id.Id); - private void ResolveBundleInstallScope(WixBundleRow bundleInfo, IEnumerable facades) - { - foreach (PackageFacade facade in facades) + foreach (var facade in facades) { - if (bundleInfo.PerMachine && YesNoDefaultType.No == facade.Package.PerMachine) + if (bundleTuple.PerMachine && YesNoDefaultType.No == facade.PackageTuple.PerMachine) { - Messaging.Instance.OnMessage(WixVerboses.SwitchingToPerUserPackage(facade.Package.SourceLineNumbers, facade.Package.WixChainItemId)); + this.Messaging.Write(VerboseMessages.SwitchingToPerUserPackage(facade.PackageTuple.SourceLineNumbers, facade.PackageId)); - bundleInfo.PerMachine = false; + bundleTuple.Attributes &= ~WixBundleAttributes.PerMachine; break; } } - foreach (PackageFacade facade in facades) + foreach (var facade in facades) { // Update package scope from bundle scope if default. - if (YesNoDefaultType.Default == facade.Package.PerMachine) + if (YesNoDefaultType.Default == facade.PackageTuple.PerMachine) { - facade.Package.PerMachine = bundleInfo.PerMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No; + facade.PackageTuple.PerMachine = bundleTuple.PerMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No; } // We will only register packages in the same scope as the bundle. Warn if any packages with providers // are in a different scope and not permanent (permanents typically don't need a ref-count). - if (!bundleInfo.PerMachine && YesNoDefaultType.Yes == facade.Package.PerMachine && !facade.Package.Permanent && 0 < facade.Provides.Count) + if (!bundleTuple.PerMachine && + YesNoDefaultType.Yes == facade.PackageTuple.PerMachine && + !facade.PackageTuple.Permanent && + dependencyTuplesById.ContainsKey(facade.PackageId)) { - Messaging.Instance.OnMessage(WixWarnings.NoPerMachineDependencies(facade.Package.SourceLineNumbers, facade.Package.WixChainItemId)); + this.Messaging.Write(WarningMessages.NoPerMachineDependencies(facade.PackageTuple.SourceLineNumbers, facade.PackageId)); } } } - private void UpdateBurnResources(string bundleTempPath, string outputPath, WixBundleRow bundleInfo) + private IEnumerable GetRequiredTuples() where T : IntermediateTuple { - WixToolset.Dtf.Resources.ResourceCollection resources = new WixToolset.Dtf.Resources.ResourceCollection(); - WixToolset.Dtf.Resources.VersionResource version = new WixToolset.Dtf.Resources.VersionResource("#1", 1033); + var tuples = this.Output.Sections.Single().Tuples.OfType().ToList(); - version.Load(bundleTempPath); - resources.Add(version); - - // Ensure the bundle info provides a full four part version. - Version fourPartVersion = new Version(bundleInfo.Version); - int major = (fourPartVersion.Major < 0) ? 0 : fourPartVersion.Major; - int minor = (fourPartVersion.Minor < 0) ? 0 : fourPartVersion.Minor; - int build = (fourPartVersion.Build < 0) ? 0 : fourPartVersion.Build; - int revision = (fourPartVersion.Revision < 0) ? 0 : fourPartVersion.Revision; - - if (UInt16.MaxValue < major || UInt16.MaxValue < minor || UInt16.MaxValue < build || UInt16.MaxValue < revision) - { - throw new WixException(WixErrors.InvalidModuleOrBundleVersion(bundleInfo.SourceLineNumbers, "Bundle", bundleInfo.Version)); - } - - fourPartVersion = new Version(major, minor, build, revision); - version.FileVersion = fourPartVersion; - version.ProductVersion = fourPartVersion; - - WixToolset.Dtf.Resources.VersionStringTable strings = version[1033]; - strings["LegalCopyright"] = bundleInfo.Copyright; - strings["OriginalFilename"] = Path.GetFileName(outputPath); - strings["FileVersion"] = bundleInfo.Version; // string versions do not have to be four parts. - strings["ProductVersion"] = bundleInfo.Version; // string versions do not have to be four parts. - - if (!String.IsNullOrEmpty(bundleInfo.Name)) + if (0 == tuples.Count) { - strings["ProductName"] = bundleInfo.Name; - strings["FileDescription"] = bundleInfo.Name; + throw new WixException(ErrorMessages.MissingBundleInformation(nameof(T))); } - if (!String.IsNullOrEmpty(bundleInfo.Publisher)) - { - strings["CompanyName"] = bundleInfo.Publisher; - } - else - { - strings["CompanyName"] = String.Empty; - } - - if (!String.IsNullOrEmpty(bundleInfo.IconPath)) - { - Dtf.Resources.GroupIconResource iconGroup = new Dtf.Resources.GroupIconResource("#1", 1033); - iconGroup.ReadFromFile(bundleInfo.IconPath); - resources.Add(iconGroup); - - foreach (Dtf.Resources.Resource icon in iconGroup.Icons) - { - resources.Add(icon); - } - } - - if (!String.IsNullOrEmpty(bundleInfo.SplashScreenBitmapPath)) - { - Dtf.Resources.BitmapResource bitmap = new Dtf.Resources.BitmapResource("#1", 1033); - bitmap.ReadFromFile(bundleInfo.SplashScreenBitmapPath); - resources.Add(bitmap); - } - - resources.Save(bundleTempPath); + return tuples; } -//#region DependencyExtension - /// - /// Imports authored dependency providers for each package in the manifest, - /// and generates dependency providers for certain package types that do not - /// have a provider defined. - /// - /// The object for the bundle. - /// An indexed collection of chained packages. - private void ProcessDependencyProviders(Output bundle, IDictionary facades) + private T GetSingleTuple() where T : IntermediateTuple { - // First import any authored dependencies. These may merge with imported provides from MSI packages. - Table wixDependencyProviderTable = bundle.Tables["WixDependencyProvider"]; - if (null != wixDependencyProviderTable && 0 < wixDependencyProviderTable.Rows.Count) - { - // Add package information for each dependency provider authored into the manifest. - foreach (Row wixDependencyProviderRow in wixDependencyProviderTable.Rows) - { - string packageId = (string)wixDependencyProviderRow[1]; - - PackageFacade facade = null; - if (facades.TryGetValue(packageId, out facade)) - { - ProvidesDependency dependency = new ProvidesDependency(wixDependencyProviderRow); - - if (String.IsNullOrEmpty(dependency.Key)) - { - switch (facade.Package.Type) - { - // The WixDependencyExtension allows an empty Key for MSIs and MSPs. - case WixBundlePackageType.Msi: - dependency.Key = facade.MsiPackage.ProductCode; - break; - case WixBundlePackageType.Msp: - dependency.Key = facade.MspPackage.PatchCode; - break; - } - } - - if (String.IsNullOrEmpty(dependency.Version)) - { - dependency.Version = facade.Package.Version; - } - - // If the version is still missing, a version could not be harvested from the package and was not authored. - if (String.IsNullOrEmpty(dependency.Version)) - { - Messaging.Instance.OnMessage(WixErrors.MissingDependencyVersion(facade.Package.WixChainItemId)); - } - - if (String.IsNullOrEmpty(dependency.DisplayName)) - { - dependency.DisplayName = facade.Package.DisplayName; - } + var tuples = this.Output.Sections.Single().Tuples.OfType().ToList(); - if (!facade.Provides.Merge(dependency)) - { - Messaging.Instance.OnMessage(WixErrors.DuplicateProviderDependencyKey(dependency.Key, facade.Package.WixChainItemId)); - } - } - } - } - - // Generate providers for MSI packages that still do not have providers. - foreach (PackageFacade facade in facades.Values) + if (1 != tuples.Count) { - string key = null; - - if (WixBundlePackageType.Msi == facade.Package.Type && 0 == facade.Provides.Count) - { - key = facade.MsiPackage.ProductCode; - } - else if (WixBundlePackageType.Msp == facade.Package.Type && 0 == facade.Provides.Count) - { - key = facade.MspPackage.PatchCode; - } - - if (!String.IsNullOrEmpty(key)) - { - ProvidesDependency dependency = new ProvidesDependency(key, facade.Package.Version, facade.Package.DisplayName, 0); - - if (!facade.Provides.Merge(dependency)) - { - Messaging.Instance.OnMessage(WixErrors.DuplicateProviderDependencyKey(dependency.Key, facade.Package.WixChainItemId)); - } - } + throw new WixException(ErrorMessages.MissingBundleInformation(nameof(T))); } - } - /// - /// Sets the provider key for the bundle. - /// - /// The object for the bundle. - /// The containing the provider key and other information for the bundle. - private void SetBundleProviderKey(Output bundle, WixBundleRow bundleInfo) - { - // From DependencyCommon.cs in the WixDependencyExtension. - const int ProvidesAttributesBundle = 0x10000; - - Table wixDependencyProviderTable = bundle.Tables["WixDependencyProvider"]; - if (null != wixDependencyProviderTable && 0 < wixDependencyProviderTable.Rows.Count) - { - // Search the WixDependencyProvider table for the single bundle provider key. - foreach (Row wixDependencyProviderRow in wixDependencyProviderTable.Rows) - { - object attributes = wixDependencyProviderRow[5]; - if (null != attributes && 0 != (ProvidesAttributesBundle & (int)attributes)) - { - bundleInfo.ProviderKey = (string)wixDependencyProviderRow[2]; - break; - } - } - } - - // Defaults to the bundle ID as the provider key. -#endif + return tuples[0]; } } } diff --git a/src/WixToolset.Core.Burn/Bind/ProcessDependencyProvidersCommand.cs b/src/WixToolset.Core.Burn/Bind/ProcessDependencyProvidersCommand.cs new file mode 100644 index 00000000..7f36dbcc --- /dev/null +++ b/src/WixToolset.Core.Burn/Bind/ProcessDependencyProvidersCommand.cs @@ -0,0 +1,166 @@ +// 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. + +namespace WixToolset.Core.Burn.Bind +{ + using System; + using System.Collections.Generic; + using System.Linq; + using WixToolset.Data; + using WixToolset.Core.Burn.Bundles; + using WixToolset.Extensibility.Services; + using WixToolset.Data.Tuples; + + internal class ProcessDependencyProvidersCommand + { + public ProcessDependencyProvidersCommand(IMessaging messaging, IntermediateSection section, IDictionary facades) + { + this.Messaging = messaging; + this.Section = section; + + this.Facades = facades; + } + + public string BundleProviderKey { get; private set; } + + public Dictionary DependencyTuplesByKey { get; private set; } + + private IMessaging Messaging { get; } + + private IntermediateSection Section { get; } + + private IDictionary Facades { get; } + + /// + /// Sets the explicitly provided bundle provider key, if provided. And... + /// Imports authored dependency providers for each package in the manifest, + /// and generates dependency providers for certain package types that do not + /// have a provider defined. + /// + public void Execute() + { + var wixDependencyProviderTuples = this.Section.Tuples.OfType(); + + foreach (var wixDependencyProviderTuple in wixDependencyProviderTuples) + { + // Sets the provider key for the bundle, if it is not set already. + if (String.IsNullOrEmpty(this.BundleProviderKey)) + { + if (wixDependencyProviderTuple.Bundle) + { + this.BundleProviderKey = wixDependencyProviderTuple.ProviderKey; + } + } + + // Import any authored dependencies. These may merge with imported provides from MSI packages. + var packageId = wixDependencyProviderTuple.Id.Id; + + if (this.Facades.TryGetValue(packageId, out var facade)) + { + var dependency = new ProvidesDependencyTuple(wixDependencyProviderTuple.SourceLineNumbers, wixDependencyProviderTuple.Id) + { + PackageRef = packageId, + Key = wixDependencyProviderTuple.ProviderKey, + Version = wixDependencyProviderTuple.Version, + DisplayName = wixDependencyProviderTuple.DisplayName, + Attributes = (int)wixDependencyProviderTuple.Attributes + }; + + if (String.IsNullOrEmpty(dependency.Key)) + { + switch (facade.SpecificPackageTuple) + { + // The WixDependencyExtension allows an empty Key for MSIs and MSPs. + case WixBundleMsiPackageTuple msiPackage: + dependency.Key = msiPackage.ProductCode; + break; + case WixBundleMspPackageTuple mspPackage: + dependency.Key = mspPackage.PatchCode; + break; + } + } + + if (String.IsNullOrEmpty(dependency.Version)) + { + dependency.Version = facade.PackageTuple.Version; + } + + // If the version is still missing, a version could not be harvested from the package and was not authored. + if (String.IsNullOrEmpty(dependency.Version)) + { + this.Messaging.Write(ErrorMessages.MissingDependencyVersion(facade.PackageId)); + } + + if (String.IsNullOrEmpty(dependency.DisplayName)) + { + dependency.DisplayName = facade.PackageTuple.DisplayName; + } + + this.Section.Tuples.Add(dependency); + } + } + + this.DependencyTuplesByKey = this.GetDependencyTuplesByKey(); + + // Generate providers for MSI and MSP packages that still do not have providers. + foreach (var facade in this.Facades.Values) + { + string key = null; + + //if (WixBundlePackageType.Msi == facade.PackageTuple.Type) + if (facade.SpecificPackageTuple is WixBundleMsiPackageTuple msiPackage) + { + //var msiPackage = (WixBundleMsiPackageTuple)facade.SpecificPackageTuple; + key = msiPackage.ProductCode; + } + //else if (WixBundlePackageType.Msp == facade.PackageTuple.Type) + else if (facade.SpecificPackageTuple is WixBundleMspPackageTuple mspPackage) + { + //var mspPackage = (WixBundleMspPackageTuple)facade.SpecificPackageTuple; + key = mspPackage.PatchCode; + } + + if (!String.IsNullOrEmpty(key) && !this.DependencyTuplesByKey.ContainsKey(key)) + { + var dependency = new ProvidesDependencyTuple(facade.PackageTuple.SourceLineNumbers, facade.PackageTuple.Id) + { + PackageRef = facade.PackageId, + Key = key, + Version = facade.PackageTuple.Version, + DisplayName = facade.PackageTuple.DisplayName + }; + + this.Section.Tuples.Add(dependency); + + this.DependencyTuplesByKey.Add(dependency.Key, dependency); + } + } + } + + private Dictionary GetDependencyTuplesByKey() + { + var dependencyTuplesByKey = new Dictionary(); + + var dependencyTuples = this.Section.Tuples.OfType(); + + foreach (var dependency in dependencyTuples) + { + if (dependencyTuplesByKey.TryGetValue(dependency.Key, out var collision)) + { + // If not a perfect dependency collision, display an error. + if (dependency.Key != collision.Key || + dependency.Version != collision.Version || + dependency.DisplayName != collision.DisplayName) + { + this.Messaging.Write(ErrorMessages.DuplicateProviderDependencyKey(dependency.Key, dependency.PackageRef)); + } + } + else + { + dependencyTuplesByKey.Add(dependency.Key, dependency); + } + } + + return dependencyTuplesByKey; + } + } +} diff --git a/src/WixToolset.Core.Burn/Bind/ProvidesDependency.cs b/src/WixToolset.Core.Burn/Bind/ProvidesDependency.cs deleted file mode 100644 index c7eba01c..00000000 --- a/src/WixToolset.Core.Burn/Bind/ProvidesDependency.cs +++ /dev/null @@ -1,110 +0,0 @@ -// 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. - -namespace WixToolset.Core.Burn -{ - using System; - using System.Xml; - using WixToolset.Data; - - /// - /// Represents an authored or imported dependency provider. - /// - internal sealed class ProvidesDependency - { -#if TODO - /// - /// Creates a new instance of the class from a . - /// - /// The from which data is imported. - internal ProvidesDependency(Row row) - : this((string)row[2], (string)row[3], (string)row[4], (int?)row[5]) - { - } -#endif - - /// - /// Creates a new instance of the class. - /// - /// The unique key of the dependency. - /// Additional attributes for the dependency. - internal ProvidesDependency(string key, string version, string displayName, int? attributes) - { - this.Key = key; - this.Version = version; - this.DisplayName = displayName; - this.Attributes = attributes; - } - - /// - /// Gets or sets the unique key of the package provider. - /// - internal string Key { get; set; } - - /// - /// Gets or sets the version of the package provider. - /// - internal string Version { get; set; } - - /// - /// Gets or sets the display name of the package provider. - /// - internal string DisplayName { get; set; } - - /// - /// Gets or sets the attributes for the dependency. - /// - internal int? Attributes { get; set; } - - /// - /// Gets or sets whether the dependency was imported from the package. - /// - internal bool Imported { get; set; } - - /// - /// Gets whether certain properties are the same. - /// - /// Another to compare. - /// This is not the same as object equality, but only checks a subset of properties - /// to determine if the objects are similar and could be merged into a collection. - /// True if certain properties are the same. - internal bool Equals(ProvidesDependency other) - { - if (null != other) - { - return this.Key == other.Key && - this.Version == other.Version && - this.DisplayName == other.DisplayName; - } - - return false; - } - - /// - /// Writes the dependency to the bundle XML manifest. - /// - /// The for the bundle XML manifest. - internal void WriteXml(XmlTextWriter writer) - { - writer.WriteStartElement("Provides"); - writer.WriteAttributeString("Key", this.Key); - - if (!String.IsNullOrEmpty(this.Version)) - { - writer.WriteAttributeString("Version", this.Version); - } - - if (!String.IsNullOrEmpty(this.DisplayName)) - { - writer.WriteAttributeString("DisplayName", this.DisplayName); - } - - if (this.Imported) - { - // The package dependency was explicitly authored into the manifest. - writer.WriteAttributeString("Imported", "yes"); - } - - writer.WriteEndElement(); - } - } -} diff --git a/src/WixToolset.Core.Burn/Bind/ProvidesDependencyCollection.cs b/src/WixToolset.Core.Burn/Bind/ProvidesDependencyCollection.cs deleted file mode 100644 index 668b81d3..00000000 --- a/src/WixToolset.Core.Burn/Bind/ProvidesDependencyCollection.cs +++ /dev/null @@ -1,64 +0,0 @@ -// 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. - -namespace WixToolset.Core.Burn -{ - using System; - using System.Collections.ObjectModel; - - /// - /// A case-insensitive collection of unique objects. - /// - internal sealed class ProvidesDependencyCollection : KeyedCollection - { - /// - /// Creates a case-insensitive collection of unique objects. - /// - internal ProvidesDependencyCollection() - : base(StringComparer.InvariantCultureIgnoreCase) - { - } - - /// - /// Adds the to the collection if it doesn't already exist. - /// - /// The to add to the collection. - /// True if the was added to the collection; otherwise, false. - /// The parameter is null. - internal bool Merge(ProvidesDependency dependency) - { - if (null == dependency) - { - throw new ArgumentNullException("dependency"); - } - - // If the dependency key is already in the collection, verify equality for a subset of properties. - if (this.Contains(dependency.Key)) - { - ProvidesDependency current = this[dependency.Key]; - if (!current.Equals(dependency)) - { - return false; - } - } - - base.Add(dependency); - return true; - } - - /// - /// Gets the for the . - /// - /// The dependency to index. - /// The parameter is null. - /// The for the . - protected override string GetKeyForItem(ProvidesDependency dependency) - { - if (null == dependency) - { - throw new ArgumentNullException("dependency"); - } - - return dependency.Key; - } - } -} diff --git a/src/WixToolset.Core.Burn/Bind/SearchFacade.cs b/src/WixToolset.Core.Burn/Bind/SearchFacade.cs new file mode 100644 index 00000000..65f3cb5b --- /dev/null +++ b/src/WixToolset.Core.Burn/Bind/SearchFacade.cs @@ -0,0 +1,197 @@ +// 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. + +namespace WixToolset.Core.Burn +{ + using System; + using System.Xml; + using WixToolset.Data; + using WixToolset.Data.Tuples; + + internal class SearchFacade + { + public SearchFacade(WixSearchTuple searchTuple, IntermediateTuple searchSpecificTuple) + { + this.SearchTuple = searchTuple; + this.SearchSpecificTuple = searchSpecificTuple; + } + + public WixSearchTuple SearchTuple { get; } + + public IntermediateTuple SearchSpecificTuple { get; } + + /// + /// Generates Burn manifest and ParameterInfo-style markup a search. + /// + /// + public void WriteXml(XmlTextWriter writer) + { + switch (this.SearchSpecificTuple) + { + case WixComponentSearchTuple tuple: + this.WriteComponentSearchXml(writer, tuple); + break; + case WixFileSearchTuple tuple: + this.WriteFileSearchXml(writer, tuple); + break; + case WixProductSearchTuple tuple: + this.WriteProductSearchXml(writer, tuple); + break; + case WixRegistrySearchTuple tuple: + this.WriteRegistrySearchXml(writer, tuple); + break; + } + } + + private void WriteCommonAttributes(XmlTextWriter writer) + { + writer.WriteAttributeString("Id", this.SearchTuple.Id.Id); + writer.WriteAttributeString("Variable", this.SearchTuple.Variable); + if (!String.IsNullOrEmpty(this.SearchTuple.Condition)) + { + writer.WriteAttributeString("Condition", this.SearchTuple.Condition); + } + } + + private void WriteComponentSearchXml(XmlTextWriter writer, WixComponentSearchTuple searchTuple) + { + writer.WriteStartElement("MsiComponentSearch"); + + this.WriteCommonAttributes(writer); + + writer.WriteAttributeString("ComponentId", searchTuple.Guid); + + if (!String.IsNullOrEmpty(searchTuple.ProductCode)) + { + writer.WriteAttributeString("ProductCode", searchTuple.ProductCode); + } + + if (0 != (searchTuple.Attributes & WixComponentSearchAttributes.KeyPath)) + { + writer.WriteAttributeString("Type", "keyPath"); + } + else if (0 != (searchTuple.Attributes & WixComponentSearchAttributes.State)) + { + writer.WriteAttributeString("Type", "state"); + } + else if (0 != (searchTuple.Attributes & WixComponentSearchAttributes.WantDirectory)) + { + writer.WriteAttributeString("Type", "directory"); + } + + writer.WriteEndElement(); + } + + private void WriteFileSearchXml(XmlTextWriter writer, WixFileSearchTuple searchTuple) + { + writer.WriteStartElement((0 == (searchTuple.Attributes & WixFileSearchAttributes.IsDirectory)) ? "FileSearch" : "DirectorySearch"); + + this.WriteCommonAttributes(writer); + + writer.WriteAttributeString("Path", searchTuple.Path); + if (WixFileSearchAttributes.WantExists == (searchTuple.Attributes & WixFileSearchAttributes.WantExists)) + { + writer.WriteAttributeString("Type", "exists"); + } + else if (WixFileSearchAttributes.WantVersion == (searchTuple.Attributes & WixFileSearchAttributes.WantVersion)) + { + // Can never get here for DirectorySearch. + writer.WriteAttributeString("Type", "version"); + } + else + { + writer.WriteAttributeString("Type", "path"); + } + writer.WriteEndElement(); + } + + private void WriteProductSearchXml(XmlTextWriter writer, WixProductSearchTuple tuple) + { + writer.WriteStartElement("MsiProductSearch"); + + this.WriteCommonAttributes(writer); + + if (0 != (tuple.Attributes & WixProductSearchAttributes.UpgradeCode)) + { + writer.WriteAttributeString("UpgradeCode", tuple.Guid); + } + else + { + writer.WriteAttributeString("ProductCode", tuple.Guid); + } + + if (0 != (tuple.Attributes & WixProductSearchAttributes.Version)) + { + writer.WriteAttributeString("Type", "version"); + } + else if (0 != (tuple.Attributes & WixProductSearchAttributes.Language)) + { + writer.WriteAttributeString("Type", "language"); + } + else if (0 != (tuple.Attributes & WixProductSearchAttributes.State)) + { + writer.WriteAttributeString("Type", "state"); + } + else if (0 != (tuple.Attributes & WixProductSearchAttributes.Assignment)) + { + writer.WriteAttributeString("Type", "assignment"); + } + + writer.WriteEndElement(); + } + + private void WriteRegistrySearchXml(XmlTextWriter writer, WixRegistrySearchTuple tuple) + { + writer.WriteStartElement("RegistrySearch"); + + this.WriteCommonAttributes(writer); + + switch (tuple.Root) + { + case RegistryRootType.ClassesRoot: + writer.WriteAttributeString("Root", "HKCR"); + break; + case RegistryRootType.CurrentUser: + writer.WriteAttributeString("Root", "HKCU"); + break; + case RegistryRootType.LocalMachine: + writer.WriteAttributeString("Root", "HKLM"); + break; + case RegistryRootType.Users: + writer.WriteAttributeString("Root", "HKU"); + break; + } + + writer.WriteAttributeString("Key", tuple.Key); + + if (!String.IsNullOrEmpty(tuple.Value)) + { + writer.WriteAttributeString("Value", tuple.Value); + } + + var existenceOnly = 0 != (tuple.Attributes & WixRegistrySearchAttributes.WantExists); + + writer.WriteAttributeString("Type", existenceOnly ? "exists" : "value"); + + if (0 != (tuple.Attributes & WixRegistrySearchAttributes.Win64)) + { + writer.WriteAttributeString("Win64", "yes"); + } + + if (!existenceOnly) + { + if (0 != (tuple.Attributes & WixRegistrySearchAttributes.ExpandEnvironmentVariables)) + { + writer.WriteAttributeString("ExpandEnvironment", "yes"); + } + + // We *always* say this is VariableType="string". If we end up + // needing to be more specific, we will have to expand the "Format" + // attribute to allow "number" and "version". + + writer.WriteAttributeString("VariableType", "string"); + } + + writer.WriteEndElement(); + } + } +} diff --git a/src/WixToolset.Core.Burn/Bind/WixComponentSearchInfo.cs b/src/WixToolset.Core.Burn/Bind/WixComponentSearchInfo.cs deleted file mode 100644 index b9c29df0..00000000 --- a/src/WixToolset.Core.Burn/Bind/WixComponentSearchInfo.cs +++ /dev/null @@ -1,65 +0,0 @@ -// 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. - -namespace WixToolset.Core.Burn -{ - using System; - using System.Xml; - using WixToolset.Data; - - /// - /// Utility class for all WixComponentSearches. - /// - internal class WixComponentSearchInfo : WixSearchInfo - { -#if TODO - public WixComponentSearchInfo(Row row) - : this((string)row[0], (string)row[1], (string)row[2], (int)row[3]) - { - } -#endif - - public WixComponentSearchInfo(string id, string guid, string productCode, int attributes) - : base(id) - { - this.Guid = guid; - this.ProductCode = productCode; - this.Attributes = (WixComponentSearchAttributes)attributes; - } - - public string Guid { get; private set; } - public string ProductCode { get; private set; } - public WixComponentSearchAttributes Attributes { get; private set; } - - /// - /// Generates Burn manifest and ParameterInfo-style markup for a component search. - /// - /// - public override void WriteXml(XmlTextWriter writer) - { - writer.WriteStartElement("MsiComponentSearch"); - this.WriteWixSearchAttributes(writer); - - writer.WriteAttributeString("ComponentId", this.Guid); - - if (!String.IsNullOrEmpty(this.ProductCode)) - { - writer.WriteAttributeString("ProductCode", this.ProductCode); - } - - if (0 != (this.Attributes & WixComponentSearchAttributes.KeyPath)) - { - writer.WriteAttributeString("Type", "keyPath"); - } - else if (0 != (this.Attributes & WixComponentSearchAttributes.State)) - { - writer.WriteAttributeString("Type", "state"); - } - else if (0 != (this.Attributes & WixComponentSearchAttributes.WantDirectory)) - { - writer.WriteAttributeString("Type", "directory"); - } - - writer.WriteEndElement(); - } - } -} diff --git a/src/WixToolset.Core.Burn/Bind/WixFileSearchInfo.cs b/src/WixToolset.Core.Burn/Bind/WixFileSearchInfo.cs deleted file mode 100644 index 41393f6b..00000000 --- a/src/WixToolset.Core.Burn/Bind/WixFileSearchInfo.cs +++ /dev/null @@ -1,56 +0,0 @@ -// 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. - -namespace WixToolset.Core.Burn -{ - using System; - using System.Xml; - using WixToolset.Data; - - /// - /// Utility class for all WixFileSearches (file and directory searches). - /// - internal class WixFileSearchInfo : WixSearchInfo - { -#if TODO - public WixFileSearchInfo(Row row) - : this((string)row[0], (string)row[1], (int)row[9]) - { - } -#endif - - public WixFileSearchInfo(string id, string path, int attributes) - : base(id) - { - this.Path = path; - this.Attributes = (WixFileSearchAttributes)attributes; - } - - public string Path { get; private set; } - public WixFileSearchAttributes Attributes { get; private set; } - - /// - /// Generates Burn manifest and ParameterInfo-style markup for a file/directory search. - /// - /// - public override void WriteXml(XmlTextWriter writer) - { - writer.WriteStartElement((0 == (this.Attributes & WixFileSearchAttributes.IsDirectory)) ? "FileSearch" : "DirectorySearch"); - this.WriteWixSearchAttributes(writer); - writer.WriteAttributeString("Path", this.Path); - if (WixFileSearchAttributes.WantExists == (this.Attributes & WixFileSearchAttributes.WantExists)) - { - writer.WriteAttributeString("Type", "exists"); - } - else if (WixFileSearchAttributes.WantVersion == (this.Attributes & WixFileSearchAttributes.WantVersion)) - { - // Can never get here for DirectorySearch. - writer.WriteAttributeString("Type", "version"); - } - else - { - writer.WriteAttributeString("Type", "path"); - } - writer.WriteEndElement(); - } - } -} diff --git a/src/WixToolset.Core.Burn/Bind/WixProductSearchInfo.cs b/src/WixToolset.Core.Burn/Bind/WixProductSearchInfo.cs deleted file mode 100644 index cd4a70b3..00000000 --- a/src/WixToolset.Core.Burn/Bind/WixProductSearchInfo.cs +++ /dev/null @@ -1,69 +0,0 @@ -// 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. - -namespace WixToolset.Core.Burn -{ - using System; - using System.Xml; - using WixToolset.Data; - - /// - /// Utility class for all WixProductSearches. - /// - internal class WixProductSearchInfo : WixSearchInfo - { -#if TODO - public WixProductSearchInfo(Row row) - : this((string)row[0], (string)row[1], (int)row[2]) - { - } -#endif - - public WixProductSearchInfo(string id, string guid, int attributes) - : base(id) - { - this.Guid = guid; - this.Attributes = (WixProductSearchAttributes)attributes; - } - - public string Guid { get; private set; } - public WixProductSearchAttributes Attributes { get; private set; } - - /// - /// Generates Burn manifest and ParameterInfo-style markup for a product search. - /// - /// - public override void WriteXml(XmlTextWriter writer) - { - writer.WriteStartElement("MsiProductSearch"); - this.WriteWixSearchAttributes(writer); - - if (0 != (this.Attributes & WixProductSearchAttributes.UpgradeCode)) - { - writer.WriteAttributeString("UpgradeCode", this.Guid); - } - else - { - writer.WriteAttributeString("ProductCode", this.Guid); - } - - if (0 != (this.Attributes & WixProductSearchAttributes.Version)) - { - writer.WriteAttributeString("Type", "version"); - } - else if (0 != (this.Attributes & WixProductSearchAttributes.Language)) - { - writer.WriteAttributeString("Type", "language"); - } - else if (0 != (this.Attributes & WixProductSearchAttributes.State)) - { - writer.WriteAttributeString("Type", "state"); - } - else if (0 != (this.Attributes & WixProductSearchAttributes.Assignment)) - { - writer.WriteAttributeString("Type", "assignment"); - } - - writer.WriteEndElement(); - } - } -} diff --git a/src/WixToolset.Core.Burn/Bind/WixRegistrySearchInfo.cs b/src/WixToolset.Core.Burn/Bind/WixRegistrySearchInfo.cs deleted file mode 100644 index 3f85b996..00000000 --- a/src/WixToolset.Core.Burn/Bind/WixRegistrySearchInfo.cs +++ /dev/null @@ -1,93 +0,0 @@ -// 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. - -namespace WixToolset.Core.Burn -{ - using System; - using System.Xml; - using WixToolset.Data.WindowsInstaller; - - /// - /// Utility class for all WixRegistrySearches. - /// - internal class WixRegistrySearchInfo : WixSearchInfo - { -#if TODO - public WixRegistrySearchInfo(Row row) - : this((string)row[0], (int)row[1], (string)row[2], (string)row[3], (int)row[4]) - { - } -#endif - - public WixRegistrySearchInfo(string id, int root, string key, string value, int attributes) - : base(id) - { - this.Root = root; - this.Key = key; - this.Value = value; - this.Attributes = (WixRegistrySearchAttributes)attributes; - } - - public int Root { get; private set; } - public string Key { get; private set; } - public string Value { get; private set; } - public WixRegistrySearchAttributes Attributes { get; private set; } - - /// - /// Generates Burn manifest and ParameterInfo-style markup for a registry search. - /// - /// - public override void WriteXml(XmlTextWriter writer) - { - writer.WriteStartElement("RegistrySearch"); - this.WriteWixSearchAttributes(writer); - - switch (this.Root) - { - case WindowsInstallerConstants.MsidbRegistryRootClassesRoot: - writer.WriteAttributeString("Root", "HKCR"); - break; - case WindowsInstallerConstants.MsidbRegistryRootCurrentUser: - writer.WriteAttributeString("Root", "HKCU"); - break; - case WindowsInstallerConstants.MsidbRegistryRootLocalMachine: - writer.WriteAttributeString("Root", "HKLM"); - break; - case WindowsInstallerConstants.MsidbRegistryRootUsers: - writer.WriteAttributeString("Root", "HKU"); - break; - } - - writer.WriteAttributeString("Key", this.Key); - - if (!String.IsNullOrEmpty(this.Value)) - { - writer.WriteAttributeString("Value", this.Value); - } - - bool existenceOnly = 0 != (this.Attributes & WixRegistrySearchAttributes.WantExists); - - writer.WriteAttributeString("Type", existenceOnly ? "exists" : "value"); - - if (0 != (this.Attributes & WixRegistrySearchAttributes.Win64)) - { - writer.WriteAttributeString("Win64", "yes"); - } - - if (!existenceOnly) - { - if (0 != (this.Attributes & WixRegistrySearchAttributes.ExpandEnvironmentVariables)) - { - writer.WriteAttributeString("ExpandEnvironment", "yes"); - } - - // We *always* say this is VariableType="string". If we end up - // needing to be more specific, we will have to expand the "Format" - // attribute to allow "number" and "version". - - writer.WriteAttributeString("VariableType", "string"); - } - - writer.WriteEndElement(); - } - } -} diff --git a/src/WixToolset.Core.Burn/Bind/WixSearchInfo.cs b/src/WixToolset.Core.Burn/Bind/WixSearchInfo.cs deleted file mode 100644 index 04347583..00000000 --- a/src/WixToolset.Core.Burn/Bind/WixSearchInfo.cs +++ /dev/null @@ -1,55 +0,0 @@ -// 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. - -namespace WixToolset.Core.Burn -{ - using System; - using System.Diagnostics; - using System.Xml; - using WixToolset.Data; - - /// - /// Utility base class for all WixSearches. - /// - internal abstract class WixSearchInfo - { - public WixSearchInfo(string id) - { - this.Id = id; - } - -#if TODO - public void AddWixSearchRowInfo(Row row) - { - Debug.Assert((string)row[0] == Id); - Variable = (string)row[1]; - Condition = (string)row[2]; - } -#endif - - public string Id { get; private set; } - public string Variable { get; private set; } - public string Condition { get; private set; } - - /// - /// Generates Burn manifest and ParameterInfo-style markup a search. - /// - /// - public virtual void WriteXml(XmlTextWriter writer) - { - } - - /// - /// Writes attributes common to all WixSearch elements. - /// - /// - protected void WriteWixSearchAttributes(XmlTextWriter writer) - { - writer.WriteAttributeString("Id", this.Id); - writer.WriteAttributeString("Variable", this.Variable); - if (!String.IsNullOrEmpty(this.Condition)) - { - writer.WriteAttributeString("Condition", this.Condition); - } - } - } -} diff --git a/src/WixToolset.Core.Burn/BundleBackend.cs b/src/WixToolset.Core.Burn/BundleBackend.cs index f859cbec..99442403 100644 --- a/src/WixToolset.Core.Burn/BundleBackend.cs +++ b/src/WixToolset.Core.Burn/BundleBackend.cs @@ -15,20 +15,26 @@ namespace WixToolset.Core.Burn { public IBindResult Bind(IBindContext context) { - BindBundleCommand command = new BindBundleCommand(context); - //command.DefaultCompressionLevel = context.DefaultCompressionLevel; - //command.Extensions = context.Extensions; - //command.IntermediateFolder = context.IntermediateFolder; - //command.Output = context.IntermediateRepresentation; - //command.OutputPath = context.OutputPath; - //command.PdbFile = context.OutputPdbPath; - //command.WixVariableResolver = context.WixVariableResolver; + var extensionManager = context.ServiceProvider.GetService(); + + var backendExtensions = extensionManager.GetServices(); + + foreach (var extension in backendExtensions) + { + extension.PreBackendBind(context); + } + + var command = new BindBundleCommand(context, backendExtensions); command.Execute(); var result = context.ServiceProvider.GetService(); result.FileTransfers = command.FileTransfers; result.TrackedFiles = command.TrackedFiles; + foreach (var extension in backendExtensions) + { + extension.PostBackendBind(result); + } return result; } @@ -53,10 +59,10 @@ namespace WixToolset.Core.Burn public Intermediate Unbind(IUnbindContext context) { - string uxExtractPath = Path.Combine(context.ExportBasePath, "UX"); - string acExtractPath = Path.Combine(context.ExportBasePath, "AttachedContainer"); + var uxExtractPath = Path.Combine(context.ExportBasePath, "UX"); + var acExtractPath = Path.Combine(context.ExportBasePath, "AttachedContainer"); - using (BurnReader reader = BurnReader.Open(context.InputFilePath)) + using (var reader = BurnReader.Open(context.InputFilePath)) { reader.ExtractUXContainer(uxExtractPath, context.IntermediateFolder); reader.ExtractAttachedContainer(acExtractPath, context.IntermediateFolder); diff --git a/src/WixToolset.Core.Burn/Bundles/AutomaticallySlipstreamPatchesCommand.cs b/src/WixToolset.Core.Burn/Bundles/AutomaticallySlipstreamPatchesCommand.cs index cf702b2e..b3a29e15 100644 --- a/src/WixToolset.Core.Burn/Bundles/AutomaticallySlipstreamPatchesCommand.cs +++ b/src/WixToolset.Core.Burn/Bundles/AutomaticallySlipstreamPatchesCommand.cs @@ -7,107 +7,116 @@ namespace WixToolset.Core.Burn.Bundles using System.Diagnostics; using System.Linq; using WixToolset.Data; + using WixToolset.Data.Tuples; internal class AutomaticallySlipstreamPatchesCommand { -#if TODO - public IEnumerable PackageFacades { private get; set; } + public AutomaticallySlipstreamPatchesCommand(IntermediateSection section, ICollection packageFacades) + { + this.Section = section; + this.PackageFacades = packageFacades; + } - public Table WixBundlePatchTargetCodeTable { private get; set; } + private IntermediateSection Section { get; } - public Table SlipstreamMspTable { private get; set; } + private IEnumerable PackageFacades { get; } public void Execute() { - List msiPackages = new List(); - Dictionary> targetsProductCode = new Dictionary>(); - Dictionary> targetsUpgradeCode = new Dictionary>(); + var msiPackages = new List(); + var targetsProductCode = new Dictionary>(); + var targetsUpgradeCode = new Dictionary>(); - foreach (PackageFacade facade in this.PackageFacades) + foreach (var facade in this.PackageFacades) { - if (WixBundlePackageType.Msi == facade.Package.Type) + // Keep track of all MSI packages. + if (facade.SpecificPackageTuple is WixBundleMsiPackageTuple msiPackage) { - // Keep track of all MSI packages. - msiPackages.Add(facade.MsiPackage); + msiPackages.Add(msiPackage); } - else if (WixBundlePackageType.Msp == facade.Package.Type && facade.MspPackage.Slipstream) + else if (facade.SpecificPackageTuple is WixBundleMspPackageTuple mspPackage && mspPackage.Slipstream) { - IEnumerable patchTargetCodeRows = this.WixBundlePatchTargetCodeTable.RowsAs().Where(r => r.MspPackageId == facade.Package.WixChainItemId); + var patchTargetCodeTuples = this.Section.Tuples + .OfType() + .Where(r => r.PackageRef == facade.PackageId); // Index target ProductCodes and UpgradeCodes for slipstreamed MSPs. - foreach (WixBundlePatchTargetCodeRow row in patchTargetCodeRows) + foreach (var tuple in patchTargetCodeTuples) { - if (row.TargetsProductCode) + if (tuple.TargetsProductCode) { - List rows; - if (!targetsProductCode.TryGetValue(row.TargetCode, out rows)) + if (!targetsProductCode.TryGetValue(tuple.TargetCode, out var tuples)) { - rows = new List(); - targetsProductCode.Add(row.TargetCode, rows); + tuples = new List(); + targetsProductCode.Add(tuple.TargetCode, tuples); } - rows.Add(row); + tuples.Add(tuple); } - else if (row.TargetsUpgradeCode) + else if (tuple.TargetsUpgradeCode) { - List rows; - if (!targetsUpgradeCode.TryGetValue(row.TargetCode, out rows)) + if (!targetsUpgradeCode.TryGetValue(tuple.TargetCode, out var tuples)) { - rows = new List(); - targetsUpgradeCode.Add(row.TargetCode, rows); + tuples = new List(); + targetsUpgradeCode.Add(tuple.TargetCode, tuples); } } } } } - RowIndexedList slipstreamMspRows = new RowIndexedList(SlipstreamMspTable); + var slipstreamMspIds = new HashSet(); // Loop through the MSI and slipstream patches targeting it. - foreach (WixBundleMsiPackageRow msi in msiPackages) + foreach (var msi in msiPackages) { - List rows; - if (targetsProductCode.TryGetValue(msi.ProductCode, out rows)) + if (targetsProductCode.TryGetValue(msi.ProductCode, out var tuples)) { - foreach (WixBundlePatchTargetCodeRow row in rows) + foreach (var tuple in tuples) { - Debug.Assert(row.TargetsProductCode); - Debug.Assert(!row.TargetsUpgradeCode); - - Row slipstreamMspRow = SlipstreamMspTable.CreateRow(row.SourceLineNumbers, false); - slipstreamMspRow[0] = msi.ChainPackageId; - slipstreamMspRow[1] = row.MspPackageId; + Debug.Assert(tuple.TargetsProductCode); + Debug.Assert(!tuple.TargetsUpgradeCode); - if (slipstreamMspRows.TryAdd(slipstreamMspRow)) - { - SlipstreamMspTable.Rows.Add(slipstreamMspRow); - } + this.TryAddSlipstreamTuple(slipstreamMspIds, msi, tuple); } - - rows = null; } - if (!String.IsNullOrEmpty(msi.UpgradeCode) && targetsUpgradeCode.TryGetValue(msi.UpgradeCode, out rows)) + if (!String.IsNullOrEmpty(msi.UpgradeCode) && targetsUpgradeCode.TryGetValue(msi.UpgradeCode, out tuples)) { - foreach (WixBundlePatchTargetCodeRow row in rows) + foreach (var tuple in tuples) { - Debug.Assert(!row.TargetsProductCode); - Debug.Assert(row.TargetsUpgradeCode); - - Row slipstreamMspRow = SlipstreamMspTable.CreateRow(row.SourceLineNumbers, false); - slipstreamMspRow[0] = msi.ChainPackageId; - slipstreamMspRow[1] = row.MspPackageId; + Debug.Assert(!tuple.TargetsProductCode); + Debug.Assert(tuple.TargetsUpgradeCode); - if (slipstreamMspRows.TryAdd(slipstreamMspRow)) - { - SlipstreamMspTable.Rows.Add(slipstreamMspRow); - } + this.TryAddSlipstreamTuple(slipstreamMspIds, msi, tuple); } - rows = null; + tuples = null; } } } -#endif + + private bool TryAddSlipstreamTuple(HashSet slipstreamMspIds, WixBundleMsiPackageTuple msiPackage, WixBundlePatchTargetCodeTuple patchTargetCode) + { + var id = new Identifier(AccessModifier.Private, msiPackage.Id.Id, patchTargetCode.PackageRef); + + if (slipstreamMspIds.Add(id.Id)) + { + var slipstreamTuple = new WixBundleSlipstreamMspTuple(patchTargetCode.SourceLineNumbers) + { + TargetPackageRef = msiPackage.Id.Id, + MspPackageRef = patchTargetCode.PackageRef + }; + + //var slipstreamMspRow = SlipstreamMspTable.CreateRow(tuple.SourceLineNumbers, false); + //slipstreamMspRow[0] = msi.ChainPackageId; + //slipstreamMspRow[1] = tuple.MspPackageId; + + this.Section.Tuples.Add(slipstreamTuple); + return true; + } + + return false; + } } } diff --git a/src/WixToolset.Core.Burn/Bundles/BundleHashAlgorithm.cs b/src/WixToolset.Core.Burn/Bundles/BundleHashAlgorithm.cs new file mode 100644 index 00000000..3a71ed4c --- /dev/null +++ b/src/WixToolset.Core.Burn/Bundles/BundleHashAlgorithm.cs @@ -0,0 +1,30 @@ +// 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. + +namespace WixToolset.Core.Burn.Bundles +{ + using System.IO; + using System.Security.Cryptography; + using System.Text; + + internal static class BundleHashAlgorithm + { + public static string Hash(FileInfo fileInfo) + { + byte[] hashBytes; + + using (var managed = new SHA1Managed()) + using (var stream = fileInfo.OpenRead()) + { + hashBytes = managed.ComputeHash(stream); + } + + var sb = new StringBuilder(hashBytes.Length * 2); + for (var i = 0; i < hashBytes.Length; i++) + { + sb.AppendFormat("{0:X2}", hashBytes[i]); + } + + return sb.ToString(); + } + } +} diff --git a/src/WixToolset.Core.Burn/Bundles/BurnCommon.cs b/src/WixToolset.Core.Burn/Bundles/BurnCommon.cs index df328eb6..78b95bf4 100644 --- a/src/WixToolset.Core.Burn/Bundles/BurnCommon.cs +++ b/src/WixToolset.Core.Burn/Bundles/BurnCommon.cs @@ -128,7 +128,7 @@ namespace WixToolset.Core.Burn.Bundles public void Dispose() { - Dispose(true); + this.Dispose(true); GC.SuppressFinalize(this); } @@ -238,7 +238,7 @@ namespace WixToolset.Core.Burn.Bundles { if (UInt32.MaxValue == this.wixburnDataOffset) { - if (!EnsureNTHeader(reader)) + if (!this.EnsureNTHeader(reader)) { return false; } @@ -286,7 +286,7 @@ namespace WixToolset.Core.Burn.Bundles { if (UInt32.MaxValue == this.firstSectionOffset) { - if (!EnsureDosHeader(reader)) + if (!this.EnsureDosHeader(reader)) { return false; } diff --git a/src/WixToolset.Core.Burn/Bundles/BurnWriter.cs b/src/WixToolset.Core.Burn/Bundles/BurnWriter.cs index 08eeaa15..83b73a61 100644 --- a/src/WixToolset.Core.Burn/Bundles/BurnWriter.cs +++ b/src/WixToolset.Core.Burn/Bundles/BurnWriter.cs @@ -67,19 +67,21 @@ namespace WixToolset.Core.Burn.Bundles /// Size of the stub engine "burn.exe". /// Unique identifier for this bundle. /// - public bool InitializeBundleSectionData(long stubSize, Guid bundleId) + public bool InitializeBundleSectionData(long stubSize, string bundleId) { if (this.invalidBundle) { return false; } + var bundleGuid = Guid.Parse(bundleId); + this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_MAGIC, BURN_SECTION_MAGIC); this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_VERSION, BURN_SECTION_VERSION); - this.messaging.Write(VerboseMessages.BundleGuid(bundleId.ToString("B"))); + this.messaging.Write(VerboseMessages.BundleGuid(bundleId)); this.binaryWriter.BaseStream.Seek(this.wixburnDataOffset + BURN_SECTION_OFFSET_BUNDLEGUID, SeekOrigin.Begin); - this.binaryWriter.Write(bundleId.ToByteArray()); + this.binaryWriter.Write(bundleGuid.ToByteArray()); this.StubSize = (uint)stubSize; @@ -146,7 +148,7 @@ namespace WixToolset.Core.Burn.Bundles return false; } - return AppendContainer(containerStream, (UInt32)containerSize, burnSectionOffsetSize, burnSectionCount); + return this.AppendContainer(containerStream, (UInt32)containerSize, burnSectionOffsetSize, burnSectionCount); } public void RememberThenResetSignature() diff --git a/src/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs b/src/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs index 4fe8688c..5cd1f7e8 100644 --- a/src/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs +++ b/src/WixToolset.Core.Burn/Bundles/CreateBootstrapperApplicationManifestCommand.cs @@ -7,236 +7,268 @@ namespace WixToolset.Core.Burn.Bundles using System.Diagnostics; using System.Globalization; using System.IO; + using System.Linq; using System.Text; using System.Xml; using WixToolset.Data; + using WixToolset.Data.Burn; + using WixToolset.Data.Tuples; internal class CreateBootstrapperApplicationManifestCommand { -#if TODO - public WixBundleRow BundleRow { private get; set; } - - public IEnumerable ChainPackages { private get; set; } + public CreateBootstrapperApplicationManifestCommand(IntermediateSection section, WixBundleTuple bundleTuple, IEnumerable chainPackages, int lastUXPayloadIndex, Dictionary payloadTuples, string intermediateFolder) + { + this.Section = section; + this.BundleTuple = bundleTuple; + this.ChainPackages = chainPackages; + this.LastUXPayloadIndex = lastUXPayloadIndex; + this.Payloads = payloadTuples; + this.IntermediateFolder = intermediateFolder; + } - public int LastUXPayloadIndex { private get; set; } + private IntermediateSection Section { get; } - public IEnumerable MsiFeatures { private get; set; } + private WixBundleTuple BundleTuple { get; } - public Output Output { private get; set; } + private IEnumerable ChainPackages { get; } - public RowDictionary Payloads { private get; set; } + private int LastUXPayloadIndex { get; } - public TableDefinitionCollection TableDefinitions { private get; set; } + private Dictionary Payloads { get; } - public string TempFilesLocation { private get; set; } + private string IntermediateFolder { get; } - public WixBundlePayloadRow BootstrapperApplicationManifestPayloadRow { get; private set; } + public WixBundlePayloadTuple BootstrapperApplicationManifestPayloadRow { get; private set; } public void Execute() { - this.GenerateBAManifestBundleTables(); + var baManifestPath = this.CreateBootstrapperApplicationManifest(); - this.GenerateBAManifestMsiFeatureTables(); + this.BootstrapperApplicationManifestPayloadRow = this.CreateBootstrapperApplicationManifestPayloadRow(baManifestPath); + } - this.GenerateBAManifestPackageTables(); + private string CreateBootstrapperApplicationManifest() + { + var path = Path.Combine(this.IntermediateFolder, "wix-badata.xml"); - this.GenerateBAManifestPayloadTables(); + Directory.CreateDirectory(Path.GetDirectoryName(path)); - string baManifestPath = Path.Combine(this.TempFilesLocation, "wix-badata.xml"); + using (var writer = new XmlTextWriter(path, Encoding.Unicode)) + { + writer.Formatting = Formatting.Indented; + writer.WriteStartDocument(); + writer.WriteStartElement("BootstrapperApplicationData", "http://wixtoolset.org/schemas/v4/BootstrapperApplicationData"); - this.CreateBootstrapperApplicationManifest(baManifestPath); + this.WriteBundleInfo(writer); - this.BootstrapperApplicationManifestPayloadRow = this.CreateBootstrapperApplicationManifestPayloadRow(baManifestPath); + this.WritePackageInfo(writer); + + this.WriteFeatureInfo(writer); + + this.WritePayloadInfo(writer); + + this.WriteCustomBootstrapperApplicationData(writer); + + writer.WriteEndElement(); + writer.WriteEndDocument(); + } + + return path; } - private void GenerateBAManifestBundleTables() + private void WriteBundleInfo(XmlTextWriter writer) { - Table wixBundlePropertiesTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleProperties"]); - - Row row = wixBundlePropertiesTable.CreateRow(this.BundleRow.SourceLineNumbers); - row[0] = this.BundleRow.Name; - row[1] = this.BundleRow.LogPathVariable; - row[2] = (YesNoDefaultType.Yes == this.BundleRow.Compressed) ? "yes" : "no"; - row[3] = this.BundleRow.BundleId.ToString("B"); - row[4] = this.BundleRow.UpgradeCode; - row[5] = this.BundleRow.PerMachine ? "yes" : "no"; + writer.WriteStartElement("WixBundleProperties"); + + writer.WriteAttributeString("DisplayName", this.BundleTuple.Name); + writer.WriteAttributeString("LogPathVariable", this.BundleTuple.LogPathVariable); + writer.WriteAttributeString("Compressed", this.BundleTuple.Compressed == true ? "yes" : "no"); + writer.WriteAttributeString("BundleId", this.BundleTuple.BundleId.ToUpperInvariant()); + writer.WriteAttributeString("UpgradeCode", this.BundleTuple.UpgradeCode); + writer.WriteAttributeString("PerMachine", this.BundleTuple.PerMachine ? "yes" : "no"); + + writer.WriteEndElement(); } - private void GenerateBAManifestPackageTables() + private void WritePackageInfo(XmlTextWriter writer) { - Table wixPackagePropertiesTable = this.Output.EnsureTable(this.TableDefinitions["WixPackageProperties"]); - - foreach (PackageFacade package in this.ChainPackages) + foreach (var package in this.ChainPackages) { - WixBundlePayloadRow packagePayload = this.Payloads[package.Package.PackagePayload]; - - Row row = wixPackagePropertiesTable.CreateRow(package.Package.SourceLineNumbers); - row[0] = package.Package.WixChainItemId; - row[1] = (YesNoType.Yes == package.Package.Vital) ? "yes" : "no"; - row[2] = package.Package.DisplayName; - row[3] = package.Package.Description; - row[4] = package.Package.Size.ToString(CultureInfo.InvariantCulture); // TODO: DownloadSize (compressed) (what does this mean when it's embedded?) - row[5] = package.Package.Size.ToString(CultureInfo.InvariantCulture); // Package.Size (uncompressed) - row[6] = package.Package.InstallSize.Value.ToString(CultureInfo.InvariantCulture); // InstallSize (required disk space) - row[7] = package.Package.Type.ToString(); - row[8] = package.Package.Permanent ? "yes" : "no"; - row[9] = package.Package.LogPathVariable; - row[10] = package.Package.RollbackLogPathVariable; - row[11] = (PackagingType.Embedded == packagePayload.Packaging) ? "yes" : "no"; - - if (WixBundlePackageType.Msi == package.Package.Type) + var packagePayload = this.Payloads[package.PackageTuple.PayloadRef]; + + var size = package.PackageTuple.Size.ToString(CultureInfo.InvariantCulture); + + writer.WriteStartElement("WixBundleProperties"); + + writer.WriteAttributeString("Package", package.PackageId); + writer.WriteAttributeString("Vital", package.PackageTuple.Vital == true ? "yes" : "no"); + writer.WriteAttributeString("DisplayName", package.PackageTuple.DisplayName); + writer.WriteAttributeString("Description", package.PackageTuple.Description); + writer.WriteAttributeString("DownloadSize", size); + writer.WriteAttributeString("PackageSize", size); + writer.WriteAttributeString("InstalledSize", package.PackageTuple.InstallSize?.ToString(CultureInfo.InvariantCulture) ?? size); + writer.WriteAttributeString("PackageType", package.PackageTuple.Type.ToString()); + writer.WriteAttributeString("Permanent", package.PackageTuple.Permanent ? "yes" : "no"); + writer.WriteAttributeString("LogPathVariable", package.PackageTuple.LogPathVariable); + writer.WriteAttributeString("RollbackLogPathVariable", package.PackageTuple.RollbackLogPathVariable); + writer.WriteAttributeString("Compressed", packagePayload.Compressed == true ? "yes" : "no"); + + if (package.SpecificPackageTuple is WixBundleMsiPackageTuple msiPackage) { - row[12] = package.MsiPackage.DisplayInternalUI ? "yes" : "no"; + writer.WriteAttributeString("DisplayInternalUI", msiPackage.DisplayInternalUI ? "yes" : "no"); - if (!String.IsNullOrEmpty(package.MsiPackage.ProductCode)) + if (!String.IsNullOrEmpty(msiPackage.ProductCode)) { - row[13] = package.MsiPackage.ProductCode; + writer.WriteAttributeString("ProductCode", msiPackage.ProductCode); } - if (!String.IsNullOrEmpty(package.MsiPackage.UpgradeCode)) + if (!String.IsNullOrEmpty(msiPackage.UpgradeCode)) { - row[14] = package.MsiPackage.UpgradeCode; + writer.WriteAttributeString("UpgradeCode", msiPackage.UpgradeCode); } } - else if (WixBundlePackageType.Msp == package.Package.Type) + else if (package.SpecificPackageTuple is WixBundleMspPackageTuple mspPackage) { - row[12] = package.MspPackage.DisplayInternalUI ? "yes" : "no"; + writer.WriteAttributeString("DisplayInternalUI", mspPackage.DisplayInternalUI ? "yes" : "no"); - if (!String.IsNullOrEmpty(package.MspPackage.PatchCode)) + if (!String.IsNullOrEmpty(mspPackage.PatchCode)) { - row[13] = package.MspPackage.PatchCode; + writer.WriteAttributeString("ProductCode", mspPackage.PatchCode); } } - if (!String.IsNullOrEmpty(package.Package.Version)) + if (!String.IsNullOrEmpty(package.PackageTuple.Version)) { - row[15] = package.Package.Version; + writer.WriteAttributeString("Version", package.PackageTuple.Version); } - if (!String.IsNullOrEmpty(package.Package.InstallCondition)) + if (!String.IsNullOrEmpty(package.PackageTuple.InstallCondition)) { - row[16] = package.Package.InstallCondition; + writer.WriteAttributeString("InstallCondition", package.PackageTuple.InstallCondition); } - switch (package.Package.Cache) + switch (package.PackageTuple.Cache) { case YesNoAlwaysType.No: - row[17] = "no"; + writer.WriteAttributeString("Cache", "no"); break; case YesNoAlwaysType.Yes: - row[17] = "yes"; + writer.WriteAttributeString("Cache", "yes"); break; case YesNoAlwaysType.Always: - row[17] = "always"; + writer.WriteAttributeString("Cache", "always"); break; } + + writer.WriteEndElement(); } } - private void GenerateBAManifestMsiFeatureTables() + private void WriteFeatureInfo(XmlTextWriter writer) { - Table wixPackageFeatureInfoTable = this.Output.EnsureTable(this.TableDefinitions["WixPackageFeatureInfo"]); + var featureTuples = this.Section.Tuples.OfType(); - foreach (WixBundleMsiFeatureRow feature in this.MsiFeatures) + foreach (var featureTuple in featureTuples) { - Row row = wixPackageFeatureInfoTable.CreateRow(feature.SourceLineNumbers); - row[0] = feature.ChainPackageId; - row[1] = feature.Name; - row[2] = Convert.ToString(feature.Size, CultureInfo.InvariantCulture); - row[3] = feature.Parent; - row[4] = feature.Title; - row[5] = feature.Description; - row[6] = Convert.ToString(feature.Display, CultureInfo.InvariantCulture); - row[7] = Convert.ToString(feature.Level, CultureInfo.InvariantCulture); - row[8] = feature.Directory; - row[9] = Convert.ToString(feature.Attributes, CultureInfo.InvariantCulture); - } + writer.WriteStartElement("WixPackageFeatureInfo"); + + writer.WriteAttributeString("Package", featureTuple.PackageRef); + writer.WriteAttributeString("Feature", featureTuple.Name); + writer.WriteAttributeString("Size", featureTuple.Size.ToString(CultureInfo.InvariantCulture)); + writer.WriteAttributeString("Parent", featureTuple.Parent); + writer.WriteAttributeString("Title", featureTuple.Title); + writer.WriteAttributeString("Description", featureTuple.Description); + writer.WriteAttributeString("Display", featureTuple.Display.ToString(CultureInfo.InvariantCulture)); + writer.WriteAttributeString("Level", featureTuple.Level.ToString(CultureInfo.InvariantCulture)); + writer.WriteAttributeString("Directory", featureTuple.Directory); + writer.WriteAttributeString("Attributes", featureTuple.Attributes.ToString(CultureInfo.InvariantCulture)); + writer.WriteEndElement(); + } } - private void GenerateBAManifestPayloadTables() + private void WritePayloadInfo(XmlTextWriter writer) { - Table wixPayloadPropertiesTable = this.Output.EnsureTable(this.TableDefinitions["WixPayloadProperties"]); + var payloadTuples = this.Section.Tuples.OfType(); - foreach (WixBundlePayloadRow payload in this.Payloads.Values) + foreach (var payloadTuple in payloadTuples) { - WixPayloadPropertiesRow row = (WixPayloadPropertiesRow)wixPayloadPropertiesTable.CreateRow(payload.SourceLineNumbers); - row.Id = payload.Id; - row.Package = payload.Package; - row.Container = payload.Container; - row.Name = payload.Name; - row.Size = payload.FileSize.ToString(); - row.DownloadUrl = payload.DownloadUrl; - row.LayoutOnly = payload.LayoutOnly ? "yes" : "no"; + writer.WriteStartElement("WixPackageFeatureInfo"); + + writer.WriteAttributeString("Id", payloadTuple.Id.Id); + writer.WriteAttributeString("Package", payloadTuple.PackageRef); + writer.WriteAttributeString("Container", payloadTuple.ContainerRef); + writer.WriteAttributeString("Name", payloadTuple.Name); + writer.WriteAttributeString("Size", payloadTuple.FileSize.ToString(CultureInfo.InvariantCulture)); + writer.WriteAttributeString("DownloadUrl", payloadTuple.DownloadUrl); + writer.WriteAttributeString("LayoutOnly", payloadTuple.LayoutOnly ? "yes" : "no"); + + writer.WriteEndElement(); } } - private void CreateBootstrapperApplicationManifest(string path) + private void WriteCustomBootstrapperApplicationData(XmlTextWriter writer) { - using (XmlTextWriter writer = new XmlTextWriter(path, Encoding.Unicode)) + var dataTuplesGroupedByDefinitionName = this.Section.Tuples + .Where(t => t.Definition.HasTag(BurnConstants.BootstrapperApplicationDataTupleDefinitionTag)) + .GroupBy(t => t.Definition); + + foreach (var group in dataTuplesGroupedByDefinitionName) { - writer.Formatting = Formatting.Indented; - writer.WriteStartDocument(); - writer.WriteStartElement("BootstrapperApplicationData", "http://wixtoolset.org/schemas/v4/2010/BootstrapperApplicationData"); + var definition = group.Key; - foreach (Table table in this.Output.Tables) - { - if (table.Definition.BootstrapperApplicationData) - { - // We simply assert that the table (and field) name is valid, because - // this is up to the extension developer to get right. An author will - // only affect the attribute value, and that will get properly escaped. + // We simply assert that the table (and field) name is valid, because + // this is up to the extension developer to get right. An author will + // only affect the attribute value, and that will get properly escaped. #if DEBUG - Debug.Assert(Common.IsIdentifier(table.Name)); - foreach (ColumnDefinition column in table.Definition.Columns) - { - Debug.Assert(Common.IsIdentifier(column.Name)); - } + Debug.Assert(Common.IsIdentifier(definition.Name)); + foreach (var fieldDef in definition.FieldDefinitions) + { + Debug.Assert(Common.IsIdentifier(fieldDef.Name)); + } #endif // DEBUG - foreach (Row row in table.Rows) - { - writer.WriteStartElement(table.Name); - - foreach (Field field in row.Fields) - { - if (null != field.Data) - { - writer.WriteAttributeString(field.Column.Name, field.Data.ToString()); - } - } + foreach (var row in group) + { + writer.WriteStartElement(definition.Name); - writer.WriteEndElement(); + foreach (var field in row.Fields) + { + if (!field.IsNull()) + { + writer.WriteAttributeString(field.Definition.Name, field.AsString()); } } - } - writer.WriteEndElement(); - writer.WriteEndDocument(); + writer.WriteEndElement(); + } } } - private WixBundlePayloadRow CreateBootstrapperApplicationManifestPayloadRow(string baManifestPath) + private WixBundlePayloadTuple CreateBootstrapperApplicationManifestPayloadRow(string baManifestPath) { - Table payloadTable = this.Output.EnsureTable(this.TableDefinitions["WixBundlePayload"]); - WixBundlePayloadRow row = (WixBundlePayloadRow)payloadTable.CreateRow(this.BundleRow.SourceLineNumbers); - row.Id = Common.GenerateIdentifier("ux", "BootstrapperApplicationData.xml"); - row.Name = "BootstrapperApplicationData.xml"; - row.SourceFile = baManifestPath; - row.Compressed = YesNoDefaultType.Yes; - row.UnresolvedSourceFile = baManifestPath; - row.Container = Compiler.BurnUXContainerId; - row.EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnUXContainerEmbeddedIdFormat, this.LastUXPayloadIndex); - row.Packaging = PackagingType.Embedded; + var generatedId = Common.GenerateIdentifier("ux", "BootstrapperApplicationData.xml"); + + var tuple = new WixBundlePayloadTuple(this.BundleTuple.SourceLineNumbers, new Identifier(AccessModifier.Private, generatedId)) + { + Name = "BootstrapperApplicationData.xml", + SourceFile = new IntermediateFieldPathValue { Path = baManifestPath }, + Compressed = true, + UnresolvedSourceFile = baManifestPath, + ContainerRef = BurnConstants.BurnUXContainerName, + EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnUXContainerEmbeddedIdFormat, this.LastUXPayloadIndex), + Packaging = PackagingType.Embedded, + }; + + var fileInfo = new FileInfo(baManifestPath); - FileInfo fileInfo = new FileInfo(row.SourceFile); + tuple.FileSize = (int)fileInfo.Length; - row.FileSize = (int)fileInfo.Length; + tuple.Hash = BundleHashAlgorithm.Hash(fileInfo); - row.Hash = Common.GetFileHash(fileInfo.FullName); + this.Section.Tuples.Add(tuple); - return row; + return tuple; } -#endif } } diff --git a/src/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs b/src/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs new file mode 100644 index 00000000..bf0473d2 --- /dev/null +++ b/src/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs @@ -0,0 +1,171 @@ +// 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. + +namespace WixToolset.Core.Burn.Bundles +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Reflection; + using WixToolset.Data; + using WixToolset.Data.Burn; + using WixToolset.Data.Tuples; + using WixToolset.Extensibility.Data; + using WixToolset.Extensibility.Services; + + internal class CreateBundleExeCommand + { + public CreateBundleExeCommand(IMessaging messaging, IBackendHelper backendHelper, string intermediateFolder, string outputPath, WixBundleTuple bundleTuple, WixBundleContainerTuple uxContainer, IEnumerable containers, string burnStubPath) + { + this.Messaging = messaging; + this.BackendHelper = backendHelper; + this.IntermediateFolder = intermediateFolder; + this.OutputPath = outputPath; + this.BundleTuple = bundleTuple; + this.UXContainer = uxContainer; + this.Containers = containers; + this.BurnStubPath = burnStubPath; + } + + public IFileTransfer Transfer { get; private set; } + + private IMessaging Messaging { get; } + + private IBackendHelper BackendHelper { get; } + + private string IntermediateFolder { get; } + + private string OutputPath { get; } + + private WixBundleTuple BundleTuple { get; } + + private WixBundleContainerTuple UXContainer { get; } + + private IEnumerable Containers { get; } + + private string BurnStubPath { get; } + + public void Execute() + { + var bundleFilename = Path.GetFileName(this.OutputPath); + + // Copy the burn.exe to a writable location then mark it to be moved to its final build location. Note + // that today, the x64 Burn uses the x86 stub. + + var stubFile = this.BurnStubPath; + + if (String.IsNullOrEmpty(stubFile)) + { + var stubPlatform = (Platform.X64 == this.BundleTuple.Platform) ? "x86" : this.BundleTuple.Platform.ToString(); + + stubFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), stubPlatform, "burn.exe"); + } + + var bundleTempPath = Path.Combine(this.IntermediateFolder, bundleFilename); + + this.Messaging.Write(VerboseMessages.GeneratingBundle(bundleTempPath, stubFile)); + + if ("setup.exe".Equals(bundleFilename, StringComparison.OrdinalIgnoreCase)) + { + this.Messaging.Write(ErrorMessages.InsecureBundleFilename(bundleFilename)); + } + + this.Transfer = this.BackendHelper.CreateFileTransfer(bundleTempPath, this.OutputPath, true, this.BundleTuple.SourceLineNumbers); + + File.Copy(stubFile, bundleTempPath, true); + File.SetAttributes(bundleTempPath, FileAttributes.Normal); + + this.UpdateBurnResources(bundleTempPath, this.OutputPath, this.BundleTuple); + + // Update the .wixburn section to point to at the UX and attached container(s) then attach the containers + // if they should be attached. + using (var writer = BurnWriter.Open(this.Messaging, bundleTempPath)) + { + var burnStubFile = new FileInfo(bundleTempPath); + writer.InitializeBundleSectionData(burnStubFile.Length, this.BundleTuple.BundleId); + + // Always attach the UX container first + writer.AppendContainer(this.UXContainer.WorkingPath, BurnWriter.Container.UX); + + // Now append all other attached containers + foreach (var container in this.Containers) + { + if (ContainerType.Attached == container.Type) + { + // The container was only created if it had payloads. + if (!String.IsNullOrEmpty(container.WorkingPath) && BurnConstants.BurnUXContainerName != container.Id.Id) + { + writer.AppendContainer(container.WorkingPath, BurnWriter.Container.Attached); + } + } + } + } + } + + private void UpdateBurnResources(string bundleTempPath, string outputPath, WixBundleTuple bundleInfo) + { + var resources = new Dtf.Resources.ResourceCollection(); + var version = new Dtf.Resources.VersionResource("#1", 1033); + + version.Load(bundleTempPath); + resources.Add(version); + + // Ensure the bundle info provides a full four part version. + var fourPartVersion = new Version(bundleInfo.Version); + var major = (fourPartVersion.Major < 0) ? 0 : fourPartVersion.Major; + var minor = (fourPartVersion.Minor < 0) ? 0 : fourPartVersion.Minor; + var build = (fourPartVersion.Build < 0) ? 0 : fourPartVersion.Build; + var revision = (fourPartVersion.Revision < 0) ? 0 : fourPartVersion.Revision; + + if (UInt16.MaxValue < major || UInt16.MaxValue < minor || UInt16.MaxValue < build || UInt16.MaxValue < revision) + { + throw new WixException(ErrorMessages.InvalidModuleOrBundleVersion(bundleInfo.SourceLineNumbers, "Bundle", bundleInfo.Version)); + } + + fourPartVersion = new Version(major, minor, build, revision); + version.FileVersion = fourPartVersion; + version.ProductVersion = fourPartVersion; + + var strings = version[1033]; + strings["LegalCopyright"] = bundleInfo.Copyright; + strings["OriginalFilename"] = Path.GetFileName(outputPath); + strings["FileVersion"] = bundleInfo.Version; // string versions do not have to be four parts. + strings["ProductVersion"] = bundleInfo.Version; // string versions do not have to be four parts. + + if (!String.IsNullOrEmpty(bundleInfo.Name)) + { + strings["ProductName"] = bundleInfo.Name; + strings["FileDescription"] = bundleInfo.Name; + } + + if (!String.IsNullOrEmpty(bundleInfo.Manufacturer)) + { + strings["CompanyName"] = bundleInfo.Manufacturer; + } + else + { + strings["CompanyName"] = String.Empty; + } + + if (!String.IsNullOrEmpty(bundleInfo.IconSourceFile)) + { + var iconGroup = new Dtf.Resources.GroupIconResource("#1", 1033); + iconGroup.ReadFromFile(bundleInfo.IconSourceFile); + resources.Add(iconGroup); + + foreach (var icon in iconGroup.Icons) + { + resources.Add(icon); + } + } + + if (!String.IsNullOrEmpty(bundleInfo.SplashScreenSourceFile)) + { + var bitmap = new Dtf.Resources.BitmapResource("#1", 1033); + bitmap.ReadFromFile(bundleInfo.SplashScreenSourceFile); + resources.Add(bitmap); + } + + resources.Save(bundleTempPath); + } + } +} diff --git a/src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs b/src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs index 0ec8e46a..b7ea4116 100644 --- a/src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs +++ b/src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs @@ -6,76 +6,103 @@ namespace WixToolset.Core.Burn.Bundles using System.Collections.Generic; using System.Diagnostics; using System.Globalization; + using System.IO; using System.Linq; using System.Text; using System.Xml; using WixToolset.Data; + using WixToolset.Data.Burn; + using WixToolset.Data.Tuples; using WixToolset.Extensibility; + using WixToolset.Extensibility.Services; internal class CreateBurnManifestCommand { -#if TODO - public IEnumerable BackendExtensions { private get; set; } + public CreateBurnManifestCommand(IMessaging messaging, IEnumerable backendExtensions, string executableName, IntermediateSection section, WixBundleTuple bundleTuple, IEnumerable containers, WixChainTuple chainTuple, IEnumerable orderedPackages, IEnumerable boundaries, IEnumerable uxPayloads, Dictionary allPayloadsById, IEnumerable orderedSearches, IEnumerable catalogs, string intermediateFolder) + { + this.Messaging = messaging; + this.BackendExtensions = backendExtensions; + this.ExecutableName = executableName; + this.Section = section; + this.BundleTuple = bundleTuple; + this.Chain = chainTuple; + this.Containers = containers; + this.OrderedPackages = orderedPackages; + this.RollbackBoundaries = boundaries; + this.UXContainerPayloads = uxPayloads; + this.Payloads = allPayloadsById; + this.OrderedSearches = orderedSearches; + this.Catalogs = catalogs; + this.IntermediateFolder = intermediateFolder; + } + + public string OutputPath { get; private set; } + + private IMessaging Messaging { get; } - public Output Output { private get; set; } + private IEnumerable BackendExtensions { get; } - public string ExecutableName { private get; set; } + private string ExecutableName { get; } - public WixBundleRow BundleInfo { private get; set; } + private IntermediateSection Section { get; } - public WixChainRow Chain { private get; set; } + private WixBundleTuple BundleTuple { get; } - public string OutputPath { private get; set; } + private WixChainTuple Chain { get; } - public IEnumerable RollbackBoundaries { private get; set; } + private IEnumerable RollbackBoundaries { get; } - public IEnumerable OrderedPackages { private get; set; } + private IEnumerable OrderedPackages { get; } - public IEnumerable OrderedSearches { private get; set; } + private IEnumerable OrderedSearches { get; } - public Dictionary Payloads { private get; set; } + private Dictionary Payloads { get; } - public Dictionary Containers { private get; set; } + private IEnumerable Containers { get; } - public IEnumerable UXContainerPayloads { private get; set; } + private IEnumerable UXContainerPayloads { get; } - public IEnumerable Catalogs { private get; set; } + private IEnumerable Catalogs { get; } + + private string IntermediateFolder { get; } public void Execute() { - using (XmlTextWriter writer = new XmlTextWriter(this.OutputPath, Encoding.UTF8)) + this.OutputPath = Path.Combine(this.IntermediateFolder, "bundle-manifest.xml"); + + using (var writer = new XmlTextWriter(this.OutputPath, Encoding.UTF8)) { writer.WriteStartDocument(); writer.WriteStartElement("BurnManifest", BurnCommon.BurnNamespace); // Write the condition, if there is one - if (null != this.BundleInfo.Condition) + if (null != this.BundleTuple.Condition) { - writer.WriteElementString("Condition", this.BundleInfo.Condition); + writer.WriteElementString("Condition", this.BundleTuple.Condition); } // Write the log element if default logging wasn't disabled. - if (!String.IsNullOrEmpty(this.BundleInfo.LogPrefix)) + if (!String.IsNullOrEmpty(this.BundleTuple.LogPrefix)) { writer.WriteStartElement("Log"); - if (!String.IsNullOrEmpty(this.BundleInfo.LogPathVariable)) + if (!String.IsNullOrEmpty(this.BundleTuple.LogPathVariable)) { - writer.WriteAttributeString("PathVariable", this.BundleInfo.LogPathVariable); + writer.WriteAttributeString("PathVariable", this.BundleTuple.LogPathVariable); } - writer.WriteAttributeString("Prefix", this.BundleInfo.LogPrefix); - writer.WriteAttributeString("Extension", this.BundleInfo.LogExtension); + writer.WriteAttributeString("Prefix", this.BundleTuple.LogPrefix); + writer.WriteAttributeString("Extension", this.BundleTuple.LogExtension); writer.WriteEndElement(); } // Get update if specified. - WixBundleUpdateRow updateRow = this.Output.Tables["WixBundleUpdate"].RowsAs().FirstOrDefault(); + var updateTuple = this.Section.Tuples.OfType().FirstOrDefault(); - if (null != updateRow) + if (null != updateTuple) { writer.WriteStartElement("Update"); - writer.WriteAttributeString("Location", updateRow.Location); + writer.WriteAttributeString("Location", updateTuple.Location); writer.WriteEndElement(); // } @@ -83,23 +110,27 @@ namespace WixToolset.Core.Burn.Bundles // For the related bundles with duplicated identifiers the second instance is ignored (i.e. the Duplicates // enumeration in the index row list is not used). - RowIndexedList relatedBundles = new RowIndexedList(this.Output.Tables["WixRelatedBundle"]); + var relatedBundles = this.Section.Tuples.OfType(); + var distinctRelatedBundles = new HashSet(); - foreach (WixRelatedBundleRow relatedBundle in relatedBundles) + foreach (var relatedBundle in relatedBundles) { - writer.WriteStartElement("RelatedBundle"); - writer.WriteAttributeString("Id", relatedBundle.Id); - writer.WriteAttributeString("Action", Convert.ToString(relatedBundle.Action, CultureInfo.InvariantCulture)); - writer.WriteEndElement(); + if (distinctRelatedBundles.Add(relatedBundle.BundleId)) + { + writer.WriteStartElement("RelatedBundle"); + writer.WriteAttributeString("Id", relatedBundle.BundleId); + writer.WriteAttributeString("Action", relatedBundle.Action.ToString()); + writer.WriteEndElement(); + } } // Write the variables - IEnumerable variables = this.Output.Tables["WixBundleVariable"].RowsAs(); + var variables = this.Section.Tuples.OfType(); - foreach (WixBundleVariableRow variable in variables) + foreach (var variable in variables) { writer.WriteStartElement("Variable"); - writer.WriteAttributeString("Id", variable.Id); + writer.WriteAttributeString("Id", variable.Id.Id); if (null != variable.Type) { writer.WriteAttributeString("Value", variable.Value); @@ -111,20 +142,20 @@ namespace WixToolset.Core.Burn.Bundles } // Write the searches - foreach (WixSearchInfo searchinfo in this.OrderedSearches) + foreach (var searchinfo in this.OrderedSearches) { searchinfo.WriteXml(writer); } // write the UX element writer.WriteStartElement("UX"); - if (!String.IsNullOrEmpty(this.BundleInfo.SplashScreenBitmapPath)) + if (!String.IsNullOrEmpty(this.BundleTuple.SplashScreenSourceFile)) { writer.WriteAttributeString("SplashScreen", "yes"); } // write the UX allPayloads... - foreach (WixBundlePayloadRow payload in this.UXContainerPayloads) + foreach (var payload in this.UXContainerPayloads) { writer.WriteStartElement("Payload"); this.WriteBurnManifestPayloadAttributes(writer, payload, true, this.Payloads); @@ -136,18 +167,18 @@ namespace WixToolset.Core.Burn.Bundles // write the catalog elements if (this.Catalogs.Any()) { - foreach (WixBundleCatalogRow catalog in this.Catalogs) + foreach (var catalog in this.Catalogs) { writer.WriteStartElement("Catalog"); - writer.WriteAttributeString("Id", catalog.Id); - writer.WriteAttributeString("Payload", catalog.Payload); + writer.WriteAttributeString("Id", catalog.Id.Id); + writer.WriteAttributeString("Payload", catalog.PayloadRef); writer.WriteEndElement(); } } - foreach (WixBundleContainerRow container in this.Containers.Values) + foreach (var container in this.Containers) { - if (!String.IsNullOrEmpty(container.WorkingPath) && Compiler.BurnUXContainerId != container.Id) + if (!String.IsNullOrEmpty(container.WorkingPath) && BurnConstants.BurnUXContainerName != container.Id.Id) { writer.WriteStartElement("Container"); this.WriteBurnManifestContainerAttributes(writer, this.ExecutableName, container); @@ -155,9 +186,9 @@ namespace WixToolset.Core.Burn.Bundles } } - foreach (WixBundlePayloadRow payload in this.Payloads.Values) + foreach (var payload in this.Payloads.Values) { - if (PackagingType.Embedded == payload.Packaging && Compiler.BurnUXContainerId != payload.Container) + if (PackagingType.Embedded == payload.Packaging && BurnConstants.BurnUXContainerName != payload.ContainerRef) { writer.WriteStartElement("Payload"); this.WriteBurnManifestPayloadAttributes(writer, payload, true, this.Payloads); @@ -171,77 +202,78 @@ namespace WixToolset.Core.Burn.Bundles } } - foreach (WixBundleRollbackBoundaryRow rollbackBoundary in this.RollbackBoundaries) + foreach (var rollbackBoundary in this.RollbackBoundaries) { writer.WriteStartElement("RollbackBoundary"); - writer.WriteAttributeString("Id", rollbackBoundary.ChainPackageId); - writer.WriteAttributeString("Vital", YesNoType.Yes == rollbackBoundary.Vital ? "yes" : "no"); - writer.WriteAttributeString("Transaction", YesNoType.Yes == rollbackBoundary.Transaction ? "yes" : "no"); + writer.WriteAttributeString("Id", rollbackBoundary.Id.Id); + writer.WriteAttributeString("Vital", rollbackBoundary.Vital == false ? "no" : "yes"); + writer.WriteAttributeString("Transaction", rollbackBoundary.Transaction == true ? "yes" : "no"); writer.WriteEndElement(); } // Write the registration information... writer.WriteStartElement("Registration"); - writer.WriteAttributeString("Id", this.BundleInfo.BundleId.ToString("B")); + writer.WriteAttributeString("Id", this.BundleTuple.BundleId); writer.WriteAttributeString("ExecutableName", this.ExecutableName); - writer.WriteAttributeString("PerMachine", this.BundleInfo.PerMachine ? "yes" : "no"); - writer.WriteAttributeString("Tag", this.BundleInfo.Tag); - writer.WriteAttributeString("Version", this.BundleInfo.Version); - writer.WriteAttributeString("ProviderKey", this.BundleInfo.ProviderKey); + writer.WriteAttributeString("PerMachine", this.BundleTuple.PerMachine ? "yes" : "no"); + writer.WriteAttributeString("Tag", this.BundleTuple.Tag); + writer.WriteAttributeString("Version", this.BundleTuple.Version); + writer.WriteAttributeString("ProviderKey", this.BundleTuple.ProviderKey); writer.WriteStartElement("Arp"); - writer.WriteAttributeString("Register", (0 < this.BundleInfo.DisableModify && this.BundleInfo.DisableRemove) ? "no" : "yes"); // do not register if disabled modify and remove. - writer.WriteAttributeString("DisplayName", this.BundleInfo.Name); - writer.WriteAttributeString("DisplayVersion", this.BundleInfo.Version); + writer.WriteAttributeString("Register", (this.BundleTuple.DisableModify || this.BundleTuple.SingleChangeUninstallButton) && this.BundleTuple.DisableRemove ? "no" : "yes"); // do not register if disabled modify and remove. + writer.WriteAttributeString("DisplayName", this.BundleTuple.Name); + writer.WriteAttributeString("DisplayVersion", this.BundleTuple.Version); - if (!String.IsNullOrEmpty(this.BundleInfo.Publisher)) + if (!String.IsNullOrEmpty(this.BundleTuple.Manufacturer)) { - writer.WriteAttributeString("Publisher", this.BundleInfo.Publisher); + writer.WriteAttributeString("Publisher", this.BundleTuple.Manufacturer); } - if (!String.IsNullOrEmpty(this.BundleInfo.HelpLink)) + if (!String.IsNullOrEmpty(this.BundleTuple.HelpUrl)) { - writer.WriteAttributeString("HelpLink", this.BundleInfo.HelpLink); + writer.WriteAttributeString("HelpLink", this.BundleTuple.HelpUrl); } - if (!String.IsNullOrEmpty(this.BundleInfo.HelpTelephone)) + if (!String.IsNullOrEmpty(this.BundleTuple.HelpTelephone)) { - writer.WriteAttributeString("HelpTelephone", this.BundleInfo.HelpTelephone); + writer.WriteAttributeString("HelpTelephone", this.BundleTuple.HelpTelephone); } - if (!String.IsNullOrEmpty(this.BundleInfo.AboutUrl)) + if (!String.IsNullOrEmpty(this.BundleTuple.AboutUrl)) { - writer.WriteAttributeString("AboutUrl", this.BundleInfo.AboutUrl); + writer.WriteAttributeString("AboutUrl", this.BundleTuple.AboutUrl); } - if (!String.IsNullOrEmpty(this.BundleInfo.UpdateUrl)) + if (!String.IsNullOrEmpty(this.BundleTuple.UpdateUrl)) { - writer.WriteAttributeString("UpdateUrl", this.BundleInfo.UpdateUrl); + writer.WriteAttributeString("UpdateUrl", this.BundleTuple.UpdateUrl); } - if (!String.IsNullOrEmpty(this.BundleInfo.ParentName)) + if (!String.IsNullOrEmpty(this.BundleTuple.ParentName)) { - writer.WriteAttributeString("ParentDisplayName", this.BundleInfo.ParentName); + writer.WriteAttributeString("ParentDisplayName", this.BundleTuple.ParentName); } - if (1 == this.BundleInfo.DisableModify) + if (this.BundleTuple.DisableModify) { writer.WriteAttributeString("DisableModify", "yes"); } - else if (2 == this.BundleInfo.DisableModify) + + if (this.BundleTuple.DisableRemove) { - writer.WriteAttributeString("DisableModify", "button"); + writer.WriteAttributeString("DisableRemove", "yes"); } - if (this.BundleInfo.DisableRemove) + if (this.BundleTuple.SingleChangeUninstallButton) { - writer.WriteAttributeString("DisableRemove", "yes"); + writer.WriteAttributeString("DisableModify", "button"); } writer.WriteEndElement(); // // Get update registration if specified. - WixUpdateRegistrationRow updateRegistrationInfo = this.Output.Tables["WixUpdateRegistration"].RowsAs().FirstOrDefault(); + var updateRegistrationInfo = this.Section.Tuples.OfType().FirstOrDefault(); if (null != updateRegistrationInfo) { @@ -263,9 +295,9 @@ namespace WixToolset.Core.Burn.Bundles writer.WriteEndElement(); // } - IEnumerable bundleTags = this.Output.Tables["WixBundleTag"].RowsAs(); - - foreach (Row row in bundleTags) +#if TODO // Handle SWID Tags + var bundleTags = this.Output.Tables["WixBundleTag"].RowsAs(); + foreach (var row in bundleTags) { writer.WriteStartElement("SoftwareTag"); writer.WriteAttributeString("Filename", (string)row[0]); @@ -273,6 +305,7 @@ namespace WixToolset.Core.Burn.Bundles writer.WriteCData((string)row[4]); writer.WriteEndElement(); } +#endif writer.WriteEndElement(); // @@ -294,25 +327,28 @@ namespace WixToolset.Core.Burn.Bundles } // Index a few tables by package. - ILookup targetCodesByPatch = this.Output.Tables["WixBundlePatchTargetCode"].RowsAs().ToLookup(r => r.MspPackageId); - ILookup msiFeaturesByPackage = this.Output.Tables["WixBundleMsiFeature"].RowsAs().ToLookup(r => r.ChainPackageId); - ILookup msiPropertiesByPackage = this.Output.Tables["WixBundleMsiProperty"].RowsAs().ToLookup(r => r.ChainPackageId); - ILookup payloadsByPackage = this.Payloads.Values.ToLookup(p => p.Package); - ILookup relatedPackagesByPackage = this.Output.Tables["WixBundleRelatedPackage"].RowsAs().ToLookup(r => r.ChainPackageId); - ILookup slipstreamMspsByPackage = this.Output.Tables["WixBundleSlipstreamMsp"].RowsAs().ToLookup(r => r.ChainPackageId); - ILookup exitCodesByPackage = this.Output.Tables["WixBundlePackageExitCode"].RowsAs().ToLookup(r => r.ChainPackageId); - ILookup commandLinesByPackage = this.Output.Tables["WixBundlePackageCommandLine"].RowsAs().ToLookup(r => r.ChainPackageId); + var targetCodesByPatch = this.Section.Tuples.OfType().ToLookup(r => r.PackageRef); + var msiFeaturesByPackage = this.Section.Tuples.OfType().ToLookup(r => r.PackageRef); + var msiPropertiesByPackage = this.Section.Tuples.OfType().ToLookup(r => r.PackageRef); + var payloadsByPackage = this.Payloads.Values.ToLookup(p => p.PackageRef); + var relatedPackagesByPackage = this.Section.Tuples.OfType().ToLookup(r => r.PackageRef); + var slipstreamMspsByPackage = this.Section.Tuples.OfType().ToLookup(r => r.MspPackageRef); + var exitCodesByPackage = this.Section.Tuples.OfType().ToLookup(r => r.ChainPackageId); + var commandLinesByPackage = this.Section.Tuples.OfType().ToLookup(r => r.WixBundlePackageRef); + + var dependenciesByPackage = this.Section.Tuples.OfType().ToLookup(p => p.PackageRef); + // Build up the list of target codes from all the MSPs in the chain. - List targetCodes = new List(); + var targetCodes = new List(); - foreach (PackageFacade package in this.OrderedPackages) + foreach (var package in this.OrderedPackages) { - writer.WriteStartElement(String.Format(CultureInfo.InvariantCulture, "{0}Package", package.Package.Type)); + writer.WriteStartElement(String.Format(CultureInfo.InvariantCulture, "{0}Package", package.PackageTuple.Type)); - writer.WriteAttributeString("Id", package.Package.WixChainItemId); + writer.WriteAttributeString("Id", package.PackageId); - switch (package.Package.Cache) + switch (package.PackageTuple.Cache) { case YesNoAlwaysType.No: writer.WriteAttributeString("Cache", "no"); @@ -325,74 +361,74 @@ namespace WixToolset.Core.Burn.Bundles break; } - writer.WriteAttributeString("CacheId", package.Package.CacheId); - writer.WriteAttributeString("InstallSize", Convert.ToString(package.Package.InstallSize)); - writer.WriteAttributeString("Size", Convert.ToString(package.Package.Size)); - writer.WriteAttributeString("PerMachine", YesNoDefaultType.Yes == package.Package.PerMachine ? "yes" : "no"); - writer.WriteAttributeString("Permanent", package.Package.Permanent ? "yes" : "no"); - writer.WriteAttributeString("Vital", (YesNoType.Yes == package.Package.Vital) ? "yes" : "no"); + writer.WriteAttributeString("CacheId", package.PackageTuple.CacheId); + writer.WriteAttributeString("InstallSize", Convert.ToString(package.PackageTuple.InstallSize)); + writer.WriteAttributeString("Size", Convert.ToString(package.PackageTuple.Size)); + writer.WriteAttributeString("PerMachine", YesNoDefaultType.Yes == package.PackageTuple.PerMachine ? "yes" : "no"); + writer.WriteAttributeString("Permanent", package.PackageTuple.Permanent ? "yes" : "no"); + writer.WriteAttributeString("Vital", package.PackageTuple.Vital == false ? "no" : "yes"); - if (null != package.Package.RollbackBoundary) + if (null != package.PackageTuple.RollbackBoundaryRef) { - writer.WriteAttributeString("RollbackBoundaryForward", package.Package.RollbackBoundary); + writer.WriteAttributeString("RollbackBoundaryForward", package.PackageTuple.RollbackBoundaryRef); } - if (!String.IsNullOrEmpty(package.Package.RollbackBoundaryBackward)) + if (!String.IsNullOrEmpty(package.PackageTuple.RollbackBoundaryBackwardRef)) { - writer.WriteAttributeString("RollbackBoundaryBackward", package.Package.RollbackBoundaryBackward); + writer.WriteAttributeString("RollbackBoundaryBackward", package.PackageTuple.RollbackBoundaryBackwardRef); } - if (!String.IsNullOrEmpty(package.Package.LogPathVariable)) + if (!String.IsNullOrEmpty(package.PackageTuple.LogPathVariable)) { - writer.WriteAttributeString("LogPathVariable", package.Package.LogPathVariable); + writer.WriteAttributeString("LogPathVariable", package.PackageTuple.LogPathVariable); } - if (!String.IsNullOrEmpty(package.Package.RollbackLogPathVariable)) + if (!String.IsNullOrEmpty(package.PackageTuple.RollbackLogPathVariable)) { - writer.WriteAttributeString("RollbackLogPathVariable", package.Package.RollbackLogPathVariable); + writer.WriteAttributeString("RollbackLogPathVariable", package.PackageTuple.RollbackLogPathVariable); } - if (!String.IsNullOrEmpty(package.Package.InstallCondition)) + if (!String.IsNullOrEmpty(package.PackageTuple.InstallCondition)) { - writer.WriteAttributeString("InstallCondition", package.Package.InstallCondition); + writer.WriteAttributeString("InstallCondition", package.PackageTuple.InstallCondition); } - if (WixBundlePackageType.Exe == package.Package.Type) + if (package.SpecificPackageTuple is WixBundleExePackageTuple exePackage) // EXE { - writer.WriteAttributeString("DetectCondition", package.ExePackage.DetectCondition); - writer.WriteAttributeString("InstallArguments", package.ExePackage.InstallCommand); - writer.WriteAttributeString("UninstallArguments", package.ExePackage.UninstallCommand); - writer.WriteAttributeString("RepairArguments", package.ExePackage.RepairCommand); - writer.WriteAttributeString("Repairable", package.ExePackage.Repairable ? "yes" : "no"); - if (!String.IsNullOrEmpty(package.ExePackage.ExeProtocol)) + writer.WriteAttributeString("DetectCondition", exePackage.DetectCondition); + writer.WriteAttributeString("InstallArguments", exePackage.InstallCommand); + writer.WriteAttributeString("UninstallArguments", exePackage.UninstallCommand); + writer.WriteAttributeString("RepairArguments", exePackage.RepairCommand); + writer.WriteAttributeString("Repairable", exePackage.Repairable ? "yes" : "no"); + if (!String.IsNullOrEmpty(exePackage.ExeProtocol)) { - writer.WriteAttributeString("Protocol", package.ExePackage.ExeProtocol); + writer.WriteAttributeString("Protocol", exePackage.ExeProtocol); } } - else if (WixBundlePackageType.Msi == package.Package.Type) + else if (package.SpecificPackageTuple is WixBundleMsiPackageTuple msiPackage) // MSI { - writer.WriteAttributeString("ProductCode", package.MsiPackage.ProductCode); - writer.WriteAttributeString("Language", package.MsiPackage.ProductLanguage.ToString(CultureInfo.InvariantCulture)); - writer.WriteAttributeString("Version", package.MsiPackage.ProductVersion); - writer.WriteAttributeString("DisplayInternalUI", package.MsiPackage.DisplayInternalUI ? "yes" : "no"); - if (!String.IsNullOrEmpty(package.MsiPackage.UpgradeCode)) + writer.WriteAttributeString("ProductCode", msiPackage.ProductCode); + writer.WriteAttributeString("Language", msiPackage.ProductLanguage.ToString(CultureInfo.InvariantCulture)); + writer.WriteAttributeString("Version", msiPackage.ProductVersion); + writer.WriteAttributeString("DisplayInternalUI", msiPackage.DisplayInternalUI ? "yes" : "no"); + if (!String.IsNullOrEmpty(msiPackage.UpgradeCode)) { - writer.WriteAttributeString("UpgradeCode", package.MsiPackage.UpgradeCode); + writer.WriteAttributeString("UpgradeCode", msiPackage.UpgradeCode); } } - else if (WixBundlePackageType.Msp == package.Package.Type) + else if (package.SpecificPackageTuple is WixBundleMspPackageTuple mspPackage) // MSP { - writer.WriteAttributeString("PatchCode", package.MspPackage.PatchCode); - writer.WriteAttributeString("PatchXml", package.MspPackage.PatchXml); - writer.WriteAttributeString("DisplayInternalUI", package.MspPackage.DisplayInternalUI ? "yes" : "no"); + writer.WriteAttributeString("PatchCode", mspPackage.PatchCode); + writer.WriteAttributeString("PatchXml", mspPackage.PatchXml); + writer.WriteAttributeString("DisplayInternalUI", mspPackage.DisplayInternalUI ? "yes" : "no"); // If there is still a chance that all of our patches will target a narrow set of // product codes, add the patch list to the overall list. if (null != targetCodes) { - if (!package.MspPackage.TargetUnspecified) + if (!mspPackage.TargetUnspecified) { - IEnumerable patchTargetCodes = targetCodesByPatch[package.MspPackage.ChainPackageId]; + var patchTargetCodes = targetCodesByPatch[mspPackage.Id.Id]; targetCodes.AddRange(patchTargetCodes); } @@ -402,24 +438,24 @@ namespace WixToolset.Core.Burn.Bundles } } } - else if (WixBundlePackageType.Msu == package.Package.Type) + else if (package.SpecificPackageTuple is WixBundleMsuPackageTuple msuPackage) // MSU { - writer.WriteAttributeString("DetectCondition", package.MsuPackage.DetectCondition); - writer.WriteAttributeString("KB", package.MsuPackage.MsuKB); + writer.WriteAttributeString("DetectCondition", msuPackage.DetectCondition); + writer.WriteAttributeString("KB", msuPackage.MsuKB); } - IEnumerable packageMsiFeatures = msiFeaturesByPackage[package.Package.WixChainItemId]; + var packageMsiFeatures = msiFeaturesByPackage[package.PackageId]; - foreach (WixBundleMsiFeatureRow feature in packageMsiFeatures) + foreach (var feature in packageMsiFeatures) { writer.WriteStartElement("MsiFeature"); writer.WriteAttributeString("Id", feature.Name); writer.WriteEndElement(); } - IEnumerable packageMsiProperties = msiPropertiesByPackage[package.Package.WixChainItemId]; + var packageMsiProperties = msiPropertiesByPackage[package.PackageId]; - foreach (WixBundleMsiPropertyRow msiProperty in packageMsiProperties) + foreach (var msiProperty in packageMsiProperties) { writer.WriteStartElement("MsiProperty"); writer.WriteAttributeString("Id", msiProperty.Name); @@ -431,18 +467,18 @@ namespace WixToolset.Core.Burn.Bundles writer.WriteEndElement(); } - IEnumerable packageSlipstreamMsps = slipstreamMspsByPackage[package.Package.WixChainItemId]; + var packageSlipstreamMsps = slipstreamMspsByPackage[package.PackageId]; - foreach (WixBundleSlipstreamMspRow slipstreamMsp in packageSlipstreamMsps) + foreach (var slipstreamMsp in packageSlipstreamMsps) { writer.WriteStartElement("SlipstreamMsp"); - writer.WriteAttributeString("Id", slipstreamMsp.MspPackageId); + writer.WriteAttributeString("Id", slipstreamMsp.MspPackageRef); writer.WriteEndElement(); } - IEnumerable packageExitCodes = exitCodesByPackage[package.Package.WixChainItemId]; + var packageExitCodes = exitCodesByPackage[package.PackageId]; - foreach (WixBundlePackageExitCodeRow exitCode in packageExitCodes) + foreach (var exitCode in packageExitCodes) { writer.WriteStartElement("ExitCode"); @@ -459,9 +495,9 @@ namespace WixToolset.Core.Burn.Bundles writer.WriteEndElement(); } - IEnumerable packageCommandLines = commandLinesByPackage[package.Package.WixChainItemId]; + var packageCommandLines = commandLinesByPackage[package.PackageId]; - foreach (WixBundlePackageCommandLineRow commandLine in packageCommandLines) + foreach (var commandLine in packageCommandLines) { writer.WriteStartElement("CommandLine"); writer.WriteAttributeString("InstallArgument", commandLine.InstallArgument); @@ -472,18 +508,38 @@ namespace WixToolset.Core.Burn.Bundles } // Output the dependency information. - foreach (ProvidesDependency dependency in package.Provides) + var dependencies = dependenciesByPackage[package.PackageId]; + + foreach (var dependency in dependencies) { - // TODO: Add to wixpdb as an imported table, or link package wixpdbs to bundle wixpdbs. - dependency.WriteXml(writer); + writer.WriteStartElement("Provides"); + writer.WriteAttributeString("Key", dependency.Key); + + if (!String.IsNullOrEmpty(dependency.Version)) + { + writer.WriteAttributeString("Version", dependency.Version); + } + + if (!String.IsNullOrEmpty(dependency.DisplayName)) + { + writer.WriteAttributeString("DisplayName", dependency.DisplayName); + } + + if (dependency.Imported) + { + // The package dependency was explicitly authored into the manifest. + writer.WriteAttributeString("Imported", "yes"); + } + + writer.WriteEndElement(); } - IEnumerable packageRelatedPackages = relatedPackagesByPackage[package.Package.WixChainItemId]; + var packageRelatedPackages = relatedPackagesByPackage[package.PackageId]; - foreach (WixBundleRelatedPackageRow related in packageRelatedPackages) + foreach (var related in packageRelatedPackages) { writer.WriteStartElement("RelatedPackage"); - writer.WriteAttributeString("Id", related.Id); + writer.WriteAttributeString("Id", related.RelatedId); if (!String.IsNullOrEmpty(related.MinVersion)) { writer.WriteAttributeString("MinVersion", related.MinVersion); @@ -496,7 +552,7 @@ namespace WixToolset.Core.Burn.Bundles } writer.WriteAttributeString("OnlyDetect", related.OnlyDetect ? "yes" : "no"); - string[] relatedLanguages = related.Languages.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + var relatedLanguages = related.Languages.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (0 < relatedLanguages.Length) { @@ -513,17 +569,17 @@ namespace WixToolset.Core.Burn.Bundles // Write any contained Payloads with the PackagePayload being first writer.WriteStartElement("PayloadRef"); - writer.WriteAttributeString("Id", package.Package.PackagePayload); + writer.WriteAttributeString("Id", package.PackageTuple.PayloadRef); writer.WriteEndElement(); - IEnumerable packagePayloads = payloadsByPackage[package.Package.WixChainItemId]; + var packagePayloads = payloadsByPackage[package.PackageId]; - foreach (WixBundlePayloadRow payload in packagePayloads) + foreach (var payload in packagePayloads) { - if (payload.Id != package.Package.PackagePayload) + if (payload.Id.Id != package.PackageTuple.PayloadRef) { writer.WriteStartElement("PayloadRef"); - writer.WriteAttributeString("Id", payload.Id); + writer.WriteAttributeString("Id", payload.Id.Id); writer.WriteEndElement(); } } @@ -534,7 +590,7 @@ namespace WixToolset.Core.Burn.Bundles if (null != targetCodes) { - foreach (WixBundlePatchTargetCodeRow targetCode in targetCodes) + foreach (var targetCode in targetCodes) { writer.WriteStartElement("PatchTargetCode"); writer.WriteAttributeString("TargetCode", targetCode.TargetCode); @@ -544,12 +600,12 @@ namespace WixToolset.Core.Burn.Bundles } // Write the ApprovedExeForElevation elements. - IEnumerable approvedExesForElevation = this.Output.Tables["WixApprovedExeForElevation"].RowsAs(); + var approvedExesForElevation = this.Section.Tuples.OfType(); - foreach (WixApprovedExeForElevationRow approvedExeForElevation in approvedExesForElevation) + foreach (var approvedExeForElevation in approvedExesForElevation) { writer.WriteStartElement("ApprovedExeForElevation"); - writer.WriteAttributeString("Id", approvedExeForElevation.Id); + writer.WriteAttributeString("Id", approvedExeForElevation.Id.Id); writer.WriteAttributeString("Key", approvedExeForElevation.Key); if (!String.IsNullOrEmpty(approvedExeForElevation.ValueName)) @@ -569,15 +625,15 @@ namespace WixToolset.Core.Burn.Bundles } } - private void WriteBurnManifestContainerAttributes(XmlTextWriter writer, string executableName, WixBundleContainerRow container) + private void WriteBurnManifestContainerAttributes(XmlTextWriter writer, string executableName, WixBundleContainerTuple container) { - writer.WriteAttributeString("Id", container.Id); + writer.WriteAttributeString("Id", container.Id.Id); writer.WriteAttributeString("FileSize", container.Size.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString("Hash", container.Hash); if (ContainerType.Detached == container.Type) { - string resolvedUrl = this.ResolveUrl(container.DownloadUrl, null, null, container.Id, container.Name); + string resolvedUrl = this.ResolveUrl(container.DownloadUrl, null, null, container.Id.Id, container.Name); if (!String.IsNullOrEmpty(resolvedUrl)) { writer.WriteAttributeString("DownloadUrl", resolvedUrl); @@ -593,7 +649,7 @@ namespace WixToolset.Core.Burn.Bundles { if (!String.IsNullOrEmpty(container.DownloadUrl)) { - Messaging.Instance.OnMessage(WixWarnings.DownloadUrlNotSupportedForAttachedContainers(container.SourceLineNumbers, container.Id)); + this.Messaging.Write(WarningMessages.DownloadUrlNotSupportedForAttachedContainers(container.SourceLineNumbers, container.Id.Id)); } writer.WriteAttributeString("FilePath", executableName); // attached containers use the name of the bundle since they are attached to the executable. @@ -603,11 +659,11 @@ namespace WixToolset.Core.Burn.Bundles } } - private void WriteBurnManifestPayloadAttributes(XmlTextWriter writer, WixBundlePayloadRow payload, bool embeddedOnly, Dictionary allPayloads) + private void WriteBurnManifestPayloadAttributes(XmlTextWriter writer, WixBundlePayloadTuple payload, bool embeddedOnly, Dictionary allPayloads) { Debug.Assert(!embeddedOnly || PackagingType.Embedded == payload.Packaging); - writer.WriteAttributeString("Id", payload.Id); + writer.WriteAttributeString("Id", payload.Id.Id); writer.WriteAttributeString("FilePath", payload.Name); writer.WriteAttributeString("FileSize", payload.FileSize.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString("Hash", payload.Hash); @@ -632,22 +688,22 @@ namespace WixToolset.Core.Burn.Bundles case PackagingType.Embedded: // this means it's in a container. if (!String.IsNullOrEmpty(payload.DownloadUrl)) { - Messaging.Instance.OnMessage(WixWarnings.DownloadUrlNotSupportedForEmbeddedPayloads(payload.SourceLineNumbers, payload.Id)); + this.Messaging.Write(WarningMessages.DownloadUrlNotSupportedForEmbeddedPayloads(payload.SourceLineNumbers, payload.Id.Id)); } writer.WriteAttributeString("Packaging", "embedded"); writer.WriteAttributeString("SourcePath", payload.EmbeddedId); - if (Compiler.BurnUXContainerId != payload.Container) + if (BurnConstants.BurnUXContainerName != payload.ContainerRef) { - writer.WriteAttributeString("Container", payload.Container); + writer.WriteAttributeString("Container", payload.ContainerRef); } break; case PackagingType.External: - string packageId = payload.ParentPackagePayload; - string parentUrl = payload.ParentPackagePayload == null ? null : allPayloads[payload.ParentPackagePayload].DownloadUrl; - string resolvedUrl = this.ResolveUrl(payload.DownloadUrl, parentUrl, packageId, payload.Id, payload.Name); + var packageId = payload.ParentPackagePayloadRef; + var parentUrl = payload.ParentPackagePayloadRef == null ? null : allPayloads[payload.ParentPackagePayloadRef].DownloadUrl; + var resolvedUrl = this.ResolveUrl(payload.DownloadUrl, parentUrl, packageId, payload.Id.Id, payload.Name); if (!String.IsNullOrEmpty(resolvedUrl)) { writer.WriteAttributeString("DownloadUrl", resolvedUrl); @@ -662,9 +718,9 @@ namespace WixToolset.Core.Burn.Bundles break; } - if (!String.IsNullOrEmpty(payload.Catalog)) + if (!String.IsNullOrEmpty(payload.CatalogRef)) { - writer.WriteAttributeString("Catalog", payload.Catalog); + writer.WriteAttributeString("Catalog", payload.CatalogRef); } } @@ -682,6 +738,5 @@ namespace WixToolset.Core.Burn.Bundles return resolved; } -#endif } } diff --git a/src/WixToolset.Core.Burn/Bundles/CreateContainerCommand.cs b/src/WixToolset.Core.Burn/Bundles/CreateContainerCommand.cs index c9dd2671..937721a6 100644 --- a/src/WixToolset.Core.Burn/Bundles/CreateContainerCommand.cs +++ b/src/WixToolset.Core.Burn/Bundles/CreateContainerCommand.cs @@ -4,24 +4,39 @@ namespace WixToolset.Core.Burn.Bundles { using System; using System.Collections.Generic; - using System.Diagnostics; using System.IO; using System.Linq; + using WixToolset.Core.Native; using WixToolset.Data; + using WixToolset.Data.Tuples; /// /// Creates cabinet files. /// internal class CreateContainerCommand { -#if TODO - public CompressionLevel DefaultCompressionLevel { private get; set; } + public CreateContainerCommand(IEnumerable payloads, string outputPath, CompressionLevel? compressionLevel) + { + this.Payloads = payloads; + this.OutputPath = outputPath; + this.CompressionLevel = compressionLevel; + } + + public CreateContainerCommand(string manifestPath, IEnumerable payloads, string outputPath, CompressionLevel? compressionLevel) + { + this.ManifestFile = manifestPath; + this.Payloads = payloads; + this.OutputPath = outputPath; + this.CompressionLevel = compressionLevel; + } + + private CompressionLevel? CompressionLevel { get; } - public IEnumerable Payloads { private get; set; } + private string ManifestFile { get; } - public string ManifestFile { private get; set; } + private string OutputPath { get; } - public string OutputPath { private get; set; } + private IEnumerable Payloads { get; } public string Hash { get; private set; } @@ -29,40 +44,34 @@ namespace WixToolset.Core.Burn.Bundles public void Execute() { - int payloadCount = this.Payloads.Count(); // The number of embedded payloads + var payloadCount = this.Payloads.Count(); // The number of embedded payloads if (!String.IsNullOrEmpty(this.ManifestFile)) { ++payloadCount; } - using (var cab = new WixCreateCab(Path.GetFileName(this.OutputPath), Path.GetDirectoryName(this.OutputPath), payloadCount, 0, 0, this.DefaultCompressionLevel)) - { - // If a manifest was provided always add it as "payload 0" to the container. - if (!String.IsNullOrEmpty(this.ManifestFile)) - { - cab.AddFile(this.ManifestFile, "0"); - } + var cabinetPath = Path.GetFullPath(this.OutputPath); - foreach (WixBundlePayloadRow payload in this.Payloads) - { - Debug.Assert(PackagingType.Embedded == payload.Packaging); + var files = new List(); - Messaging.Instance.OnMessage(WixVerboses.LoadingPayload(payload.FullFileName)); + // If a manifest was provided always add it as "payload 0" to the container. + if (!String.IsNullOrEmpty(this.ManifestFile)) + { + files.Add(new CabinetCompressFile(this.ManifestFile, "0")); + } - cab.AddFile(payload.FullFileName, payload.EmbeddedId); - } + files.AddRange(this.Payloads.Select(p => new CabinetCompressFile(p.SourceFile.Path, p.EmbeddedId))); - cab.Complete(); - } + var cab = new Cabinet(cabinetPath); + cab.Compress(files, this.CompressionLevel ?? Data.CompressionLevel.Mszip); // Now that the container is created, set the outputs of the command. - FileInfo fileInfo = new FileInfo(this.OutputPath); + var fileInfo = new FileInfo(cabinetPath); - this.Hash = Common.GetFileHash(fileInfo.FullName); + this.Hash = BundleHashAlgorithm.Hash(fileInfo); this.Size = fileInfo.Length; } -#endif } } diff --git a/src/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs b/src/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs new file mode 100644 index 00000000..612e0e11 --- /dev/null +++ b/src/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs @@ -0,0 +1,134 @@ +// 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. + +namespace WixToolset.Core.Burn.Bundles +{ + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Linq; + using WixToolset.Data; + using WixToolset.Data.Burn; + using WixToolset.Data.Tuples; + using WixToolset.Extensibility.Data; + using WixToolset.Extensibility.Services; + + internal class CreateNonUXContainers + { + public CreateNonUXContainers(IBackendHelper backendHelper, IntermediateSection section, WixBootstrapperApplicationTuple bootstrapperApplicationTuple, Dictionary payloadTuples, string intermediateFolder, string layoutFolder, CompressionLevel? defaultCompressionLevel) + { + this.BackendHelper = backendHelper; + this.Section = section; + this.BootstrapperApplicationTuple = bootstrapperApplicationTuple; + this.PayloadTuples = payloadTuples; + this.IntermediateFolder = intermediateFolder; + this.LayoutFolder = layoutFolder; + this.DefaultCompressionLevel = defaultCompressionLevel; + } + + public IEnumerable FileTransfers { get; private set; } + + public WixBundleContainerTuple UXContainer { get; set; } + + public IEnumerable UXContainerPayloads { get; private set; } + + public IEnumerable Containers { get; private set; } + + private IBackendHelper BackendHelper { get; } + + private IntermediateSection Section { get; } + + private WixBootstrapperApplicationTuple BootstrapperApplicationTuple { get; } + + private Dictionary PayloadTuples { get; } + + private string IntermediateFolder { get; } + + private string LayoutFolder { get; } + + private CompressionLevel? DefaultCompressionLevel { get; } + + public void Execute() + { + var fileTransfers = new List(); + + var uxPayloadTuples = new List(); + + var attachedContainerIndex = 1; // count starts at one because UX container is "0". + + var containerTuples = this.Section.Tuples.OfType().ToList(); + + var payloadsByContainer = this.PayloadTuples.Values.ToLookup(p => p.ContainerRef); + + foreach (var container in containerTuples) + { + var containerId = container.Id.Id; + + var containerPayloads = payloadsByContainer[containerId]; + + if (!containerPayloads.Any()) + { + if (containerId != BurnConstants.BurnDefaultAttachedContainerName) + { + // TODO: display warning that we're ignoring container that ended up with no paylods in it. + } + } + else if (BurnConstants.BurnUXContainerName == containerId) + { + this.UXContainer = container; + + container.WorkingPath = Path.Combine(this.IntermediateFolder, container.Name); + container.AttachedContainerIndex = 0; + + // Gather the list of UX payloads but ensure the BootstrapperApplication Payload is the first + // in the list since that is the Payload that Burn attempts to load. + var baPayloadId = this.BootstrapperApplicationTuple.Id.Id; + + foreach (var uxPayload in containerPayloads) + { + if (uxPayload.Id.Id == baPayloadId) + { + uxPayloadTuples.Insert(0, uxPayload); + } + else + { + uxPayloadTuples.Add(uxPayload); + } + } + } + else + { + container.WorkingPath = Path.Combine(this.IntermediateFolder, container.Name); + + // Add detached containers to the list of file transfers. + if (ContainerType.Detached == container.Type) + { + var transfer = this.BackendHelper.CreateFileTransfer(container.WorkingPath, Path.Combine(this.LayoutFolder, container.Name), true, container.SourceLineNumbers); + fileTransfers.Add(transfer); + } + else // update the attached container index. + { + Debug.Assert(ContainerType.Attached == container.Type); + + container.AttachedContainerIndex = attachedContainerIndex; + ++attachedContainerIndex; + } + + this.CreateContainer(container, containerPayloads, null); + } + } + + this.Containers = containerTuples; + this.UXContainerPayloads = uxPayloadTuples; + this.FileTransfers = fileTransfers; + } + + private void CreateContainer(WixBundleContainerTuple container, IEnumerable containerPayloads, string manifestFile) + { + var command = new CreateContainerCommand(containerPayloads, container.WorkingPath, this.DefaultCompressionLevel); + command.Execute(); + + container.Hash = command.Hash; + container.Size = command.Size; + } + } +} diff --git a/src/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs b/src/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs index 1ed37046..71e4cfea 100644 --- a/src/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs +++ b/src/WixToolset.Core.Burn/Bundles/GetPackageFacadesCommand.cs @@ -3,61 +3,69 @@ namespace WixToolset.Core.Burn.Bundles { using System.Collections.Generic; + using System.Linq; using WixToolset.Data; + using WixToolset.Data.Tuples; internal class GetPackageFacadesCommand { -#if TODO - public Table PackageTable { private get; set; } - - public Table ExePackageTable { private get; set; } - - public Table MsiPackageTable { private get; set; } + public GetPackageFacadesCommand(IEnumerable chainPackageTuples, IntermediateSection section) + { + this.ChainPackageTuples = chainPackageTuples; + this.Section = section; + } - public Table MspPackageTable { private get; set; } + private IEnumerable ChainPackageTuples { get; } - public Table MsuPackageTable { private get; set; } + private IntermediateSection Section { get; } public IDictionary PackageFacades { get; private set; } public void Execute() { - RowDictionary exePackages = new RowDictionary(this.ExePackageTable); - RowDictionary msiPackages = new RowDictionary(this.MsiPackageTable); - RowDictionary mspPackages = new RowDictionary(this.MspPackageTable); - RowDictionary msuPackages = new RowDictionary(this.MsuPackageTable); + var exePackages = this.Section.Tuples.OfType().ToDictionary(t => t.Id.Id); + var msiPackages = this.Section.Tuples.OfType().ToDictionary(t => t.Id.Id); + var mspPackages = this.Section.Tuples.OfType().ToDictionary(t => t.Id.Id); + var msuPackages = this.Section.Tuples.OfType().ToDictionary(t => t.Id.Id); - Dictionary facades = new Dictionary(this.PackageTable.Rows.Count); + var facades = new Dictionary(); - foreach (WixBundlePackageRow package in this.PackageTable.Rows) + foreach (var package in this.ChainPackageTuples) { - string id = package.WixChainItemId; - PackageFacade facade = null; - + var id = package.Id.Id; switch (package.Type) { - case WixBundlePackageType.Exe: - facade = new PackageFacade(package, exePackages.Get(id)); - break; - - case WixBundlePackageType.Msi: - facade = new PackageFacade(package, msiPackages.Get(id)); - break; - - case WixBundlePackageType.Msp: - facade = new PackageFacade(package, mspPackages.Get(id)); - break; - - case WixBundlePackageType.Msu: - facade = new PackageFacade(package, msuPackages.Get(id)); - break; + case WixBundlePackageType.Exe: + if (exePackages.TryGetValue(id, out var exePackage)) + { + facades.Add(id, new PackageFacade(package, exePackage)); + } + break; + + case WixBundlePackageType.Msi: + if (msiPackages.TryGetValue(id, out var msiPackage)) + { + facades.Add(id, new PackageFacade(package, msiPackage)); + } + break; + + case WixBundlePackageType.Msp: + if (mspPackages.TryGetValue(id, out var mspPackage)) + { + facades.Add(id, new PackageFacade(package, mspPackage)); + } + break; + + case WixBundlePackageType.Msu: + if (msuPackages.TryGetValue(id, out var msuPackage)) + { + facades.Add(id, new PackageFacade(package, msuPackage)); + } + break; } - - facades.Add(id, facade); } this.PackageFacades = facades; } -#endif } } diff --git a/src/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs b/src/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs index 48923ba1..8ead0952 100644 --- a/src/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs +++ b/src/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs @@ -5,24 +5,35 @@ namespace WixToolset.Core.Burn.Bundles using System; using System.Collections.Generic; using WixToolset.Data; + using WixToolset.Data.Tuples; + using WixToolset.Extensibility.Services; internal class OrderPackagesAndRollbackBoundariesCommand { -#if TODO - public Table WixGroupTable { private get; set; } + public OrderPackagesAndRollbackBoundariesCommand(IMessaging messaging, IEnumerable groupTuples, Dictionary boundaryTuples, IDictionary packageFacades) + { + this.Messaging = messaging; + this.GroupTuples = groupTuples; + this.Boundaries = boundaryTuples; + this.PackageFacades = packageFacades; + } + + private IMessaging Messaging { get; } + + public IEnumerable GroupTuples { get; } - public RowDictionary Boundaries { private get; set; } + public Dictionary Boundaries { get; } - public IDictionary PackageFacades { private get; set; } + public IDictionary PackageFacades { get; } public IEnumerable OrderedPackageFacades { get; private set; } - public IEnumerable UsedRollbackBoundaries { get; private set; } + public IEnumerable UsedRollbackBoundaries { get; private set; } public void Execute() { - List orderedFacades = new List(); - List usedBoundaries = new List(); + var orderedFacades = new List(); + var usedBoundaries = new List(); // Process the chain of packages to add them in the correct order // and assign the forward rollback boundaries as appropriate. Remember @@ -33,44 +44,44 @@ namespace WixToolset.Core.Burn.Bundles // We handle uninstall (aka: backwards) rollback boundaries after // we get these install/repair (aka: forward) rollback boundaries // defined. - WixBundleRollbackBoundaryRow previousRollbackBoundary = null; - WixBundleRollbackBoundaryRow lastRollbackBoundary = null; - bool boundaryHadX86Package = false; + WixBundleRollbackBoundaryTuple previousRollbackBoundary = null; + WixBundleRollbackBoundaryTuple lastRollbackBoundary = null; + var boundaryHadX86Package = false; - foreach (WixGroupRow row in this.WixGroupTable.Rows) + foreach (var groupTuple in this.GroupTuples) { - if (ComplexReferenceChildType.Package == row.ChildType && ComplexReferenceParentType.PackageGroup == row.ParentType && "WixChain" == row.ParentId) + if (ComplexReferenceChildType.Package == groupTuple.ChildType && ComplexReferenceParentType.PackageGroup == groupTuple.ParentType && "WixChain" == groupTuple.ParentId) { - PackageFacade facade = null; - if (PackageFacades.TryGetValue(row.ChildId, out facade)) + if (this.PackageFacades.TryGetValue(groupTuple.ChildId, out var facade)) { if (null != previousRollbackBoundary) { usedBoundaries.Add(previousRollbackBoundary); - facade.Package.RollbackBoundary = previousRollbackBoundary.ChainPackageId; + facade.PackageTuple.RollbackBoundaryRef = previousRollbackBoundary.Id.Id; previousRollbackBoundary = null; - boundaryHadX86Package = (facade.Package.x64 == YesNoType.Yes); + boundaryHadX86Package = facade.PackageTuple.Win64; } // Error if MSI transaction has x86 package preceding x64 packages - if ((lastRollbackBoundary != null) && (lastRollbackBoundary.Transaction == YesNoType.Yes) + if ((lastRollbackBoundary != null) + && lastRollbackBoundary.Transaction == true && boundaryHadX86Package - && (facade.Package.x64 == YesNoType.Yes)) + && facade.PackageTuple.Win64) { - Messaging.Instance.OnMessage(WixErrors.MsiTransactionX86BeforeX64(lastRollbackBoundary.SourceLineNumbers)); + this.Messaging.Write(ErrorMessages.MsiTransactionX86BeforeX64(lastRollbackBoundary.SourceLineNumbers)); } - boundaryHadX86Package = boundaryHadX86Package || (facade.Package.x64 == YesNoType.No); + boundaryHadX86Package |= facade.PackageTuple.Win64; orderedFacades.Add(facade); } else // must be a rollback boundary. { // Discard the next rollback boundary if we have a previously defined boundary. - WixBundleRollbackBoundaryRow nextRollbackBoundary = Boundaries.Get(row.ChildId); + var nextRollbackBoundary = this.Boundaries[groupTuple.ChildId]; if (null != previousRollbackBoundary) { - Messaging.Instance.OnMessage(WixWarnings.DiscardedRollbackBoundary(nextRollbackBoundary.SourceLineNumbers, nextRollbackBoundary.ChainPackageId)); + this.Messaging.Write(WarningMessages.DiscardedRollbackBoundary(nextRollbackBoundary.SourceLineNumbers, nextRollbackBoundary.Id.Id)); } else { @@ -83,7 +94,7 @@ namespace WixToolset.Core.Burn.Bundles if (null != previousRollbackBoundary) { - Messaging.Instance.OnMessage(WixWarnings.DiscardedRollbackBoundary(previousRollbackBoundary.SourceLineNumbers, previousRollbackBoundary.ChainPackageId)); + this.Messaging.Write(WarningMessages.DiscardedRollbackBoundary(previousRollbackBoundary.SourceLineNumbers, previousRollbackBoundary.Id.Id)); } // With the forward rollback boundaries assigned, we can now go @@ -120,14 +131,14 @@ namespace WixToolset.Core.Burn.Bundles foreach (PackageFacade package in orderedFacades) { - if (null != package.Package.RollbackBoundary) + if (null != package.PackageTuple.RollbackBoundaryRef) { if (null != previousFacade) { - previousFacade.Package.RollbackBoundaryBackward = previousRollbackBoundaryId; + previousFacade.PackageTuple.RollbackBoundaryBackwardRef = previousRollbackBoundaryId; } - previousRollbackBoundaryId = package.Package.RollbackBoundary; + previousRollbackBoundaryId = package.PackageTuple.RollbackBoundaryRef; } previousFacade = package; @@ -135,12 +146,11 @@ namespace WixToolset.Core.Burn.Bundles if (!String.IsNullOrEmpty(previousRollbackBoundaryId) && null != previousFacade) { - previousFacade.Package.RollbackBoundaryBackward = previousRollbackBoundaryId; + previousFacade.PackageTuple.RollbackBoundaryBackwardRef = previousRollbackBoundaryId; } this.OrderedPackageFacades = orderedFacades; this.UsedRollbackBoundaries = usedBoundaries; } -#endif } } diff --git a/src/WixToolset.Core.Burn/Bundles/PackageFacade.cs b/src/WixToolset.Core.Burn/Bundles/PackageFacade.cs index c68a8311..8b1711a1 100644 --- a/src/WixToolset.Core.Burn/Bundles/PackageFacade.cs +++ b/src/WixToolset.Core.Burn/Bundles/PackageFacade.cs @@ -2,57 +2,24 @@ namespace WixToolset.Core.Burn.Bundles { + using System.Diagnostics; + using WixToolset.Data; + using WixToolset.Data.Tuples; + internal class PackageFacade { -#if TODO - private PackageFacade(WixBundlePackageRow package) - { - this.Package = package; - this.Provides = new ProvidesDependencyCollection(); - } - - public PackageFacade(WixBundlePackageRow package, WixBundleExePackageRow exePackage) - : this(package) + public PackageFacade(WixBundlePackageTuple packageTuple, IntermediateTuple specificPackageTuple) { - this.ExePackage = exePackage; - } + Debug.Assert(packageTuple.Id.Id == specificPackageTuple.Id.Id); - public PackageFacade(WixBundlePackageRow package, WixBundleMsiPackageRow msiPackage) - : this(package) - { - this.MsiPackage = msiPackage; + this.PackageTuple = packageTuple; + this.SpecificPackageTuple = specificPackageTuple; } - public PackageFacade(WixBundlePackageRow package, WixBundleMspPackageRow mspPackage) - : this(package) - { - this.MspPackage = mspPackage; - } - - public PackageFacade(WixBundlePackageRow package, WixBundleMsuPackageRow msuPackage) - : this(package) - { - this.MsuPackage = msuPackage; - } - - public WixBundlePackageRow Package { get; private set; } - - public WixBundleExePackageRow ExePackage { get; private set; } - - public WixBundleMsiPackageRow MsiPackage { get; private set; } - - public WixBundleMspPackageRow MspPackage { get; private set; } + public string PackageId => this.PackageTuple.Id.Id; - public WixBundleMsuPackageRow MsuPackage { get; private set; } + public WixBundlePackageTuple PackageTuple { get; } - /// - /// The provides dependencies authored and imported for this package. - /// - /// - /// TODO: Eventually this collection should turn into Rows so they are tracked in the PDB but - /// the relationship with the extension makes it much trickier to pull off. - /// - public ProvidesDependencyCollection Provides { get; private set; } -#endif + public IntermediateTuple SpecificPackageTuple { get; } } } diff --git a/src/WixToolset.Core.Burn/Bundles/ProcessExePackageCommand.cs b/src/WixToolset.Core.Burn/Bundles/ProcessExePackageCommand.cs index 77102c0f..56254a06 100644 --- a/src/WixToolset.Core.Burn/Bundles/ProcessExePackageCommand.cs +++ b/src/WixToolset.Core.Burn/Bundles/ProcessExePackageCommand.cs @@ -3,32 +3,37 @@ namespace WixToolset.Core.Burn.Bundles { using System; - using WixToolset.Data; + using System.Collections.Generic; + using WixToolset.Data.Tuples; /// /// Initializes package state from the Exe contents. /// internal class ProcessExePackageCommand { -#if TODO - public RowDictionary AuthoredPayloads { private get; set; } + public ProcessExePackageCommand(PackageFacade facade, Dictionary payloadTuples) + { + this.AuthoredPayloads = payloadTuples; + this.Facade = facade; + } + + public Dictionary AuthoredPayloads { get; } - public PackageFacade Facade { private get; set; } + public PackageFacade Facade { get; } /// /// Processes the Exe packages to add properties and payloads from the Exe packages. /// public void Execute() { - WixBundlePayloadRow packagePayload = this.AuthoredPayloads.Get(this.Facade.Package.PackagePayload); + var packagePayload = this.AuthoredPayloads[this.Facade.PackageTuple.PayloadRef]; - if (String.IsNullOrEmpty(this.Facade.Package.CacheId)) + if (String.IsNullOrEmpty(this.Facade.PackageTuple.CacheId)) { - this.Facade.Package.CacheId = packagePayload.Hash; + this.Facade.PackageTuple.CacheId = packagePayload.Hash; } - this.Facade.Package.Version = packagePayload.Version; + this.Facade.PackageTuple.Version = packagePayload.Version; } -#endif } } diff --git a/src/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs b/src/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs index 39a71be7..5fcf172f 100644 --- a/src/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs +++ b/src/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs @@ -3,7 +3,6 @@ namespace WixToolset.Core.Burn.Bundles { using System; - using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; @@ -11,47 +10,62 @@ namespace WixToolset.Core.Burn.Bundles using System.Linq; using WixToolset.Data; using WixToolset.Extensibility; - using WixToolset.Core.Native; using Dtf = WixToolset.Dtf.WindowsInstaller; - using WixToolset.Data.Bind; + using WixToolset.Extensibility.Services; + using WixToolset.Data.Tuples; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Extensibility.Data; /// /// Initializes package state from the MSI contents. /// internal class ProcessMsiPackageCommand { -#if TODO private const string PropertySqlFormat = "SELECT `Value` FROM `Property` WHERE `Property` = '{0}'"; - public RowDictionary AuthoredPayloads { private get; set; } + public ProcessMsiPackageCommand(IServiceProvider serviceProvider, IEnumerable backendExtensions, IntermediateSection section, PackageFacade facade, Dictionary payloadTuples) + { + this.Messaging = serviceProvider.GetService(); + this.BackendHelper = serviceProvider.GetService(); + this.PathResolver = serviceProvider.GetService(); + + this.BackendExtensions = backendExtensions; + + this.AuthoredPayloads = payloadTuples; + this.Section = section; + this.Facade = facade; + } + + private IMessaging Messaging { get; } - public PackageFacade Facade { private get; set; } + private IBackendHelper BackendHelper { get; } - public IEnumerable BackendExtensions { private get; set; } + private IPathResolver PathResolver { get; } - public Table MsiFeatureTable { private get; set; } + private IEnumerable BackendExtensions { get; } - public Table MsiPropertyTable { private get; set; } + private Dictionary AuthoredPayloads { get; } - public Table PayloadTable { private get; set; } + private PackageFacade Facade { get; } - public Table RelatedPackageTable { private get; set; } + private IntermediateSection Section { get; } /// /// Processes the MSI packages to add properties and payloads from the MSI packages. /// public void Execute() { - WixBundlePayloadRow packagePayload = this.AuthoredPayloads.Get(this.Facade.Package.PackagePayload); + var packagePayload = this.AuthoredPayloads[this.Facade.PackageTuple.PayloadRef]; - string sourcePath = packagePayload.FullFileName; - bool longNamesInImage = false; - bool compressed = false; - bool x64 = false; + var msiPackage = (WixBundleMsiPackageTuple)this.Facade.SpecificPackageTuple; + + var sourcePath = packagePayload.SourceFile.Path; + var longNamesInImage = false; + var compressed = false; try { // Read data out of the msi database... - using (Dtf.SummaryInfo sumInfo = new Dtf.SummaryInfo(sourcePath, false)) + using (var sumInfo = new Dtf.SummaryInfo(sourcePath, false)) { // 1 is the Word Count summary information stream bit that means // the MSI uses short file names when set. We care about long file @@ -62,83 +76,84 @@ namespace WixToolset.Core.Burn.Bundles // files are compressed in the MSI by default when the bit is set. compressed = 2 == (sumInfo.WordCount & 2); - x64 = (sumInfo.Template.Contains("x64") || sumInfo.Template.Contains("Intel64")); - // 8 is the Word Count summary information stream bit that means // "Elevated privileges are not required to install this package." // in MSI 4.5 and below, if this bit is 0, elevation is required. - this.Facade.Package.PerMachine = (0 == (sumInfo.WordCount & 8)) ? YesNoDefaultType.Yes : YesNoDefaultType.No; - this.Facade.Package.x64 = x64 ? YesNoType.Yes : YesNoType.No; + var perMachine = (0 == (sumInfo.WordCount & 8)); + var x64 = (sumInfo.Template.Contains("x64") || sumInfo.Template.Contains("Intel64")); + + this.Facade.PackageTuple.PerMachine = perMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No; + this.Facade.PackageTuple.Win64 = x64; } - using (Dtf.Database db = new Dtf.Database(sourcePath)) + using (var db = new Dtf.Database(sourcePath)) { - this.Facade.MsiPackage.ProductCode = ProcessMsiPackageCommand.GetProperty(db, "ProductCode"); - this.Facade.MsiPackage.UpgradeCode = ProcessMsiPackageCommand.GetProperty(db, "UpgradeCode"); - this.Facade.MsiPackage.Manufacturer = ProcessMsiPackageCommand.GetProperty(db, "Manufacturer"); - this.Facade.MsiPackage.ProductLanguage = Convert.ToInt32(ProcessMsiPackageCommand.GetProperty(db, "ProductLanguage"), CultureInfo.InvariantCulture); - this.Facade.MsiPackage.ProductVersion = ProcessMsiPackageCommand.GetProperty(db, "ProductVersion"); + msiPackage.ProductCode = ProcessMsiPackageCommand.GetProperty(db, "ProductCode"); + msiPackage.UpgradeCode = ProcessMsiPackageCommand.GetProperty(db, "UpgradeCode"); + msiPackage.Manufacturer = ProcessMsiPackageCommand.GetProperty(db, "Manufacturer"); + msiPackage.ProductLanguage = Convert.ToInt32(ProcessMsiPackageCommand.GetProperty(db, "ProductLanguage"), CultureInfo.InvariantCulture); + msiPackage.ProductVersion = ProcessMsiPackageCommand.GetProperty(db, "ProductVersion"); - if (!Common.IsValidModuleOrBundleVersion(this.Facade.MsiPackage.ProductVersion)) + if (!Common.IsValidModuleOrBundleVersion(msiPackage.ProductVersion)) { // not a proper .NET version (e.g., five fields); can we get a valid four-part version number? string version = null; - string[] versionParts = this.Facade.MsiPackage.ProductVersion.Split('.'); - int count = versionParts.Length; + string[] versionParts = msiPackage.ProductVersion.Split('.'); + var count = versionParts.Length; if (0 < count) { version = versionParts[0]; - for (int i = 1; i < 4 && i < count; ++i) + for (var i = 1; i < 4 && i < count; ++i) { version = String.Concat(version, ".", versionParts[i]); } } - + if (!String.IsNullOrEmpty(version) && Common.IsValidModuleOrBundleVersion(version)) { - Messaging.Instance.OnMessage(WixWarnings.VersionTruncated(this.Facade.Package.SourceLineNumbers, this.Facade.MsiPackage.ProductVersion, sourcePath, version)); - this.Facade.MsiPackage.ProductVersion = version; + this.Messaging.Write(WarningMessages.VersionTruncated(this.Facade.PackageTuple.SourceLineNumbers, msiPackage.ProductVersion, sourcePath, version)); + msiPackage.ProductVersion = version; } else { - Messaging.Instance.OnMessage(WixErrors.InvalidProductVersion(this.Facade.Package.SourceLineNumbers, this.Facade.MsiPackage.ProductVersion, sourcePath)); + this.Messaging.Write(ErrorMessages.InvalidProductVersion(this.Facade.PackageTuple.SourceLineNumbers, msiPackage.ProductVersion, sourcePath)); } } - if (String.IsNullOrEmpty(this.Facade.Package.CacheId)) + if (String.IsNullOrEmpty(this.Facade.PackageTuple.CacheId)) { - this.Facade.Package.CacheId = String.Format("{0}v{1}", this.Facade.MsiPackage.ProductCode, this.Facade.MsiPackage.ProductVersion); + this.Facade.PackageTuple.CacheId = String.Format("{0}v{1}", msiPackage.ProductCode, msiPackage.ProductVersion); } - if (String.IsNullOrEmpty(this.Facade.Package.DisplayName)) + if (String.IsNullOrEmpty(this.Facade.PackageTuple.DisplayName)) { - this.Facade.Package.DisplayName = ProcessMsiPackageCommand.GetProperty(db, "ProductName"); + this.Facade.PackageTuple.DisplayName = ProcessMsiPackageCommand.GetProperty(db, "ProductName"); } - if (String.IsNullOrEmpty(this.Facade.Package.Description)) + if (String.IsNullOrEmpty(this.Facade.PackageTuple.Description)) { - this.Facade.Package.Description = ProcessMsiPackageCommand.GetProperty(db, "ARPCOMMENTS"); + this.Facade.PackageTuple.Description = ProcessMsiPackageCommand.GetProperty(db, "ARPCOMMENTS"); } - ISet payloadNames = this.GetPayloadTargetNames(); + var payloadNames = this.GetPayloadTargetNames(packagePayload.Id.Id); - ISet msiPropertyNames = this.GetMsiPropertyNames(); + var msiPropertyNames = this.GetMsiPropertyNames(packagePayload.Id.Id); - this.SetPerMachineAppropriately(db, sourcePath); + this.SetPerMachineAppropriately(db, msiPackage, sourcePath); // Ensure the MSI package is appropriately marked visible or not. - this.SetPackageVisibility(db, msiPropertyNames); + this.SetPackageVisibility(db, msiPackage, msiPropertyNames); // Unless the MSI or setup code overrides the default, set MSIFASTINSTALL for best performance. if (!msiPropertyNames.Contains("MSIFASTINSTALL") && !ProcessMsiPackageCommand.HasProperty(db, "MSIFASTINSTALL")) { - this.AddMsiProperty("MSIFASTINSTALL", "7"); + this.AddMsiProperty(msiPackage, "MSIFASTINSTALL", "7"); } this.CreateRelatedPackages(db); // If feature selection is enabled, represent the Feature table in the manifest. - if (this.Facade.MsiPackage.EnableFeatureSelection) + if ((msiPackage.Attributes & WixBundleMsiPackageAttributes.EnableFeatureSelection) == WixBundleMsiPackageAttributes.EnableFeatureSelection) { this.CreateMsiFeatures(db); } @@ -148,90 +163,92 @@ namespace WixToolset.Core.Burn.Bundles // Add all external files as package payloads and calculate the total install size as the rollup of // File table's sizes. - this.Facade.Package.InstallSize = this.ImportExternalFileAsPayloadsAndReturnInstallSize(db, packagePayload, longNamesInImage, compressed, payloadNames); + this.Facade.PackageTuple.InstallSize = this.ImportExternalFileAsPayloadsAndReturnInstallSize(db, packagePayload, longNamesInImage, compressed, payloadNames); // Add all dependency providers from the MSI. - this.ImportDependencyProviders(db); + this.ImportDependencyProviders(msiPackage, db); } } catch (Dtf.InstallerException e) { - Messaging.Instance.OnMessage(WixErrors.UnableToReadPackageInformation(this.Facade.Package.SourceLineNumbers, sourcePath, e.Message)); + this.Messaging.Write(ErrorMessages.UnableToReadPackageInformation(this.Facade.PackageTuple.SourceLineNumbers, sourcePath, e.Message)); } } - private ISet GetPayloadTargetNames() + private ISet GetPayloadTargetNames(string packageId) { - IEnumerable payloadNames = this.PayloadTable.RowsAs() - .Where(r => r.Package == this.Facade.Package.WixChainItemId) - .Select(r => r.Name); + var payloadNames = this.Section.Tuples.OfType() + .Where(p => p.PackageRef == packageId) + .Select(p => p.Name); return new HashSet(payloadNames, StringComparer.OrdinalIgnoreCase); } - private ISet GetMsiPropertyNames() + private ISet GetMsiPropertyNames(string packageId) { - IEnumerable properties = this.MsiPropertyTable.RowsAs() - .Where(r => r.ChainPackageId == this.Facade.Package.WixChainItemId) - .Select(r => r.Name); + var properties = this.Section.Tuples.OfType() + .Where(p => p.Id.Id == packageId) + .Select(p => p.Name); return new HashSet(properties, StringComparer.Ordinal); } - private void SetPerMachineAppropriately(Dtf.Database db, string sourcePath) + private void SetPerMachineAppropriately(Dtf.Database db, WixBundleMsiPackageTuple msiPackage, string sourcePath) { - if (this.Facade.MsiPackage.ForcePerMachine) + if (msiPackage.ForcePerMachine) { - if (YesNoDefaultType.No == this.Facade.Package.PerMachine) + if (YesNoDefaultType.No == this.Facade.PackageTuple.PerMachine) { - Messaging.Instance.OnMessage(WixWarnings.PerUserButForcingPerMachine(this.Facade.Package.SourceLineNumbers, sourcePath)); - this.Facade.Package.PerMachine = YesNoDefaultType.Yes; // ensure that we think the package is per-machine. + this.Messaging.Write(WarningMessages.PerUserButForcingPerMachine(this.Facade.PackageTuple.SourceLineNumbers, sourcePath)); + this.Facade.PackageTuple.PerMachine = YesNoDefaultType.Yes; // ensure that we think the package is per-machine. } // Force ALLUSERS=1 via the MSI command-line. - this.AddMsiProperty("ALLUSERS", "1"); + this.AddMsiProperty(msiPackage, "ALLUSERS", "1"); } else { - string allusers = ProcessMsiPackageCommand.GetProperty(db, "ALLUSERS"); + var allusers = ProcessMsiPackageCommand.GetProperty(db, "ALLUSERS"); if (String.IsNullOrEmpty(allusers)) { // Not forced per-machine and no ALLUSERS property, flip back to per-user. - if (YesNoDefaultType.Yes == this.Facade.Package.PerMachine) + if (YesNoDefaultType.Yes == this.Facade.PackageTuple.PerMachine) { - Messaging.Instance.OnMessage(WixWarnings.ImplicitlyPerUser(this.Facade.Package.SourceLineNumbers, sourcePath)); - this.Facade.Package.PerMachine = YesNoDefaultType.No; + this.Messaging.Write(WarningMessages.ImplicitlyPerUser(this.Facade.PackageTuple.SourceLineNumbers, sourcePath)); + this.Facade.PackageTuple.PerMachine = YesNoDefaultType.No; } } else if (allusers.Equals("1", StringComparison.Ordinal)) { - if (YesNoDefaultType.No == this.Facade.Package.PerMachine) + if (YesNoDefaultType.No == this.Facade.PackageTuple.PerMachine) { - Messaging.Instance.OnMessage(WixErrors.PerUserButAllUsersEquals1(this.Facade.Package.SourceLineNumbers, sourcePath)); + this.Messaging.Write(ErrorMessages.PerUserButAllUsersEquals1(this.Facade.PackageTuple.SourceLineNumbers, sourcePath)); } } else if (allusers.Equals("2", StringComparison.Ordinal)) { - Messaging.Instance.OnMessage(WixWarnings.DiscouragedAllUsersValue(this.Facade.Package.SourceLineNumbers, sourcePath, (YesNoDefaultType.Yes == this.Facade.Package.PerMachine) ? "machine" : "user")); + this.Messaging.Write(WarningMessages.DiscouragedAllUsersValue(this.Facade.PackageTuple.SourceLineNumbers, sourcePath, (YesNoDefaultType.Yes == this.Facade.PackageTuple.PerMachine) ? "machine" : "user")); } else { - Messaging.Instance.OnMessage(WixErrors.UnsupportedAllUsersValue(this.Facade.Package.SourceLineNumbers, sourcePath, allusers)); + this.Messaging.Write(ErrorMessages.UnsupportedAllUsersValue(this.Facade.PackageTuple.SourceLineNumbers, sourcePath, allusers)); } } } - private void SetPackageVisibility(Dtf.Database db, ISet msiPropertyNames) + private void SetPackageVisibility(Dtf.Database db, WixBundleMsiPackageTuple msiPackage, ISet msiPropertyNames) { - bool alreadyVisible = !ProcessMsiPackageCommand.HasProperty(db, "ARPSYSTEMCOMPONENT"); + var alreadyVisible = !ProcessMsiPackageCommand.HasProperty(db, "ARPSYSTEMCOMPONENT"); + var visible = (this.Facade.PackageTuple.Attributes & WixBundlePackageAttributes.Visible) == WixBundlePackageAttributes.Visible; - if (alreadyVisible != this.Facade.Package.Visible) // if not already set to the correct visibility. + // If not already set to the correct visibility. + if (alreadyVisible != visible) { // If the authoring specifically added "ARPSYSTEMCOMPONENT", don't do it again. if (!msiPropertyNames.Contains("ARPSYSTEMCOMPONENT")) { - this.AddMsiProperty("ARPSYSTEMCOMPONENT", this.Facade.Package.Visible ? String.Empty : "1"); + this.AddMsiProperty(msiPackage, "ARPSYSTEMCOMPONENT", visible ? String.Empty : "1"); } } } @@ -241,30 +258,35 @@ namespace WixToolset.Core.Burn.Bundles // Represent the Upgrade table as related packages. if (db.Tables.Contains("Upgrade")) { - using (Dtf.View view = db.OpenView("SELECT `UpgradeCode`, `VersionMin`, `VersionMax`, `Language`, `Attributes` FROM `Upgrade`")) + using (var view = db.OpenView("SELECT `UpgradeCode`, `VersionMin`, `VersionMax`, `Language`, `Attributes` FROM `Upgrade`")) { view.Execute(); while (true) { - using (Dtf.Record record = view.Fetch()) + using (var record = view.Fetch()) { if (null == record) { break; } - WixBundleRelatedPackageRow related = (WixBundleRelatedPackageRow)this.RelatedPackageTable.CreateRow(this.Facade.Package.SourceLineNumbers); - related.ChainPackageId = this.Facade.Package.WixChainItemId; - related.Id = record.GetString(1); - related.MinVersion = record.GetString(2); - related.MaxVersion = record.GetString(3); - related.Languages = record.GetString(4); - - int attributes = record.GetInteger(5); - related.OnlyDetect = (attributes & MsiInterop.MsidbUpgradeAttributesOnlyDetect) == MsiInterop.MsidbUpgradeAttributesOnlyDetect; - related.MinInclusive = (attributes & MsiInterop.MsidbUpgradeAttributesVersionMinInclusive) == MsiInterop.MsidbUpgradeAttributesVersionMinInclusive; - related.MaxInclusive = (attributes & MsiInterop.MsidbUpgradeAttributesVersionMaxInclusive) == MsiInterop.MsidbUpgradeAttributesVersionMaxInclusive; - related.LangInclusive = (attributes & MsiInterop.MsidbUpgradeAttributesLanguagesExclusive) == 0; + var recordAttributes = record.GetInteger(5); + + var attributes = WixBundleRelatedPackageAttributes.None; + attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect) == WindowsInstallerConstants.MsidbUpgradeAttributesOnlyDetect ? WixBundleRelatedPackageAttributes.OnlyDetect : 0; + attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesVersionMinInclusive ? WixBundleRelatedPackageAttributes.MinInclusive : 0; + attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive ? WixBundleRelatedPackageAttributes.MaxInclusive : 0; + attributes |= (recordAttributes & WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive) == WindowsInstallerConstants.MsidbUpgradeAttributesLanguagesExclusive ? WixBundleRelatedPackageAttributes.LangInclusive : 0; + + var related = new WixBundleRelatedPackageTuple(this.Facade.PackageTuple.SourceLineNumbers) + { + PackageRef = this.Facade.PackageId, + RelatedId = record.GetString(1), + MinVersion = record.GetString(2), + MaxVersion = record.GetString(3), + Languages = record.GetString(4), + Attributes = attributes, + }; } } } @@ -275,26 +297,26 @@ namespace WixToolset.Core.Burn.Bundles { if (db.Tables.Contains("Feature")) { - using (Dtf.View featureView = db.OpenView("SELECT `Component_` FROM `FeatureComponents` WHERE `Feature_` = ?")) - using (Dtf.View componentView = db.OpenView("SELECT `FileSize` FROM `File` WHERE `Component_` = ?")) + using (var featureView = db.OpenView("SELECT `Component_` FROM `FeatureComponents` WHERE `Feature_` = ?")) + using (var componentView = db.OpenView("SELECT `FileSize` FROM `File` WHERE `Component_` = ?")) { - using (Dtf.Record featureRecord = new Dtf.Record(1)) - using (Dtf.Record componentRecord = new Dtf.Record(1)) + using (var featureRecord = new Dtf.Record(1)) + using (var componentRecord = new Dtf.Record(1)) { - using (Dtf.View allFeaturesView = db.OpenView("SELECT * FROM `Feature`")) + using (var allFeaturesView = db.OpenView("SELECT * FROM `Feature`")) { allFeaturesView.Execute(); while (true) { - using (Dtf.Record allFeaturesResultRecord = allFeaturesView.Fetch()) + using (var allFeaturesResultRecord = allFeaturesView.Fetch()) { if (null == allFeaturesResultRecord) { break; } - string featureName = allFeaturesResultRecord.GetString(1); + var featureName = allFeaturesResultRecord.GetString(1); // Calculate the Feature size. featureRecord.SetString(1, featureName); @@ -304,43 +326,46 @@ namespace WixToolset.Core.Burn.Bundles long size = 0; while (true) { - using (Dtf.Record componentResultRecord = featureView.Fetch()) + using (var componentResultRecord = featureView.Fetch()) { if (null == componentResultRecord) { break; } - string component = componentResultRecord.GetString(1); + + var component = componentResultRecord.GetString(1); componentRecord.SetString(1, component); componentView.Execute(componentRecord); while (true) { - using (Dtf.Record fileResultRecord = componentView.Fetch()) + using (var fileResultRecord = componentView.Fetch()) { if (null == fileResultRecord) { break; } - string fileSize = fileResultRecord.GetString(1); + var fileSize = fileResultRecord.GetString(1); size += Convert.ToInt32(fileSize, CultureInfo.InvariantCulture.NumberFormat); } } } } - WixBundleMsiFeatureRow feature = (WixBundleMsiFeatureRow)this.MsiFeatureTable.CreateRow(this.Facade.Package.SourceLineNumbers); - feature.ChainPackageId = this.Facade.Package.WixChainItemId; - feature.Name = featureName; - feature.Parent = allFeaturesResultRecord.GetString(2); - feature.Title = allFeaturesResultRecord.GetString(3); - feature.Description = allFeaturesResultRecord.GetString(4); - feature.Display = allFeaturesResultRecord.GetInteger(5); - feature.Level = allFeaturesResultRecord.GetInteger(6); - feature.Directory = allFeaturesResultRecord.GetString(7); - feature.Attributes = allFeaturesResultRecord.GetInteger(8); - feature.Size = size; + var feature = new WixBundleMsiFeatureTuple(this.Facade.PackageTuple.SourceLineNumbers, new Identifier(AccessModifier.Private, this.Facade.PackageId, featureName)) + { + PackageRef = this.Facade.PackageId, + Name = featureName, + Parent = allFeaturesResultRecord.GetString(2), + Title = allFeaturesResultRecord.GetString(3), + Description = allFeaturesResultRecord.GetString(4), + Display = allFeaturesResultRecord.GetInteger(5), + Level = allFeaturesResultRecord.GetInteger(6), + Directory = allFeaturesResultRecord.GetString(7), + Attributes = allFeaturesResultRecord.GetInteger(8), + Size = size + }; } } } @@ -349,113 +374,119 @@ namespace WixToolset.Core.Burn.Bundles } } - private void ImportExternalCabinetAsPayloads(Dtf.Database db, WixBundlePayloadRow packagePayload, ISet payloadNames) + private void ImportExternalCabinetAsPayloads(Dtf.Database db, WixBundlePayloadTuple packagePayload, ISet payloadNames) { if (db.Tables.Contains("Media")) { - foreach (string cabinet in db.ExecuteStringQuery("SELECT `Cabinet` FROM `Media`")) + foreach (var cabinet in db.ExecuteStringQuery("SELECT `Cabinet` FROM `Media`")) { if (!String.IsNullOrEmpty(cabinet) && !cabinet.StartsWith("#", StringComparison.Ordinal)) { // If we didn't find the Payload as an existing child of the package, we need to // add it. We expect the file to exist on-disk in the same relative location as // the MSI expects to find it... - string cabinetName = Path.Combine(Path.GetDirectoryName(packagePayload.Name), cabinet); + var cabinetName = Path.Combine(Path.GetDirectoryName(packagePayload.Name), cabinet); if (!payloadNames.Contains(cabinetName)) { - string generatedId = Common.GenerateIdentifier("cab", packagePayload.Id, cabinet); - string payloadSourceFile = this.ResolveRelatedFile(packagePayload.UnresolvedSourceFile, cabinet, "Cabinet", this.Facade.Package.SourceLineNumbers, BindStage.Normal); - - WixBundlePayloadRow payload = (WixBundlePayloadRow)this.PayloadTable.CreateRow(this.Facade.Package.SourceLineNumbers); - payload.Id = generatedId; - payload.Name = cabinetName; - payload.SourceFile = payloadSourceFile; - payload.Compressed = packagePayload.Compressed; - payload.UnresolvedSourceFile = cabinetName; - payload.Package = packagePayload.Package; - payload.Container = packagePayload.Container; - payload.ContentFile = true; - payload.EnableSignatureValidation = packagePayload.EnableSignatureValidation; - payload.Packaging = packagePayload.Packaging; - payload.ParentPackagePayload = packagePayload.Id; + var generatedId = Common.GenerateIdentifier("cab", packagePayload.Id.Id, cabinet); + var payloadSourceFile = this.ResolveRelatedFile(packagePayload.SourceFile.Path, packagePayload.UnresolvedSourceFile, cabinet, "Cabinet", this.Facade.PackageTuple.SourceLineNumbers, BindStage.Normal); + + var tuple = new WixBundlePayloadTuple(this.Facade.PackageTuple.SourceLineNumbers, new Identifier(AccessModifier.Private, generatedId)) + { + Name = cabinetName, + SourceFile = new IntermediateFieldPathValue { Path = payloadSourceFile }, + Compressed = packagePayload.Compressed, + UnresolvedSourceFile = cabinetName, + PackageRef = packagePayload.PackageRef, + ContainerRef = packagePayload.ContainerRef, + ContentFile = true, + EnableSignatureValidation = packagePayload.EnableSignatureValidation, + Packaging = packagePayload.Packaging, + ParentPackagePayloadRef = packagePayload.Id.Id, + }; + + this.Section.Tuples.Add(tuple); } } } } } - private long ImportExternalFileAsPayloadsAndReturnInstallSize(Dtf.Database db, WixBundlePayloadRow packagePayload, bool longNamesInImage, bool compressed, ISet payloadNames) + private long ImportExternalFileAsPayloadsAndReturnInstallSize(Dtf.Database db, WixBundlePayloadTuple packagePayload, bool longNamesInImage, bool compressed, ISet payloadNames) { long size = 0; if (db.Tables.Contains("Component") && db.Tables.Contains("Directory") && db.Tables.Contains("File")) { - Hashtable directories = new Hashtable(); + var directories = new Dictionary(); // Load up the directory hash table so we will be able to resolve source paths // for files in the MSI database. - using (Dtf.View view = db.OpenView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`")) + using (var view = db.OpenView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`")) { view.Execute(); while (true) { - using (Dtf.Record record = view.Fetch()) + using (var record = view.Fetch()) { if (null == record) { break; } - string sourceName = Common.GetName(record.GetString(3), true, longNamesInImage); - directories.Add(record.GetString(1), new ResolvedDirectory(record.GetString(2), sourceName)); + var sourceName = Common.GetName(record.GetString(3), true, longNamesInImage); + + var resolvedDirectory = this.BackendHelper.CreateResolvedDirectory(record.GetString(2), sourceName); + + directories.Add(record.GetString(1), resolvedDirectory); } } } // Resolve the source paths to external files and add each file size to the total // install size of the package. - using (Dtf.View view = db.OpenView("SELECT `Directory_`, `File`, `FileName`, `File`.`Attributes`, `FileSize` FROM `Component`, `File` WHERE `Component`.`Component`=`File`.`Component_`")) + using (var view = db.OpenView("SELECT `Directory_`, `File`, `FileName`, `File`.`Attributes`, `FileSize` FROM `Component`, `File` WHERE `Component`.`Component`=`File`.`Component_`")) { view.Execute(); while (true) { - using (Dtf.Record record = view.Fetch()) + using (var record = view.Fetch()) { if (null == record) { break; } - // Skip adding the loose files as payloads if it was suppressed. - if (!this.Facade.MsiPackage.SuppressLooseFilePayloadGeneration) + // If the file is explicitly uncompressed or the MSI is uncompressed and the file is not + // explicitly marked compressed then this is an external file. + var compressionBit = record.GetInteger(4); + if (WindowsInstallerConstants.MsidbFileAttributesNoncompressed == (compressionBit & WindowsInstallerConstants.MsidbFileAttributesNoncompressed) || + (!compressed && 0 == (compressionBit & WindowsInstallerConstants.MsidbFileAttributesCompressed))) { - // If the file is explicitly uncompressed or the MSI is uncompressed and the file is not - // explicitly marked compressed then this is an external file. - if (MsiInterop.MsidbFileAttributesNoncompressed == (record.GetInteger(4) & MsiInterop.MsidbFileAttributesNoncompressed) || - (!compressed && 0 == (record.GetInteger(4) & MsiInterop.MsidbFileAttributesCompressed))) + string fileSourcePath = this.PathResolver.GetFileSourcePath(directories, record.GetString(1), record.GetString(3), compressed, longNamesInImage); + var name = Path.Combine(Path.GetDirectoryName(packagePayload.Name), fileSourcePath); + + if (!payloadNames.Contains(name)) { - string fileSourcePath = Binder.GetFileSourcePath(directories, record.GetString(1), record.GetString(3), compressed, longNamesInImage); - string name = Path.Combine(Path.GetDirectoryName(packagePayload.Name), fileSourcePath); + var generatedId = Common.GenerateIdentifier("f", packagePayload.Id.Id, record.GetString(2)); + var payloadSourceFile = this.ResolveRelatedFile(packagePayload.SourceFile.Path, packagePayload.UnresolvedSourceFile, fileSourcePath, "File", this.Facade.PackageTuple.SourceLineNumbers, BindStage.Normal); - if (!payloadNames.Contains(name)) + var tuple = new WixBundlePayloadTuple(this.Facade.PackageTuple.SourceLineNumbers, new Identifier(AccessModifier.Private, generatedId)) { - string generatedId = Common.GenerateIdentifier("f", packagePayload.Id, record.GetString(2)); - string payloadSourceFile = this.ResolveRelatedFile(packagePayload.UnresolvedSourceFile, fileSourcePath, "File", this.Facade.Package.SourceLineNumbers, BindStage.Normal); - - WixBundlePayloadRow payload = (WixBundlePayloadRow)this.PayloadTable.CreateRow(this.Facade.Package.SourceLineNumbers); - payload.Id = generatedId; - payload.Name = name; - payload.SourceFile = payloadSourceFile; - payload.Compressed = packagePayload.Compressed; - payload.UnresolvedSourceFile = name; - payload.Package = packagePayload.Package; - payload.Container = packagePayload.Container; - payload.ContentFile = true; - payload.EnableSignatureValidation = packagePayload.EnableSignatureValidation; - payload.Packaging = packagePayload.Packaging; - payload.ParentPackagePayload = packagePayload.Id; - } + Name = name, + SourceFile = new IntermediateFieldPathValue { Path = payloadSourceFile }, + Compressed = packagePayload.Compressed, + UnresolvedSourceFile = name, + PackageRef = packagePayload.PackageRef, + ContainerRef = packagePayload.ContainerRef, + ContentFile = true, + EnableSignatureValidation = packagePayload.EnableSignatureValidation, + Packaging = packagePayload.Packaging, + ParentPackagePayloadRef = packagePayload.Id.Id, + }; + + this.Section.Tuples.Add(tuple); } } @@ -468,26 +499,30 @@ namespace WixToolset.Core.Burn.Bundles return size; } - private void AddMsiProperty(string name, string value) + private void AddMsiProperty(WixBundleMsiPackageTuple msiPackage, string name, string value) { - WixBundleMsiPropertyRow row = (WixBundleMsiPropertyRow)this.MsiPropertyTable.CreateRow(this.Facade.MsiPackage.SourceLineNumbers); - row.ChainPackageId = this.Facade.Package.WixChainItemId; - row.Name = name; - row.Value = value; + var tuple = new WixBundleMsiPropertyTuple(msiPackage.SourceLineNumbers, new Identifier(AccessModifier.Private, msiPackage.Id.Id, name)) + { + PackageRef = msiPackage.Id.Id, + Name = name, + Value = value + }; + + this.Section.Tuples.Add(tuple); } - private void ImportDependencyProviders(Dtf.Database db) + private void ImportDependencyProviders(WixBundleMsiPackageTuple msiPackage, Dtf.Database db) { if (db.Tables.Contains("WixDependencyProvider")) { - string query = "SELECT `ProviderKey`, `Version`, `DisplayName`, `Attributes` FROM `WixDependencyProvider`"; + var query = "SELECT `ProviderKey`, `Version`, `DisplayName`, `Attributes` FROM `WixDependencyProvider`"; - using (Dtf.View view = db.OpenView(query)) + using (var view = db.OpenView(query)) { view.Execute(); while (true) { - using (Dtf.Record record = view.Fetch()) + using (var record = view.Fetch()) { if (null == record) { @@ -495,34 +530,51 @@ namespace WixToolset.Core.Burn.Bundles } // Import the provider key and attributes. - string providerKey = record.GetString(1); - string version = record.GetString(2) ?? this.Facade.MsiPackage.ProductVersion; - string displayName = record.GetString(3) ?? this.Facade.Package.DisplayName; - int attributes = record.GetInteger(4); - - ProvidesDependency dependency = new ProvidesDependency(providerKey, version, displayName, attributes); - dependency.Imported = true; - - this.Facade.Provides.Add(dependency); + var tuple = new ProvidesDependencyTuple(msiPackage.SourceLineNumbers) + { + PackageRef = msiPackage.Id.Id, + Key = record.GetString(1), + Version = record.GetString(2) ?? msiPackage.ProductVersion, + DisplayName = record.GetString(3) ?? this.Facade.PackageTuple.DisplayName, + Attributes = record.GetInteger(4), + Imported = true + }; + + this.Section.Tuples.Add(tuple); } } } } } - private string ResolveRelatedFile(string sourceFile, string relatedSource, string type, SourceLineNumber sourceLineNumbers, BindStage stage) + private string ResolveRelatedFile(string resolvedSource, string unresolvedSource, string relatedSource, string type, SourceLineNumber sourceLineNumbers, BindStage stage) { + var checkedPaths = new List(); + foreach (var extension in this.BackendExtensions) { - var relatedFile = extension.ResolveRelatedFile(sourceFile, relatedSource, type, sourceLineNumbers, stage); + var resolved = extension.ResolveRelatedFile(unresolvedSource, relatedSource, type, sourceLineNumbers, stage); - if (!String.IsNullOrEmpty(relatedFile)) + if (resolved?.CheckedPaths != null) { - return relatedFile; + checkedPaths.AddRange(resolved.CheckedPaths); + } + + if (!String.IsNullOrEmpty(resolved?.Path)) + { + return resolved?.Path; } } - return null; + var resolvedPath = Path.Combine(Path.GetDirectoryName(resolvedSource), relatedSource); + + if (!File.Exists(resolvedPath)) + { + checkedPaths.Add(resolvedPath); + this.Messaging.Write(ErrorMessages.FileNotFound(sourceLineNumbers, resolvedPath, type, checkedPaths)); + } + + return resolvedPath; } /// @@ -571,6 +623,5 @@ namespace WixToolset.Core.Burn.Bundles return String.Format(CultureInfo.InvariantCulture, ProcessMsiPackageCommand.PropertySqlFormat, property); } -#endif } } diff --git a/src/WixToolset.Core.Burn/Bundles/ProcessMspPackageCommand.cs b/src/WixToolset.Core.Burn/Bundles/ProcessMspPackageCommand.cs index e0390360..dc832d40 100644 --- a/src/WixToolset.Core.Burn/Bundles/ProcessMspPackageCommand.cs +++ b/src/WixToolset.Core.Burn/Bundles/ProcessMspPackageCommand.cs @@ -10,6 +10,8 @@ namespace WixToolset.Core.Burn.Bundles using System.Text; using System.Xml; using WixToolset.Data; + using WixToolset.Data.Tuples; + using WixToolset.Extensibility.Services; using Dtf = WixToolset.Dtf.WindowsInstaller; /// @@ -17,80 +19,92 @@ namespace WixToolset.Core.Burn.Bundles /// internal class ProcessMspPackageCommand { -#if TODO private const string PatchMetadataFormat = "SELECT `Value` FROM `MsiPatchMetadata` WHERE `Property` = '{0}'"; private static readonly Encoding XmlOutputEncoding = new UTF8Encoding(false); - public RowDictionary AuthoredPayloads { private get; set; } + public ProcessMspPackageCommand(IMessaging messaging, IntermediateSection section, PackageFacade facade, Dictionary payloadTuples) + { + this.Messaging = messaging; + + this.AuthoredPayloads = payloadTuples; + this.Section = section; + this.Facade = facade; + } + + public IMessaging Messaging { get; } + + public Dictionary AuthoredPayloads { private get; set; } public PackageFacade Facade { private get; set; } - public Table WixBundlePatchTargetCodeTable { private get; set; } + public IntermediateSection Section { get; } /// /// Processes the Msp packages to add properties and payloads from the Msp packages. /// public void Execute() { - WixBundlePayloadRow packagePayload = this.AuthoredPayloads.Get(this.Facade.Package.PackagePayload); + var packagePayload = this.AuthoredPayloads[this.Facade.PackageTuple.PayloadRef]; + + var mspPackage = (WixBundleMspPackageTuple)this.Facade.SpecificPackageTuple; - string sourcePath = packagePayload.FullFileName; + var sourcePath = packagePayload.SourceFile.Path; try { // Read data out of the msp database... - using (Dtf.SummaryInfo sumInfo = new Dtf.SummaryInfo(sourcePath, false)) + using (var sumInfo = new Dtf.SummaryInfo(sourcePath, false)) { - this.Facade.MspPackage.PatchCode = sumInfo.RevisionNumber.Substring(0, 38); + mspPackage.PatchCode = sumInfo.RevisionNumber.Substring(0, 38); } - using (Dtf.Database db = new Dtf.Database(sourcePath)) + using (var db = new Dtf.Database(sourcePath)) { - if (String.IsNullOrEmpty(this.Facade.Package.DisplayName)) + if (String.IsNullOrEmpty(this.Facade.PackageTuple.DisplayName)) { - this.Facade.Package.DisplayName = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "DisplayName"); + this.Facade.PackageTuple.DisplayName = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "DisplayName"); } - if (String.IsNullOrEmpty(this.Facade.Package.Description)) + if (String.IsNullOrEmpty(this.Facade.PackageTuple.Description)) { - this.Facade.Package.Description = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "Description"); + this.Facade.PackageTuple.Description = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "Description"); } - this.Facade.MspPackage.Manufacturer = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "ManufacturerName"); + mspPackage.Manufacturer = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "ManufacturerName"); } - this.ProcessPatchXml(packagePayload, sourcePath); + this.ProcessPatchXml(packagePayload, mspPackage, sourcePath); } catch (Dtf.InstallerException e) { - Messaging.Instance.OnMessage(WixErrors.UnableToReadPackageInformation(packagePayload.SourceLineNumbers, sourcePath, e.Message)); + this.Messaging.Write(ErrorMessages.UnableToReadPackageInformation(packagePayload.SourceLineNumbers, sourcePath, e.Message)); return; } - if (String.IsNullOrEmpty(this.Facade.Package.CacheId)) + if (String.IsNullOrEmpty(this.Facade.PackageTuple.CacheId)) { - this.Facade.Package.CacheId = this.Facade.MspPackage.PatchCode; + this.Facade.PackageTuple.CacheId = mspPackage.PatchCode; } } - private void ProcessPatchXml(WixBundlePayloadRow packagePayload, string sourcePath) + private void ProcessPatchXml(WixBundlePayloadTuple packagePayload, WixBundleMspPackageTuple mspPackage, string sourcePath) { - HashSet uniqueTargetCodes = new HashSet(); + var uniqueTargetCodes = new HashSet(); - string patchXml = Dtf.Installer.ExtractPatchXmlData(sourcePath); + var patchXml = Dtf.Installer.ExtractPatchXmlData(sourcePath); - XmlDocument doc = new XmlDocument(); + var doc = new XmlDocument(); doc.LoadXml(patchXml); - XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable); + var nsmgr = new XmlNamespaceManager(doc.NameTable); nsmgr.AddNamespace("p", "http://www.microsoft.com/msi/patch_applicability.xsd"); // Determine target ProductCodes and/or UpgradeCodes. foreach (XmlNode node in doc.SelectNodes("/p:MsiPatch/p:TargetProduct", nsmgr)) { // If this patch targets a product code, this is the best case. - XmlNode targetCodeElement = node.SelectSingleNode("p:TargetProductCode", nsmgr); - WixBundlePatchTargetCodeAttributes attributes = WixBundlePatchTargetCodeAttributes.None; + var targetCodeElement = node.SelectSingleNode("p:TargetProductCode", nsmgr); + var attributes = WixBundlePatchTargetCodeAttributes.None; if (ProcessMspPackageCommand.TargetsCode(targetCodeElement)) { @@ -105,45 +119,49 @@ namespace WixToolset.Core.Burn.Bundles } else // this patch targets an unknown number of products { - this.Facade.MspPackage.Attributes |= WixBundleMspPackageAttributes.TargetUnspecified; + mspPackage.Attributes |= WixBundleMspPackageAttributes.TargetUnspecified; } } - string targetCode = targetCodeElement.InnerText; + var targetCode = targetCodeElement.InnerText; if (uniqueTargetCodes.Add(targetCode)) { - WixBundlePatchTargetCodeRow row = (WixBundlePatchTargetCodeRow)this.WixBundlePatchTargetCodeTable.CreateRow(packagePayload.SourceLineNumbers); - row.MspPackageId = packagePayload.Id; - row.TargetCode = targetCode; - row.Attributes = attributes; + var tuple = new WixBundlePatchTargetCodeTuple(packagePayload.SourceLineNumbers) + { + PackageRef = packagePayload.Id.Id, + TargetCode = targetCode, + Attributes = attributes + }; + + this.Section.Tuples.Add(tuple); } } // Suppress patch sequence data for improved performance. - XmlNode root = doc.DocumentElement; + var root = doc.DocumentElement; foreach (XmlNode node in root.SelectNodes("p:SequenceData", nsmgr)) { root.RemoveChild(node); } // Save the XML as compact as possible. - using (StringWriter writer = new StringWriter()) + using (var writer = new StringWriter()) { - XmlWriterSettings settings = new XmlWriterSettings() + var settings = new XmlWriterSettings() { Encoding = ProcessMspPackageCommand.XmlOutputEncoding, Indent = false, - NewLineChars = string.Empty, + NewLineChars = String.Empty, NewLineHandling = NewLineHandling.Replace, }; - using (XmlWriter xmlWriter = XmlWriter.Create(writer, settings)) + using (var xmlWriter = XmlWriter.Create(writer, settings)) { doc.WriteTo(xmlWriter); } - this.Facade.MspPackage.PatchXml = writer.ToString(); + mspPackage.PatchXml = writer.ToString(); } } @@ -175,16 +193,6 @@ namespace WixToolset.Core.Burn.Bundles return String.Format(CultureInfo.InvariantCulture, ProcessMspPackageCommand.PatchMetadataFormat, property); } - private static bool TargetsCode(XmlNode node) - { - if (null != node) - { - XmlAttribute attr = node.Attributes["Validate"]; - return null != attr && "true".Equals(attr.Value); - } - - return false; - } -#endif + private static bool TargetsCode(XmlNode node) => "true" == node?.Attributes["Validate"]?.Value; } } diff --git a/src/WixToolset.Core.Burn/Bundles/ProcessMsuPackageCommand.cs b/src/WixToolset.Core.Burn/Bundles/ProcessMsuPackageCommand.cs index ef720bc1..6a39f42f 100644 --- a/src/WixToolset.Core.Burn/Bundles/ProcessMsuPackageCommand.cs +++ b/src/WixToolset.Core.Burn/Bundles/ProcessMsuPackageCommand.cs @@ -3,29 +3,35 @@ namespace WixToolset.Core.Burn.Bundles { using System; + using System.Collections.Generic; using WixToolset.Data; + using WixToolset.Data.Tuples; /// /// Processes the Msu packages to add properties and payloads from the Msu packages. /// internal class ProcessMsuPackageCommand { -#if TODO - public RowDictionary AuthoredPayloads { private get; set; } + public ProcessMsuPackageCommand(PackageFacade facade, Dictionary payloadTuples) + { + this.AuthoredPayloads = payloadTuples; + this.Facade = facade; + } + + public Dictionary AuthoredPayloads { private get; set; } public PackageFacade Facade { private get; set; } public void Execute() { - WixBundlePayloadRow packagePayload = this.AuthoredPayloads.Get(this.Facade.Package.PackagePayload); + var packagePayload = this.AuthoredPayloads[this.Facade.PackageTuple.PayloadRef]; - if (String.IsNullOrEmpty(this.Facade.Package.CacheId)) + if (String.IsNullOrEmpty(this.Facade.PackageTuple.CacheId)) { - this.Facade.Package.CacheId = packagePayload.Hash; + this.Facade.PackageTuple.CacheId = packagePayload.Hash; } - this.Facade.Package.PerMachine = YesNoDefaultType.Yes; // MSUs are always per-machine. + this.Facade.PackageTuple.PerMachine = YesNoDefaultType.Yes; // MSUs are always per-machine. } -#endif } } diff --git a/src/WixToolset.Core.Burn/Bundles/ProcessPayloadsCommand.cs b/src/WixToolset.Core.Burn/Bundles/ProcessPayloadsCommand.cs index 80f3add3..0560e336 100644 --- a/src/WixToolset.Core.Burn/Bundles/ProcessPayloadsCommand.cs +++ b/src/WixToolset.Core.Burn/Bundles/ProcessPayloadsCommand.cs @@ -9,58 +9,73 @@ namespace WixToolset.Core.Burn.Bundles using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; + using WixToolset.Data; + using WixToolset.Data.Burn; + using WixToolset.Data.Tuples; + using WixToolset.Extensibility.Data; + using WixToolset.Extensibility.Services; internal class ProcessPayloadsCommand { -#if TODO private static readonly Version EmptyVersion = new Version(0, 0, 0, 0); - public IEnumerable Payloads { private get; set; } + public ProcessPayloadsCommand(IServiceProvider serviceProvider, IBackendHelper backendHelper, IEnumerable payloads, PackagingType defaultPackaging, string layoutDirectory) + { + this.Messaging = serviceProvider.GetService(); + + this.BackendHelper = backendHelper; + this.Payloads = payloads; + this.DefaultPackaging = defaultPackaging; + this.LayoutDirectory = layoutDirectory; + } + + public IEnumerable FileTransfers { get; private set; } - public PackagingType DefaultPackaging { private get; set; } + private IMessaging Messaging { get; } - public string LayoutDirectory { private get; set; } + private IBackendHelper BackendHelper { get; } - public IEnumerable FileTransfers { get; private set; } + private IEnumerable Payloads { get; } + + private PackagingType DefaultPackaging { get; } + + private string LayoutDirectory { get; } public void Execute() { - List fileTransfers = new List(); + var fileTransfers = new List(); - foreach (WixBundlePayloadRow payload in this.Payloads) + foreach (var payload in this.Payloads) { - string normalizedPath = payload.Name.Replace('\\', '/'); + var normalizedPath = payload.Name.Replace('\\', '/'); if (normalizedPath.StartsWith("../", StringComparison.Ordinal) || normalizedPath.Contains("/../")) { - Messaging.Instance.OnMessage(WixErrors.PayloadMustBeRelativeToCache(payload.SourceLineNumbers, "Payload", "Name", payload.Name)); + this.Messaging.Write(ErrorMessages.PayloadMustBeRelativeToCache(payload.SourceLineNumbers, "Payload", "Name", payload.Name)); } // Embedded files (aka: files from binary .wixlibs) are not content files (because they are hidden // in the .wixlib). - ObjectField field = (ObjectField)payload.Fields[2]; - payload.ContentFile = !field.EmbeddedFileIndex.HasValue; + var sourceFile = payload.SourceFile; + payload.ContentFile = !sourceFile.EmbeddedFileIndex.HasValue; this.UpdatePayloadPackagingType(payload); - if (String.IsNullOrEmpty(payload.SourceFile)) + if (String.IsNullOrEmpty(sourceFile?.Path)) { // Remote payloads obviously cannot be embedded. Debug.Assert(PackagingType.Embedded != payload.Packaging); } else // not a remote payload so we have a lot more to update. { - this.UpdatePayloadFileInformation(payload); + this.UpdatePayloadFileInformation(payload, sourceFile); - this.UpdatePayloadVersionInformation(payload); + this.UpdatePayloadVersionInformation(payload, sourceFile); // External payloads need to be transfered. if (PackagingType.External == payload.Packaging) { - FileTransfer transfer; - if (FileTransfer.TryCreate(payload.FullFileName, Path.Combine(this.LayoutDirectory, payload.Name), false, "Payload", payload.SourceLineNumbers, out transfer)) - { - fileTransfers.Add(transfer); - } + var transfer = this.BackendHelper.CreateFileTransfer(sourceFile.Path, Path.Combine(this.LayoutDirectory, payload.Name), false, payload.SourceLineNumbers); + fileTransfers.Add(transfer); } } } @@ -68,41 +83,41 @@ namespace WixToolset.Core.Burn.Bundles this.FileTransfers = fileTransfers; } - private void UpdatePayloadPackagingType(WixBundlePayloadRow payload) + private void UpdatePayloadPackagingType(WixBundlePayloadTuple payload) { if (PackagingType.Unknown == payload.Packaging) { - if (YesNoDefaultType.Yes == payload.Compressed) + if (!payload.Compressed.HasValue) { - payload.Packaging = PackagingType.Embedded; + payload.Packaging = this.DefaultPackaging; } - else if (YesNoDefaultType.No == payload.Compressed) + else if (payload.Compressed.Value) { - payload.Packaging = PackagingType.External; + payload.Packaging = PackagingType.Embedded; } else { - payload.Packaging = this.DefaultPackaging; + payload.Packaging = PackagingType.External; } } // Embedded payloads that are not assigned a container already are placed in the default attached // container. - if (PackagingType.Embedded == payload.Packaging && String.IsNullOrEmpty(payload.Container)) + if (PackagingType.Embedded == payload.Packaging && String.IsNullOrEmpty(payload.ContainerRef)) { - payload.Container = Compiler.BurnDefaultAttachedContainerId; + payload.ContainerRef = BurnConstants.BurnDefaultAttachedContainerName; } } - private void UpdatePayloadFileInformation(WixBundlePayloadRow payload) + private void UpdatePayloadFileInformation(WixBundlePayloadTuple payload, IntermediateFieldPathValue sourceFile) { - FileInfo fileInfo = new FileInfo(payload.SourceFile); + var fileInfo = new FileInfo(sourceFile.Path); if (null != fileInfo) { payload.FileSize = (int)fileInfo.Length; - payload.Hash = Common.GetFileHash(fileInfo.FullName); + payload.Hash = BundleHashAlgorithm.Hash(fileInfo); // Try to get the certificate if the payload is a signed file and we're not suppressing signature validation. if (payload.EnableSignatureValidation) @@ -122,9 +137,10 @@ namespace WixToolset.Core.Burn.Bundles byte[] publicKeyIdentifierHash = new byte[128]; uint publicKeyIdentifierHashSize = (uint)publicKeyIdentifierHash.Length; - WixToolset.Core.Native.NativeMethods.HashPublicKeyInfo(certificate.Handle, publicKeyIdentifierHash, ref publicKeyIdentifierHashSize); - StringBuilder sb = new StringBuilder(((int)publicKeyIdentifierHashSize + 1) * 2); - for (int i = 0; i < publicKeyIdentifierHashSize; ++i) + Native.NativeMethods.HashPublicKeyInfo(certificate.Handle, publicKeyIdentifierHash, ref publicKeyIdentifierHashSize); + + var sb = new StringBuilder(((int)publicKeyIdentifierHashSize + 1) * 2); + for (var i = 0; i < publicKeyIdentifierHashSize; ++i) { sb.AppendFormat("{0:X2}", publicKeyIdentifierHash[i]); } @@ -136,14 +152,14 @@ namespace WixToolset.Core.Burn.Bundles } } - private void UpdatePayloadVersionInformation(WixBundlePayloadRow payload) + private void UpdatePayloadVersionInformation(WixBundlePayloadTuple payload, IntermediateFieldPathValue sourceFile) { - FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(payload.SourceFile); + var versionInfo = FileVersionInfo.GetVersionInfo(sourceFile.Path); if (null != versionInfo) { // Use the fixed version info block for the file since the resource text may not be a dotted quad. - Version version = new Version(versionInfo.ProductMajorPart, versionInfo.ProductMinorPart, versionInfo.ProductBuildPart, versionInfo.ProductPrivatePart); + var version = new Version(versionInfo.ProductMajorPart, versionInfo.ProductMinorPart, versionInfo.ProductBuildPart, versionInfo.ProductPrivatePart); if (ProcessPayloadsCommand.EmptyVersion != version) { @@ -154,6 +170,5 @@ namespace WixToolset.Core.Burn.Bundles payload.DisplayName = versionInfo.ProductName; } } -#endif } } diff --git a/src/WixToolset.Core.Burn/Bundles/VerifyPayloadsWithCatalogCommand.cs b/src/WixToolset.Core.Burn/Bundles/VerifyPayloadsWithCatalogCommand.cs index 82682a47..2b17f985 100644 --- a/src/WixToolset.Core.Burn/Bundles/VerifyPayloadsWithCatalogCommand.cs +++ b/src/WixToolset.Core.Burn/Bundles/VerifyPayloadsWithCatalogCommand.cs @@ -9,31 +9,42 @@ namespace WixToolset.Core.Burn.Bundles using System.Runtime.InteropServices; using System.Text; using WixToolset.Data; + using WixToolset.Data.Tuples; + using WixToolset.Extensibility.Services; internal class VerifyPayloadsWithCatalogCommand { -#if TODO - public IEnumerable Catalogs { private get; set; } + public VerifyPayloadsWithCatalogCommand(IMessaging messaging, IEnumerable catalogs, IEnumerable payloads) + { + this.Messaging = messaging; + this.Catalogs = catalogs; + this.Payloads = payloads; + } + + private IMessaging Messaging { get; } + + private IEnumerable Catalogs { get; } - public IEnumerable Payloads { private get; set; } + private IEnumerable Payloads { get; } public void Execute() { - List catalogIdsWithPaths = this.Catalogs + var catalogIdsWithPaths = this.Catalogs .Join(this.Payloads, - catalog => catalog.Payload, - payload => payload.Id, - (catalog, payload) => new CatalogIdWithPath() { Id = catalog.Id, FullPath = Path.GetFullPath(payload.SourceFile) }) + catalog => catalog.PayloadRef, + payload => payload.Id.Id, + (catalog, payload) => new CatalogIdWithPath() { Id = catalog.Id.Id, FullPath = Path.GetFullPath(payload.SourceFile.Path) }) .ToList(); - foreach (WixBundlePayloadRow payloadInfo in this.Payloads) + foreach (var payloadInfo in this.Payloads) { // Payloads that are not embedded should be verfied. if (String.IsNullOrEmpty(payloadInfo.EmbeddedId)) { - bool validated = false; + var sourceFile = payloadInfo.SourceFile.Path; + var validated = false; - foreach (CatalogIdWithPath catalog in catalogIdsWithPaths) + foreach (var catalog in catalogIdsWithPaths) { if (!validated) { @@ -41,11 +52,10 @@ namespace WixToolset.Core.Burn.Bundles uint cryptHashSize = 20; byte[] cryptHashBytes = new byte[cryptHashSize]; int error; - IntPtr fileHandle = IntPtr.Zero; - using (FileStream payloadStream = File.OpenRead(payloadInfo.FullFileName)) + using (var payloadStream = File.OpenRead(sourceFile)) { // Get the file handle - fileHandle = payloadStream.SafeFileHandle.DangerousGetHandle(); + var fileHandle = payloadStream.SafeFileHandle.DangerousGetHandle(); // 20 bytes is usually the hash size. Future hashes may be bigger if (!VerifyInterop.CryptCATAdminCalcHashFromFileHandle(fileHandle, ref cryptHashSize, cryptHashBytes, 0)) @@ -64,7 +74,7 @@ namespace WixToolset.Core.Burn.Bundles if (0 != error) { - Messaging.Instance.OnMessage(WixErrors.CatalogFileHashFailed(payloadInfo.FullFileName, error)); + this.Messaging.Write(ErrorMessages.CatalogFileHashFailed(sourceFile, error)); } } } @@ -79,15 +89,15 @@ namespace WixToolset.Core.Burn.Bundles catalogData.pbCalculatedFileHash = Marshal.AllocCoTaskMem((int)cryptHashSize); Marshal.Copy(cryptHashBytes, 0, catalogData.pbCalculatedFileHash, (int)cryptHashSize); - StringBuilder hashString = new StringBuilder(); - foreach (byte hashByte in cryptHashBytes) + var hashString = new StringBuilder(); + foreach (var hashByte in cryptHashBytes) { hashString.Append(hashByte.ToString("X2")); } catalogData.pcwszMemberTag = hashString.ToString(); // The file names need to be lower case for older OSes - catalogData.pcwszMemberFilePath = payloadInfo.FullFileName.ToLowerInvariant(); + catalogData.pcwszMemberFilePath = sourceFile.ToLowerInvariant(); catalogData.pcwszCatalogFilePath = catalog.FullPath.ToLowerInvariant(); // Create WINTRUST_DATA structure @@ -108,7 +118,7 @@ namespace WixToolset.Core.Burn.Bundles long verifyResult = VerifyInterop.WinVerifyTrust(noWindow, ref verifyGuid, ref trustData); if (0 == verifyResult) { - payloadInfo.Catalog = catalog.Id; + payloadInfo.CatalogRef = catalog.Id; validated = true; break; } @@ -132,7 +142,7 @@ namespace WixToolset.Core.Burn.Bundles // Error message if the file was not validated by one of the catalogs if (!validated) { - Messaging.Instance.OnMessage(WixErrors.CatalogVerificationFailed(payloadInfo.FullFileName)); + this.Messaging.Write(ErrorMessages.CatalogVerificationFailed(sourceFile)); } } } @@ -144,6 +154,5 @@ namespace WixToolset.Core.Burn.Bundles public string FullPath { get; set; } } -#endif } } diff --git a/src/WixToolset.Core.Burn/BurnBackendFactory.cs b/src/WixToolset.Core.Burn/BurnBackendFactory.cs index 4b2e833f..03013a08 100644 --- a/src/WixToolset.Core.Burn/BurnBackendFactory.cs +++ b/src/WixToolset.Core.Burn/BurnBackendFactory.cs @@ -1,11 +1,10 @@ -// 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. +// 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. namespace WixToolset.Core.Burn { using System; using System.IO; using WixToolset.Extensibility; - using WixToolset.Extensibility.Data; internal class BurnBackendFactory : IBackendFactory { diff --git a/src/WixToolset.Core.Burn/VerifyInterop.cs b/src/WixToolset.Core.Burn/VerifyInterop.cs index 81fbec65..f021f1d0 100644 --- a/src/WixToolset.Core.Burn/VerifyInterop.cs +++ b/src/WixToolset.Core.Burn/VerifyInterop.cs @@ -3,8 +3,6 @@ namespace WixToolset { using System; - using System.Collections; - using System.Runtime.CompilerServices; using System.Runtime.InteropServices; internal class VerifyInterop diff --git a/src/WixToolset.Core.Burn/WixToolset.Core.Burn.csproj b/src/WixToolset.Core.Burn/WixToolset.Core.Burn.csproj index db413cbb..d9493b8a 100644 --- a/src/WixToolset.Core.Burn/WixToolset.Core.Burn.csproj +++ b/src/WixToolset.Core.Burn/WixToolset.Core.Burn.csproj @@ -15,6 +15,7 @@ + diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs index 830880ee..53451752 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs @@ -30,6 +30,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind this.BackendHelper = context.ServiceProvider.GetService(); + this.PathResolver = this.ServiceProvider.GetService(); + this.TableDefinitions = WindowsInstallerStandardInternal.GetTableDefinitions(); this.CabbingThreadCount = context.CabbingThreadCount; @@ -54,6 +56,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind private IBackendHelper BackendHelper { get; } + private IPathResolver PathResolver { get; } + private int Codepage { get; } private int CabbingThreadCount { get; } @@ -241,7 +245,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind // Set generated component guids. { - var command = new CalculateComponentGuids(this.Messaging, this.BackendHelper, section); + var command = new CalculateComponentGuids(this.Messaging, this.BackendHelper, this.PathResolver, section); command.Execute(); } @@ -501,7 +505,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind // Process uncompressed files. if (!this.Messaging.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any()) { - var command = new ProcessUncompressedFilesCommand(section, this.BackendHelper); + var command = new ProcessUncompressedFilesCommand(section, this.BackendHelper, this.PathResolver); command.Compressed = compressed; command.FileFacades = uncompressedFiles; command.LayoutDirectory = layoutDirectory; diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CalculateComponentGuids.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CalculateComponentGuids.cs index 835d9b8d..8135ae2e 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CalculateComponentGuids.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CalculateComponentGuids.cs @@ -6,9 +6,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind using System.Collections.Generic; using System.IO; using System.Linq; - using WixToolset.Core.Native; using WixToolset.Data; using WixToolset.Data.Tuples; + using WixToolset.Extensibility.Data; using WixToolset.Extensibility.Services; /// @@ -16,10 +16,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind /// internal class CalculateComponentGuids { - internal CalculateComponentGuids(IMessaging messaging, IBackendHelper helper, IntermediateSection section) + internal CalculateComponentGuids(IMessaging messaging, IBackendHelper helper, IPathResolver pathResolver, IntermediateSection section) { this.Messaging = messaging; this.BackendHelper = helper; + this.PathResolver = pathResolver; this.Section = section; } @@ -27,12 +28,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind private IBackendHelper BackendHelper { get; } + private IPathResolver PathResolver { get; } + private IntermediateSection Section { get; } public void Execute() { Dictionary registryKeyRows = null; - Dictionary targetPathsByDirectoryId = null; + Dictionary targetPathsByDirectoryId = null; Dictionary componentIdGenSeeds = null; Dictionary> filesByComponentId = null; @@ -73,7 +76,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind { var directories = this.Section.Tuples.OfType().ToList(); - targetPathsByDirectoryId = new Dictionary(directories.Count); + targetPathsByDirectoryId = new Dictionary(directories.Count); // Get the target paths for all directories. foreach (var directory in directories) @@ -86,7 +89,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind continue; } - targetPathsByDirectoryId.Add(directory.Id.Id, new ResolvedDirectory(directory.ParentDirectoryRef, directory.Name)); + var resolvedDirectory = this.BackendHelper.CreateResolvedDirectory(directory.ParentDirectoryRef, directory.Name); + targetPathsByDirectoryId.Add(directory.Id.Id, resolvedDirectory); } } @@ -131,7 +135,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind if (fileRow.Id.Id == componentTuple.KeyPath) { // calculate the key file's canonical target path - string directoryPath = PathResolver.GetDirectoryPath(targetPathsByDirectoryId, componentIdGenSeeds, componentTuple.DirectoryRef, true); + string directoryPath = this.PathResolver.GetDirectoryPath(targetPathsByDirectoryId, componentIdGenSeeds, componentTuple.DirectoryRef, true); string fileName = Common.GetName(fileRow.Name, false, true).ToLowerInvariant(); path = Path.Combine(directoryPath, fileName); diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs index 95438f96..a9b0f5f5 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs @@ -8,7 +8,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind using System.IO; using System.Linq; using System.Runtime.InteropServices; - using System.Threading; using WixToolset.Core.Bind; using WixToolset.Data; using WixToolset.Data.Tuples; diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs index f76cd227..cd3a67fa 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs @@ -105,6 +105,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind this.AddMsiEmbeddedUITuple((MsiEmbeddedUITuple)tuple, output); break; + case TupleDefinitionType.MsiFileHash: + this.AddMsiFileHashTuple((MsiFileHashTuple)tuple, output); + break; + case TupleDefinitionType.MsiServiceConfig: this.AddMsiServiceConfigTuple((MsiServiceConfigTuple)tuple, output); break; @@ -500,6 +504,18 @@ namespace WixToolset.Core.WindowsInstaller.Bind row[4] = tuple.Source; } + private void AddMsiFileHashTuple(MsiFileHashTuple tuple, Output output) + { + var table = output.EnsureTable(this.TableDefinitions["MsiFileHash"]); + var row = table.CreateRow(tuple.SourceLineNumbers); + row[0] = tuple.Id.Id; + row[1] = tuple.Options; + row[2] = tuple.HashPart1; + row[3] = tuple.HashPart2; + row[4] = tuple.HashPart3; + row[5] = tuple.HashPart4; + } + private void AddMsiServiceConfigTuple(MsiServiceConfigTuple tuple, Output output) { var events = tuple.OnInstall ? WindowsInstallerConstants.MsidbServiceConfigEventInstall : 0; diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/PathResolver.cs b/src/WixToolset.Core.WindowsInstaller/Bind/PathResolver.cs deleted file mode 100644 index 6dc18271..00000000 --- a/src/WixToolset.Core.WindowsInstaller/Bind/PathResolver.cs +++ /dev/null @@ -1,106 +0,0 @@ -// 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. - -namespace WixToolset.Core.WindowsInstaller.Bind -{ - using System; - using System.Collections.Generic; - using System.IO; - using WixToolset.Data; - using WixToolset.Data.WindowsInstaller; - - internal static class PathResolver - { - /// - /// Get the source path of a directory. - /// - /// All cached directories. - /// Hash table of Component GUID generation seeds indexed by directory id. - /// Directory identifier. - /// Canonicalize the path for standard directories. - /// Source path of a directory. - public static string GetDirectoryPath(Dictionary directories, Dictionary componentIdGenSeeds, string directory, bool canonicalize) - { - if (!directories.TryGetValue(directory, out var resolvedDirectory)) - { - throw new WixException(ErrorMessages.ExpectedDirectory(directory)); - } - - if (null == resolvedDirectory.Path) - { - if (null != componentIdGenSeeds && componentIdGenSeeds.ContainsKey(directory)) - { - resolvedDirectory.Path = componentIdGenSeeds[directory]; - } - else if (canonicalize && WindowsInstallerStandard.IsStandardDirectory(directory)) - { - // when canonicalization is on, standard directories are treated equally - resolvedDirectory.Path = directory; - } - else - { - string name = resolvedDirectory.Name; - - if (canonicalize) - { - name = name?.ToLowerInvariant(); - } - - if (String.IsNullOrEmpty(resolvedDirectory.DirectoryParent)) - { - resolvedDirectory.Path = name; - } - else - { - string parentPath = GetDirectoryPath(directories, componentIdGenSeeds, resolvedDirectory.DirectoryParent, canonicalize); - - if (null != resolvedDirectory.Name) - { - resolvedDirectory.Path = Path.Combine(parentPath, name); - } - else - { - resolvedDirectory.Path = parentPath; - } - } - } - } - - return resolvedDirectory.Path; - } - - /// - /// Gets the source path of a file. - /// - /// All cached directories in . - /// Parent directory identifier. - /// File name (in long|source format). - /// Specifies the package is compressed. - /// Specifies the package uses long file names. - /// Source path of file relative to package directory. - public static string GetFileSourcePath(Dictionary directories, string directoryId, string fileName, bool compressed, bool useLongName) - { - string fileSourcePath = Common.GetName(fileName, true, useLongName); - - if (compressed) - { - // Use just the file name of the file since all uncompressed files must appear - // in the root of the image in a compressed package. - } - else - { - // Get the relative path of where we want the file to be layed out as specified - // in the Directory table. - string directoryPath = PathResolver.GetDirectoryPath(directories, null, directoryId, false); - fileSourcePath = Path.Combine(directoryPath, fileSourcePath); - } - - // Strip off "SourceDir" if it's still on there. - if (fileSourcePath.StartsWith("SourceDir\\", StringComparison.Ordinal)) - { - fileSourcePath = fileSourcePath.Substring(10); - } - - return fileSourcePath; - } - } -} diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs index 61e82f68..64fb3e4d 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs @@ -18,16 +18,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind /// internal class ProcessUncompressedFilesCommand { - public ProcessUncompressedFilesCommand(IntermediateSection section, IBackendHelper backendHelper) + public ProcessUncompressedFilesCommand(IntermediateSection section, IBackendHelper backendHelper, IPathResolver pathResolver) { this.Section = section; this.BackendHelper = backendHelper; + this.PathResolver = pathResolver; } private IntermediateSection Section { get; } public IBackendHelper BackendHelper { get; } + public IPathResolver PathResolver { get; } + public string DatabasePath { private get; set; } public IEnumerable FileFacades { private get; set; } @@ -50,7 +53,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind var trackedFiles = new List(); - var directories = new Dictionary(); + var directories = new Dictionary(); var mediaRows = this.Section.Tuples.OfType().ToDictionary(t => t.DiskId); @@ -69,7 +72,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind string sourceName = Common.GetName(directoryRecord.GetString(3), true, this.LongNamesInImage); - directories.Add(directoryRecord.GetString(1), new ResolvedDirectory(directoryRecord.GetString(2), sourceName)); + var resolvedDirectory = this.BackendHelper.CreateResolvedDirectory(directoryRecord.GetString(2), sourceName); + + directories.Add(directoryRecord.GetString(1), resolvedDirectory); } } } @@ -99,7 +104,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind throw new WixException(ErrorMessages.FileIdentifierNotFound(facade.File.SourceLineNumbers, facade.File.Id.Id)); } - relativeFileLayoutPath = PathResolver.GetFileSourcePath(directories, fileRecord[1], fileRecord[2], this.Compressed, this.LongNamesInImage); + relativeFileLayoutPath = this.PathResolver.GetFileSourcePath(directories, fileRecord[1], fileRecord[2], this.Compressed, this.LongNamesInImage); } // finally put together the base media layout path and the relative file layout path diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/ResolvedDirectory.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ResolvedDirectory.cs deleted file mode 100644 index e06321cf..00000000 --- a/src/WixToolset.Core.WindowsInstaller/Bind/ResolvedDirectory.cs +++ /dev/null @@ -1,31 +0,0 @@ -// 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. - -namespace WixToolset.Core.WindowsInstaller.Bind -{ - /// - /// Structure used for resolved directory information. - /// - internal struct ResolvedDirectory - { - /// - /// Constructor for ResolvedDirectory. - /// - /// Parent directory. - /// The directory name. - public ResolvedDirectory(string directoryParent, string name) - { - this.DirectoryParent = directoryParent; - this.Name = name; - this.Path = null; - } - - /// The directory parent. - public string DirectoryParent { get; set; } - - /// The name of this directory. - public string Name { get; set; } - - /// The path of this directory. - public string Path { get; set; } - } -} diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs index 397092c4..1f2a22d9 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs @@ -162,7 +162,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind this.Section.Tuples.Add(facade.Hash); } - facade.Hash.FileRef = facade.File.Id.Id; facade.Hash.Options = 0; facade.Hash.HashPart1 = hash[0]; facade.Hash.HashPart2 = hash[1]; diff --git a/src/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs b/src/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs index c6e21973..644e5c63 100644 --- a/src/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs +++ b/src/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs @@ -30,10 +30,10 @@ namespace WixToolset.Core.Bind { // If the uri to the file that contains the embedded file does not already have embedded files // being extracted, create the dictionary to track that. - if (!filesWithEmbeddedFiles.TryGetValue(uri, out var extracts)) + if (!this.filesWithEmbeddedFiles.TryGetValue(uri, out var extracts)) { extracts = new SortedList(); - filesWithEmbeddedFiles.Add(uri, extracts); + this.filesWithEmbeddedFiles.Add(uri, extracts); } // If the embedded file is not already tracked in the dictionary of extracts, add it. @@ -52,7 +52,7 @@ namespace WixToolset.Core.Bind public IEnumerable GetExpectedEmbeddedFiles() { - foreach (var uriWithExtracts in filesWithEmbeddedFiles) + foreach (var uriWithExtracts in this.filesWithEmbeddedFiles) { foreach (var extracts in uriWithExtracts.Value) { @@ -68,7 +68,7 @@ namespace WixToolset.Core.Bind public IEnumerable GetExtractFilesForUri(Uri uri) { - if (!filesWithEmbeddedFiles.TryGetValue(uri, out var extracts)) + if (!this.filesWithEmbeddedFiles.TryGetValue(uri, out var extracts)) { extracts = new SortedList(); } diff --git a/src/WixToolset.Core/Bind/FileResolver.cs b/src/WixToolset.Core/Bind/FileResolver.cs index a67d784d..b1676fad 100644 --- a/src/WixToolset.Core/Bind/FileResolver.cs +++ b/src/WixToolset.Core/Bind/FileResolver.cs @@ -70,11 +70,17 @@ namespace WixToolset.Core.Bind /// Optional type of source file being resolved. /// Optional source line of source file being resolved. /// The binding stage used to determine what collection of bind paths will be used + /// Optional collection of paths already checked. /// Should return a valid path for the stream to be imported. - public string ResolveFile(string source, IntermediateTupleDefinition tupleDefinition, SourceLineNumber sourceLineNumbers, BindStage bindStage) + public string ResolveFile(string source, IntermediateTupleDefinition tupleDefinition, SourceLineNumber sourceLineNumbers, BindStage bindStage, IEnumerable alreadyCheckedPaths = null) { var checkedPaths = new List(); + if (alreadyCheckedPaths != null) + { + checkedPaths.AddRange(alreadyCheckedPaths); + } + foreach (var extension in this.ResolverExtensions) { var resolved = extension.ResolveFile(source, tupleDefinition, sourceLineNumbers, bindStage); diff --git a/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs b/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs index bec03907..22710aca 100644 --- a/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs +++ b/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs @@ -113,7 +113,7 @@ namespace WixToolset.Core.Bind } } - public static string ResolveDelayedVariables(SourceLineNumber sourceLineNumbers, string value, IDictionary resolutionData) + private static string ResolveDelayedVariables(SourceLineNumber sourceLineNumbers, string value, IDictionary resolutionData) { var matches = Common.WixVariableRegex.Matches(value); diff --git a/src/WixToolset.Core/BindContext.cs b/src/WixToolset.Core/BindContext.cs index 413be301..7882b22d 100644 --- a/src/WixToolset.Core/BindContext.cs +++ b/src/WixToolset.Core/BindContext.cs @@ -1,4 +1,4 @@ -// 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. +// 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. namespace WixToolset.Core { @@ -19,6 +19,8 @@ namespace WixToolset.Core public IEnumerable BindPaths { get; set; } + public string BurnStubPath { get; set; } + public int CabbingThreadCount { get; set; } public string CabCachePath { get; set; } diff --git a/src/WixToolset.Core/CommandLine/BuildCommand.cs b/src/WixToolset.Core/CommandLine/BuildCommand.cs index bfee2478..972258fe 100644 --- a/src/WixToolset.Core/CommandLine/BuildCommand.cs +++ b/src/WixToolset.Core/CommandLine/BuildCommand.cs @@ -122,7 +122,7 @@ namespace WixToolset.Core.CommandLine } else { - this.BindPhase(wixipl, wxls, filterCultures, this.commandLine.CabCachePath, this.commandLine.BindPaths); + this.BindPhase(wixipl, wxls, filterCultures, this.commandLine.CabCachePath, this.commandLine.BindPaths, this.commandLine.BurnStubPath); } } } @@ -257,7 +257,7 @@ namespace WixToolset.Core.CommandLine return linker.Link(context); } - private void BindPhase(Intermediate output, IEnumerable localizations, IEnumerable filterCultures, string cabCachePath, IEnumerable bindPaths) + private void BindPhase(Intermediate output, IEnumerable localizations, IEnumerable filterCultures, string cabCachePath, IEnumerable bindPaths, string burnStubPath) { var intermediateFolder = this.IntermediateFolder; if (String.IsNullOrEmpty(intermediateFolder)) @@ -290,6 +290,7 @@ namespace WixToolset.Core.CommandLine { var context = this.ServiceProvider.GetService(); //context.CabbingThreadCount = this.CabbingThreadCount; + context.BurnStubPath = burnStubPath; context.CabCachePath = cabCachePath; context.Codepage = resolveResult.Codepage; //context.DefaultCompressionLevel = this.DefaultCompressionLevel; @@ -399,6 +400,8 @@ namespace WixToolset.Core.CommandLine public List BindPaths { get; } = new List(); + public string BurnStubPath { get; private set; } + public string CabCachePath { get; private set; } public List Cultures { get; } = new List(); @@ -480,6 +483,10 @@ namespace WixToolset.Core.CommandLine } break; } + case "burnstub": + this.BurnStubPath = parser.GetNextArgumentOrError(arg); + return true; + case "cc": this.CabCachePath = parser.GetNextArgumentOrError(arg); return true; diff --git a/src/WixToolset.Core/Compiler.cs b/src/WixToolset.Core/Compiler.cs index 0dade46d..3ee87872 100644 --- a/src/WixToolset.Core/Compiler.cs +++ b/src/WixToolset.Core/Compiler.cs @@ -818,7 +818,7 @@ namespace WixToolset.Core { this.Core.AddTuple(new IconTuple(sourceLineNumbers, id) { - Data = sourceFile + Data = new IntermediateFieldPathValue { Path = sourceFile } }); } diff --git a/src/WixToolset.Core/Compiler_Bundle.cs b/src/WixToolset.Core/Compiler_Bundle.cs index 0ed49fbc..3be7d0c5 100644 --- a/src/WixToolset.Core/Compiler_Bundle.cs +++ b/src/WixToolset.Core/Compiler_Bundle.cs @@ -9,6 +9,7 @@ namespace WixToolset.Core using System.IO; using System.Xml.Linq; using WixToolset.Data; + using WixToolset.Data.Burn; using WixToolset.Data.Tuples; using WixToolset.Extensibility; @@ -17,15 +18,9 @@ namespace WixToolset.Core /// internal partial class Compiler : ICompiler { - public static readonly Identifier BurnUXContainerId = new Identifier(AccessModifier.Private, "WixUXContainer"); - public static readonly Identifier BurnDefaultAttachedContainerId = new Identifier(AccessModifier.Private, "WixAttachedContainer"); - public static readonly Identifier BundleLayoutOnlyPayloads = new Identifier(AccessModifier.Private, "BundleLayoutOnlyPayloads"); - - // The following constants must stay in sync with src\burn\engine\core.h - private const string BURN_BUNDLE_NAME = "WixBundleName"; - private const string BURN_BUNDLE_ORIGINAL_SOURCE = "WixBundleOriginalSource"; - private const string BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = "WixBundleOriginalSourceFolder"; - private const string BURN_BUNDLE_LAST_USED_SOURCE = "WixBundleLastUsedSource"; + private static readonly Identifier BurnUXContainerId = new Identifier(AccessModifier.Private, BurnConstants.BurnUXContainerName); + private static readonly Identifier BurnDefaultAttachedContainerId = new Identifier(AccessModifier.Private, BurnConstants.BurnDefaultAttachedContainerName); + private static readonly Identifier BundleLayoutOnlyPayloads = new Identifier(AccessModifier.Private, BurnConstants.BundleLayoutOnlyPayloadsName); /// /// Parses an ApprovedExeForElevation element. @@ -78,11 +73,11 @@ namespace WixToolset.Core this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); } - var attributes = BundleApprovedExeForElevationAttributes.None; + var attributes = WixApprovedExeForElevationAttributes.None; if (win64 == YesNoType.Yes) { - attributes |= BundleApprovedExeForElevationAttributes.Win64; + attributes |= WixApprovedExeForElevationAttributes.Win64; } this.Core.ParseForExtensionElements(node); @@ -92,7 +87,7 @@ namespace WixToolset.Core var tuple = new WixApprovedExeForElevationTuple(sourceLineNumbers, id) { Key = key, - Value = valueName, + ValueName = valueName, Attributes = attributes }; @@ -110,8 +105,7 @@ namespace WixToolset.Core string copyright = null; string aboutUrl = null; var compressed = YesNoDefaultType.Default; - var disableModify = -1; - var disableRemove = YesNoType.NotSet; + WixBundleAttributes attributes = 0; string helpTelephone = null; string helpUrl = null; string manufacturer = null; @@ -152,13 +146,12 @@ namespace WixToolset.Core switch (value) { case "button": - disableModify = 2; + attributes |= WixBundleAttributes.SingleChangeUninstallButton; break; case "yes": - disableModify = 1; + attributes |= WixBundleAttributes.DisableModify; break; case "no": - disableModify = 0; break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, value, "button", "yes", "no")); @@ -166,10 +159,10 @@ namespace WixToolset.Core } break; case "DisableRemove": - disableRemove = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "DisableRepair": - this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName)); + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= WixBundleAttributes.DisableRemove; + } break; case "HelpTelephone": helpTelephone = this.Core.GetAttributeValue(sourceLineNumbers, attrib); @@ -239,13 +232,13 @@ namespace WixToolset.Core if (String.IsNullOrEmpty(name)) { - logVariablePrefixAndExtension = String.Concat("WixBundleLog:Setup.log"); + logVariablePrefixAndExtension = String.Concat("WixBundleLog:Setup:.log"); } else { // Ensure only allowable path characters are in "name" (and change spaces to underscores). fileSystemSafeBundleName = CompilerCore.MakeValidLongFileName(name.Replace(' ', '_'), "_"); - logVariablePrefixAndExtension = String.Concat("WixBundleLog:", fileSystemSafeBundleName, ".log"); + logVariablePrefixAndExtension = String.Concat("WixBundleLog:", fileSystemSafeBundleName, ":.log"); } this.activeName = String.IsNullOrEmpty(name) ? Common.GenerateGuid() : name; @@ -351,6 +344,37 @@ namespace WixToolset.Core if (!this.Core.EncounteredError) { + var tuple = new WixBundleTuple(sourceLineNumbers) + { + UpgradeCode = upgradeCode, + Version = version, + Copyright = copyright, + Name = name, + Manufacturer = manufacturer, + Attributes = attributes, + AboutUrl = aboutUrl, + HelpUrl = helpUrl, + HelpTelephone = helpTelephone, + UpdateUrl = updateUrl, + Compressed = YesNoDefaultType.Yes == compressed ? true : YesNoDefaultType.No == compressed ? (bool?)false : null, + IconSourceFile = iconSourceFile, + SplashScreenSourceFile = splashScreenSourceFile, + Condition = condition, + Tag = tag, + Platform = this.CurrentPlatform, + ParentName = parentName, + }; + + if (!String.IsNullOrEmpty(logVariablePrefixAndExtension)) + { + var split = logVariablePrefixAndExtension.Split(':'); + tuple.LogPathVariable = split[0]; + tuple.LogPrefix = split[1]; + tuple.LogExtension = split[2]; + } + + this.Core.AddTuple(tuple);; + if (null != upgradeCode) { this.Core.AddTuple(new WixRelatedBundleTuple(sourceLineNumbers) @@ -360,64 +384,32 @@ namespace WixToolset.Core }); } - this.Core.AddTuple(new WixBundleContainerTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, Compiler.BurnDefaultAttachedContainerId)) + this.Core.AddTuple(new WixBundleContainerTuple(sourceLineNumbers, Compiler.BurnDefaultAttachedContainerId) { Name = "bundle-attached.cab", Type = ContainerType.Attached }); - var bundleTuple = this.Core.CreateTuple(sourceLineNumbers, TupleDefinitionType.WixBundle); - bundleTuple.Set(0, version); - bundleTuple.Set(1, copyright); - bundleTuple.Set(2, name); - bundleTuple.Set(3, aboutUrl); - if (-1 != disableModify) - { - bundleTuple.Set(4, disableModify); - } - if (YesNoType.NotSet != disableRemove) - { - bundleTuple.Set(5, (YesNoType.Yes == disableRemove) ? 1 : 0); - } - // row.Set(6] - (deprecated) "disable repair" - bundleTuple.Set(7, helpTelephone); - bundleTuple.Set(8, helpUrl); - bundleTuple.Set(9, manufacturer); - bundleTuple.Set(10, updateUrl); - if (YesNoDefaultType.Default != compressed) - { - bundleTuple.Set(11, (YesNoDefaultType.Yes == compressed) ? 1 : 0); - } - - bundleTuple.Set(12, logVariablePrefixAndExtension); - bundleTuple.Set(13, iconSourceFile); - bundleTuple.Set(14, splashScreenSourceFile); - bundleTuple.Set(15, condition); - bundleTuple.Set(16, tag); - bundleTuple.Set(17, this.CurrentPlatform.ToString()); - bundleTuple.Set(18, parentName); - bundleTuple.Set(19, upgradeCode); - // Ensure that the bundle stores the well-known persisted values. - this.Core.AddTuple(new WixBundleVariableTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, Compiler.BURN_BUNDLE_NAME)) + this.Core.AddTuple(new WixBundleVariableTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, BurnConstants.BURN_BUNDLE_NAME)) { Hidden = false, Persisted = true }); - this.Core.AddTuple(new WixBundleVariableTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, Compiler.BURN_BUNDLE_ORIGINAL_SOURCE)) + this.Core.AddTuple(new WixBundleVariableTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, BurnConstants.BURN_BUNDLE_ORIGINAL_SOURCE)) { Hidden = false, Persisted = true }); - this.Core.AddTuple(new WixBundleVariableTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, Compiler.BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER)) + this.Core.AddTuple(new WixBundleVariableTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, BurnConstants.BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER)) { Hidden = false, Persisted = true }); - this.Core.AddTuple(new WixBundleVariableTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, Compiler.BURN_BUNDLE_LAST_USED_SOURCE)) + this.Core.AddTuple(new WixBundleVariableTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, BurnConstants.BURN_BUNDLE_LAST_USED_SOURCE)) { Hidden = false, Persisted = true @@ -473,7 +465,7 @@ namespace WixToolset.Core this.Core.ParseForExtensionElements(node); - return YesNoType.Yes == disableLog ? null : String.Concat(variable, ":", logPrefix, logExtension); + return YesNoType.Yes == disableLog ? null : String.Join(":", variable, logPrefix, logExtension); } /// @@ -1126,9 +1118,9 @@ namespace WixToolset.Core tuple = new WixBundlePayloadTuple(sourceLineNumbers, id) { Name = String.IsNullOrEmpty(name) ? Path.GetFileName(sourceFile) : name, - SourceFile = sourceFile, + SourceFile = new IntermediateFieldPathValue { Path = sourceFile }, DownloadUrl = downloadUrl, - Compressed = compressed, + Compressed = (compressed == YesNoDefaultType.Yes) ? true : (compressed == YesNoDefaultType.No) ? (bool?)false : null, UnresolvedSourceFile = sourceFile, // duplicate of sourceFile but in a string column so it won't get resolved to a full path during binding. DisplayName = displayName, Description = description, @@ -1665,14 +1657,12 @@ namespace WixToolset.Core var vital = YesNoType.Yes; string installCommand = null; string repairCommand = null; - var repairable = YesNoType.NotSet; string uninstallCommand = null; var perMachine = YesNoDefaultType.NotSet; string detectCondition = null; string protocol = null; var installSize = CompilerConstants.IntegerNotSet; string msuKB = null; - var suppressLooseFilePayloadGeneration = YesNoType.NotSet; var enableSignatureVerification = YesNoType.No; var compressed = YesNoDefaultType.Default; var displayInternalUI = YesNoType.NotSet; @@ -1779,7 +1769,6 @@ namespace WixToolset.Core break; case "RepairCommand": repairCommand = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); - repairable = YesNoType.Yes; allowed = (packageType == WixBundlePackageType.Exe); break; case "UninstallCommand": @@ -1808,11 +1797,6 @@ namespace WixToolset.Core case "Compressed": compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); break; - case "SuppressLooseFilePayloadGeneration": - this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName)); - suppressLooseFilePayloadGeneration = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - allowed = (packageType == WixBundlePackageType.Msi); - break; case "EnableSignatureVerification": enableSignatureVerification = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; @@ -2076,7 +2060,7 @@ namespace WixToolset.Core case WixBundlePackageType.Exe: this.Core.AddTuple(new WixBundleExePackageTuple(sourceLineNumbers, id) { - Attributes = (YesNoType.Yes == repairable) ? WixBundleExePackageAttributes.Repairable : 0, + Attributes = WixBundleExePackageAttributes.None, DetectCondition = detectCondition, InstallCommand = installCommand, RepairCommand = repairCommand, @@ -2090,7 +2074,6 @@ namespace WixToolset.Core msiAttributes |= (YesNoType.Yes == displayInternalUI) ? WixBundleMsiPackageAttributes.DisplayInternalUI : 0; msiAttributes |= (YesNoType.Yes == enableFeatureSelection) ? WixBundleMsiPackageAttributes.EnableFeatureSelection : 0; msiAttributes |= (YesNoType.Yes == forcePerMachine) ? WixBundleMsiPackageAttributes.ForcePerMachine : 0; - msiAttributes |= (YesNoType.Yes == suppressLooseFilePayloadGeneration) ? WixBundleMsiPackageAttributes.SuppressLooseFilePayloadGeneration : 0; this.Core.AddTuple(new WixBundleMsiPackageTuple(sourceLineNumbers, id) { @@ -2458,9 +2441,9 @@ namespace WixToolset.Core if (!this.Core.EncounteredError) { - var tuple = new WixBundleMsiPropertyTuple(sourceLineNumbers) + var tuple = new WixBundleMsiPropertyTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, packageId, name)) { - WixBundlePackageRef = packageId, + PackageRef = packageId, Name = name, Value = value }; @@ -2514,10 +2497,10 @@ namespace WixToolset.Core if (!this.Core.EncounteredError) { - this.Core.AddTuple(new WixBundleSlipstreamMspTuple(sourceLineNumbers) + this.Core.AddTuple(new WixBundleSlipstreamMspTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, packageId, id)) { - WixBundlePackageRef = packageId, - MspWixBundlePackageRef = id + TargetPackageRef = packageId, + MspPackageRef = id }); } } diff --git a/src/WixToolset.Core/ExtensibilityServices/BackendHelper.cs b/src/WixToolset.Core/ExtensibilityServices/BackendHelper.cs index 6cc91487..0bdecf7a 100644 --- a/src/WixToolset.Core/ExtensibilityServices/BackendHelper.cs +++ b/src/WixToolset.Core/ExtensibilityServices/BackendHelper.cs @@ -1,4 +1,4 @@ -// 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. +// 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. namespace WixToolset.Core.ExtensibilityServices { @@ -40,6 +40,15 @@ namespace WixToolset.Core.ExtensibilityServices return Uuid.NewUuid(namespaceGuid, value).ToString("B").ToUpperInvariant(); } + public IResolvedDirectory CreateResolvedDirectory(string directoryParent, string name) + { + return new ResolvedDirectory + { + DirectoryParent = directoryParent, + Name = name + }; + } + public ITrackedFile TrackFile(string path, TrackedFileType type, SourceLineNumber sourceLineNumbers = null) { return new TrackedFile(path, type, sourceLineNumbers); diff --git a/src/WixToolset.Core/ExtensibilityServices/PathResolver.cs b/src/WixToolset.Core/ExtensibilityServices/PathResolver.cs new file mode 100644 index 00000000..15cd4fc9 --- /dev/null +++ b/src/WixToolset.Core/ExtensibilityServices/PathResolver.cs @@ -0,0 +1,91 @@ +// 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. + +namespace WixToolset.Core.ExtensibilityServices +{ + using System; + using System.Collections.Generic; + using System.IO; + using WixToolset.Data; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Extensibility.Data; + using WixToolset.Extensibility.Services; + + internal class PathResolver : IPathResolver + { + public string GetDirectoryPath(Dictionary directories, Dictionary componentIdGenSeeds, string directory, bool canonicalize) + { + if (!directories.TryGetValue(directory, out var resolvedDirectory)) + { + throw new WixException(ErrorMessages.ExpectedDirectory(directory)); + } + + if (null == resolvedDirectory.Path) + { + if (null != componentIdGenSeeds && componentIdGenSeeds.ContainsKey(directory)) + { + resolvedDirectory.Path = componentIdGenSeeds[directory]; + } + else if (canonicalize && WindowsInstallerStandard.IsStandardDirectory(directory)) + { + // when canonicalization is on, standard directories are treated equally + resolvedDirectory.Path = directory; + } + else + { + string name = resolvedDirectory.Name; + + if (canonicalize) + { + name = name?.ToLowerInvariant(); + } + + if (String.IsNullOrEmpty(resolvedDirectory.DirectoryParent)) + { + resolvedDirectory.Path = name; + } + else + { + var parentPath = this.GetDirectoryPath(directories, componentIdGenSeeds, resolvedDirectory.DirectoryParent, canonicalize); + + if (null != resolvedDirectory.Name) + { + resolvedDirectory.Path = Path.Combine(parentPath, name); + } + else + { + resolvedDirectory.Path = parentPath; + } + } + } + } + + return resolvedDirectory.Path; + } + + public string GetFileSourcePath(Dictionary directories, string directoryId, string fileName, bool compressed, bool useLongName) + { + var fileSourcePath = Common.GetName(fileName, true, useLongName); + + if (compressed) + { + // Use just the file name of the file since all uncompressed files must appear + // in the root of the image in a compressed package. + } + else + { + // Get the relative path of where we want the file to be layed out as specified + // in the Directory table. + var directoryPath = this.GetDirectoryPath(directories, null, directoryId, false); + fileSourcePath = Path.Combine(directoryPath, fileSourcePath); + } + + // Strip off "SourceDir" if it's still on there. + if (fileSourcePath.StartsWith("SourceDir\\", StringComparison.Ordinal)) + { + fileSourcePath = fileSourcePath.Substring(10); + } + + return fileSourcePath; + } + } +} diff --git a/src/WixToolset.Core/ExtensibilityServices/ResolvedDirectory.cs b/src/WixToolset.Core/ExtensibilityServices/ResolvedDirectory.cs new file mode 100644 index 00000000..cc8acfdd --- /dev/null +++ b/src/WixToolset.Core/ExtensibilityServices/ResolvedDirectory.cs @@ -0,0 +1,15 @@ +// 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. + +namespace WixToolset.Core.ExtensibilityServices +{ + using WixToolset.Extensibility.Data; + + internal class ResolvedDirectory : IResolvedDirectory + { + public string DirectoryParent { get; set; } + + public string Name { get; set; } + + public string Path { get; set; } + } +} diff --git a/src/WixToolset.Core/ExtensibilityServices/WindowsInstallerBackendHelper.cs b/src/WixToolset.Core/ExtensibilityServices/WindowsInstallerBackendHelper.cs index 6e0ffce6..26982ad6 100644 --- a/src/WixToolset.Core/ExtensibilityServices/WindowsInstallerBackendHelper.cs +++ b/src/WixToolset.Core/ExtensibilityServices/WindowsInstallerBackendHelper.cs @@ -1,8 +1,7 @@ -// 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. +// 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. namespace WixToolset.Core.ExtensibilityServices { - using System; using System.Linq; using WixToolset.Data; using WixToolset.Data.WindowsInstaller; @@ -10,14 +9,9 @@ namespace WixToolset.Core.ExtensibilityServices internal class WindowsInstallerBackendHelper : IWindowsInstallerBackendHelper { - public WindowsInstallerBackendHelper(IServiceProvider serviceProvider) - { - this.ServiceProvider = serviceProvider; - } + public bool TryAddTupleToOutputMatchingTableDefinitions(IntermediateTuple tuple, Output output, TableDefinition[] tableDefinitions) => this.TryAddTupleToOutputMatchingTableDefinitions(tuple, output, tableDefinitions, false); - private IServiceProvider ServiceProvider { get; } - - public bool TryAddTupleToOutputMatchingTableDefinitions(IntermediateTuple tuple, Output output, TableDefinition[] tableDefinitions) + public bool TryAddTupleToOutputMatchingTableDefinitions(IntermediateTuple tuple, Output output, TableDefinition[] tableDefinitions, bool columnZeroIsId) { var tableDefinition = tableDefinitions.FirstOrDefault(t => t.Name == tuple.Definition.Name); @@ -28,6 +22,14 @@ namespace WixToolset.Core.ExtensibilityServices var table = output.EnsureTable(tableDefinition); var row = table.CreateRow(tuple.SourceLineNumbers); + var rowOffset = 0; + + if (columnZeroIsId) + { + row[0] = tuple.Id.Id; + rowOffset = 1; + } + for (var i = 0; i < tuple.Fields.Length; ++i) { if (i < tableDefinition.Columns.Length) @@ -36,13 +38,13 @@ namespace WixToolset.Core.ExtensibilityServices switch (column.Type) { - case ColumnType.Number: - row[i] = tuple.AsNumber(i); - break; + case ColumnType.Number: + row[i + rowOffset] = column.Nullable ? tuple.AsNullableNumber(i) : tuple.AsNumber(i); + break; - default: - row[i] = tuple.AsString(i); - break; + default: + row[i + rowOffset] = tuple.AsString(i); + break; } } } diff --git a/src/WixToolset.Core/Link/WixGroupingOrdering.cs b/src/WixToolset.Core/Link/WixGroupingOrdering.cs index 9080775e..563cd565 100644 --- a/src/WixToolset.Core/Link/WixGroupingOrdering.cs +++ b/src/WixToolset.Core/Link/WixGroupingOrdering.cs @@ -12,19 +12,19 @@ namespace WixToolset.Core.Link using WixToolset.Data; using WixToolset.Data.Tuples; using WixToolset.Extensibility.Services; + using WixToolset.Data.Burn; /// /// Grouping and Ordering class of the WiX toolset. /// internal class WixGroupingOrdering { - private readonly IMessaging messageHandler; + private readonly IMessaging Messaging; private List groupTypes; private List itemTypes; private ItemCollection items; private readonly List rowsUsed; private bool loaded; - private bool encounteredError; /// /// Creates a WixGroupingOrdering object. @@ -36,11 +36,10 @@ namespace WixToolset.Core.Link public WixGroupingOrdering(IntermediateSection entrySections, IMessaging messageHandler) { this.EntrySection = entrySections; - this.messageHandler = messageHandler; + this.Messaging = messageHandler; this.rowsUsed = new List(); this.loaded = false; - this.encounteredError = false; } private IntermediateSection EntrySection { get; } @@ -71,7 +70,7 @@ namespace WixToolset.Core.Link Debug.Assert(this.groupTypes.Contains(parentTypeString)); this.CreateOrderedList(parentTypeString, parentId, out var orderedItems); - if (this.encounteredError) + if (this.Messaging.EncounteredError) { return; } @@ -95,7 +94,7 @@ namespace WixToolset.Core.Link Debug.Assert(this.groupTypes.Contains(parentTypeString)); this.LoadFlattenOrderGroups(); - if (this.encounteredError) + if (this.Messaging.EncounteredError) { return; } @@ -127,14 +126,14 @@ namespace WixToolset.Core.Link orderedItems = null; this.LoadFlattenOrderGroups(); - if (this.encounteredError) + if (this.Messaging.EncounteredError) { return; } if (!this.items.TryGetValue(parentType, parentId, out var parentItem)) { - this.messageHandler.Write(ErrorMessages.IdentifierNotFound(parentType, parentId)); + this.Messaging.Write(ErrorMessages.IdentifierNotFound(parentType, parentId)); return; } @@ -216,7 +215,7 @@ namespace WixToolset.Core.Link // dependencies. Group references, however, we can check directly. this.FindCircularGroupReferences(); - if (!this.encounteredError) + if (!this.Messaging.EncounteredError) { this.FlattenGroups(); this.FlattenOrdering(); @@ -304,7 +303,7 @@ namespace WixToolset.Core.Link if (this.FindCircularGroupReference(item, item, itemsSeen, out circularReference)) { itemsInKnownLoops.Add(itemsSeen); - this.messageHandler.Write(ErrorMessages.ReferenceLoopDetected(item.Row.SourceLineNumbers, circularReference)); + this.Messaging.Write(ErrorMessages.ReferenceLoopDetected(item.Row.SourceLineNumbers, circularReference)); } } } @@ -376,12 +375,12 @@ namespace WixToolset.Core.Link if (!this.items.TryGetValue(rowItemType, rowItemName, out var item)) { - this.messageHandler.Write(ErrorMessages.IdentifierNotFound(rowItemType, rowItemName)); + this.Messaging.Write(ErrorMessages.IdentifierNotFound(rowItemType, rowItemName)); } if (!this.items.TryGetValue(rowDependsOnType, rowDependsOnName, out var dependsOn)) { - this.messageHandler.Write(ErrorMessages.IdentifierNotFound(rowDependsOnType, rowDependsOnName)); + this.Messaging.Write(ErrorMessages.IdentifierNotFound(rowDependsOnType, rowDependsOnName)); } if (null == item || null == dependsOn) @@ -389,7 +388,7 @@ namespace WixToolset.Core.Link continue; } - item.AddAfter(dependsOn, this.messageHandler); + item.AddAfter(dependsOn, this.Messaging); } } @@ -404,12 +403,12 @@ namespace WixToolset.Core.Link // ordering. foreach (Item item in this.items) { - item.PropagateAfterToChildItems(this.messageHandler); + item.PropagateAfterToChildItems(this.Messaging); } foreach (Item item in this.items) { - item.FlattenAfters(this.messageHandler); + item.FlattenAfters(this.Messaging); } } @@ -668,7 +667,7 @@ namespace WixToolset.Core.Link { if (String.Equals(nameof(ComplexReferenceChildType.Package), this.Type, StringComparison.Ordinal) || (String.Equals(nameof(ComplexReferenceParentType.Container), this.Type, StringComparison.Ordinal) && - !String.Equals(Compiler.BurnUXContainerId.Id, this.Id, StringComparison.Ordinal))) + !String.Equals(BurnConstants.BurnUXContainerName, this.Id, StringComparison.Ordinal))) { return false; } diff --git a/src/WixToolset.Core/WixToolsetServiceProvider.cs b/src/WixToolset.Core/WixToolsetServiceProvider.cs index 267e4524..c7d6ff1d 100644 --- a/src/WixToolset.Core/WixToolsetServiceProvider.cs +++ b/src/WixToolset.Core/WixToolsetServiceProvider.cs @@ -24,7 +24,8 @@ namespace WixToolset.Core this.AddService((provider, singletons) => AddSingleton(singletons, new ParseHelper(provider))); this.AddService((provider, singletons) => AddSingleton(singletons, new PreprocessHelper(provider))); this.AddService((provider, singletons) => AddSingleton(singletons, new BackendHelper(provider))); - this.AddService((provider, singletons) => AddSingleton(singletons, new WindowsInstallerBackendHelper(provider))); + this.AddService((provider, singletons) => AddSingleton(singletons, new PathResolver())); + this.AddService((provider, singletons) => AddSingleton(singletons, new WindowsInstallerBackendHelper())); // Transients. this.AddService((provider, singletons) => new CommandLineArguments(provider)); @@ -47,6 +48,7 @@ namespace WixToolset.Core this.AddService((provider, singletons) => new DecompileResult()); this.AddService((provider, singletons) => new IncludedFile()); this.AddService((provider, singletons) => new PreprocessResult()); + this.AddService((provider, singletons) => new ResolvedDirectory()); this.AddService((provider, singletons) => new ResolveFileResult()); this.AddService((provider, singletons) => new ResolveResult()); this.AddService((provider, singletons) => new ResolvedCabinet()); diff --git a/src/test/WixToolsetTest.CoreIntegration/BundleFixture.cs b/src/test/WixToolsetTest.CoreIntegration/BundleFixture.cs new file mode 100644 index 00000000..554f4b17 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/BundleFixture.cs @@ -0,0 +1,52 @@ +// 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. + +namespace WixToolsetTest.CoreIntegration +{ + using System.IO; + using System.Linq; + using WixBuildTools.TestSupport; + using WixToolset.Core.TestPackage; + using WixToolset.Data; + using WixToolset.Data.Tuples; + using Xunit; + + public class BundleFixture + { + [Fact] + public void CanBuildSimpleBundle() + { + var burnStubPath = TestData.Get(@"TestData\.Data\burn.exe"); + var folder = TestData.Get(@"TestData\SimpleBundle"); + + using (var fs = new DisposableFileSystem()) + { + var baseFolder = fs.GetFolder(); + var intermediateFolder = Path.Combine(baseFolder, "obj"); + + var result = WixRunner.Execute(new[] + { + "build", + Path.Combine(folder, "Bundle.wxs"), + "-loc", Path.Combine(folder, "Bundle.en-us.wxl"), + "-bindpath", Path.Combine(folder, "data"), + "-intermediateFolder", intermediateFolder, + "-burnStub", burnStubPath, + "-o", Path.Combine(baseFolder, @"bin\test.exe") + }); + + result.AssertSuccess(); + + Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\test.exe"))); +#if TODO + Assert.True(File.Exists(Path.Combine(baseFolder, @"bin\test.wixpdb"))); +#endif + + var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"test.wir")); + var section = intermediate.Sections.Single(); + + var msiTuple = section.Tuples.OfType().Single(); + Assert.Equal("test.msi", msiTuple.Id.Id ); + } + } + } +} diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/.Data/burn.exe b/src/test/WixToolsetTest.CoreIntegration/TestData/.Data/burn.exe new file mode 100644 index 00000000..2a4f423f Binary files /dev/null and b/src/test/WixToolsetTest.CoreIntegration/TestData/.Data/burn.exe differ diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/Bundle.en-us.wxl b/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/Bundle.en-us.wxl new file mode 100644 index 00000000..bc1dee83 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/Bundle.en-us.wxl @@ -0,0 +1,10 @@ + + + + + + ~TestBundle + + diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/Bundle.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/Bundle.wxs new file mode 100644 index 00000000..89dbb503 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/Bundle.wxs @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/MsiPackage/Shared.dll b/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/MsiPackage/Shared.dll new file mode 100644 index 00000000..0e461ba8 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/MsiPackage/Shared.dll @@ -0,0 +1 @@ +This is Shared.dll. \ No newline at end of file diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/MsiPackage/test.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/MsiPackage/test.txt new file mode 100644 index 00000000..8b986220 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/MsiPackage/test.txt @@ -0,0 +1 @@ +This is test.txt \ No newline at end of file diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/fakeba.dll b/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/fakeba.dll new file mode 100644 index 00000000..970efdf0 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/fakeba.dll @@ -0,0 +1 @@ +This is a fakeba.dll \ No newline at end of file diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/test.msi b/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/test.msi new file mode 100644 index 00000000..0722d60e Binary files /dev/null and b/src/test/WixToolsetTest.CoreIntegration/TestData/SimpleBundle/data/test.msi differ diff --git a/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj b/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj index a5eadae3..65034159 100644 --- a/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj +++ b/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj @@ -13,6 +13,7 @@ + @@ -31,6 +32,9 @@ + + + @@ -66,6 +70,9 @@ + + + -- cgit v1.2.3-55-g6feb