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 --- 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 -- 10 files changed, 605 insertions(+), 1130 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 (limited to 'src/WixToolset.Core.Burn/Bind') 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); - } - } - } -} -- cgit v1.2.3-55-g6feb