From dbde9e7104b907bbbaea17e21247d8cafc8b3a4c Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sat, 14 Oct 2017 16:12:07 -0700 Subject: Massive refactoring to introduce the concept of IBackend --- .../Bundles/CreateBurnManifestCommand.cs | 686 +++++++++++++++++++++ 1 file changed, 686 insertions(+) create mode 100644 src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs (limited to 'src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs') diff --git a/src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs b/src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs new file mode 100644 index 00000000..772265a0 --- /dev/null +++ b/src/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs @@ -0,0 +1,686 @@ +// 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.Diagnostics; + using System.Globalization; + using System.Linq; + using System.Text; + using System.Xml; + using WixToolset.Data; + using WixToolset.Data.Rows; + using WixToolset.Extensibility; + + internal class CreateBurnManifestCommand + { + public IEnumerable BackendExtensions { private get; set; } + + public Output Output { private get; set; } + + public string ExecutableName { private get; set; } + + public WixBundleRow BundleInfo { private get; set; } + + public WixChainRow Chain { private get; set; } + + public string OutputPath { private get; set; } + + public IEnumerable RollbackBoundaries { private get; set; } + + public IEnumerable OrderedPackages { private get; set; } + + public IEnumerable OrderedSearches { private get; set; } + + public Dictionary Payloads { private get; set; } + + public Dictionary Containers { private get; set; } + + public IEnumerable UXContainerPayloads { private get; set; } + + public IEnumerable Catalogs { private get; set; } + + public void Execute() + { + using (XmlTextWriter 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) + { + writer.WriteElementString("Condition", this.BundleInfo.Condition); + } + + // Write the log element if default logging wasn't disabled. + if (!String.IsNullOrEmpty(this.BundleInfo.LogPrefix)) + { + writer.WriteStartElement("Log"); + if (!String.IsNullOrEmpty(this.BundleInfo.LogPathVariable)) + { + writer.WriteAttributeString("PathVariable", this.BundleInfo.LogPathVariable); + } + writer.WriteAttributeString("Prefix", this.BundleInfo.LogPrefix); + writer.WriteAttributeString("Extension", this.BundleInfo.LogExtension); + writer.WriteEndElement(); + } + + + // Get update if specified. + WixBundleUpdateRow updateRow = this.Output.Tables["WixBundleUpdate"].RowsAs().FirstOrDefault(); + + if (null != updateRow) + { + writer.WriteStartElement("Update"); + writer.WriteAttributeString("Location", updateRow.Location); + writer.WriteEndElement(); // + } + + // Write the RelatedBundle elements + + // 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"]); + + foreach (WixRelatedBundleRow relatedBundle in relatedBundles) + { + writer.WriteStartElement("RelatedBundle"); + writer.WriteAttributeString("Id", relatedBundle.Id); + writer.WriteAttributeString("Action", Convert.ToString(relatedBundle.Action, CultureInfo.InvariantCulture)); + writer.WriteEndElement(); + } + + // Write the variables + IEnumerable variables = this.Output.Tables["WixBundleVariable"].RowsAs(); + + foreach (WixBundleVariableRow variable in variables) + { + writer.WriteStartElement("Variable"); + writer.WriteAttributeString("Id", variable.Id); + if (null != variable.Type) + { + writer.WriteAttributeString("Value", variable.Value); + writer.WriteAttributeString("Type", variable.Type); + } + writer.WriteAttributeString("Hidden", variable.Hidden ? "yes" : "no"); + writer.WriteAttributeString("Persisted", variable.Persisted ? "yes" : "no"); + writer.WriteEndElement(); + } + + // Write the searches + foreach (WixSearchInfo searchinfo in this.OrderedSearches) + { + searchinfo.WriteXml(writer); + } + + // write the UX element + writer.WriteStartElement("UX"); + if (!String.IsNullOrEmpty(this.BundleInfo.SplashScreenBitmapPath)) + { + writer.WriteAttributeString("SplashScreen", "yes"); + } + + // write the UX allPayloads... + foreach (WixBundlePayloadRow payload in this.UXContainerPayloads) + { + writer.WriteStartElement("Payload"); + this.WriteBurnManifestPayloadAttributes(writer, payload, true, this.Payloads); + writer.WriteEndElement(); + } + + writer.WriteEndElement(); // + + // write the catalog elements + if (this.Catalogs.Any()) + { + foreach (WixBundleCatalogRow catalog in this.Catalogs) + { + writer.WriteStartElement("Catalog"); + writer.WriteAttributeString("Id", catalog.Id); + writer.WriteAttributeString("Payload", catalog.Payload); + writer.WriteEndElement(); + } + } + + foreach (WixBundleContainerRow container in this.Containers.Values) + { + if (!String.IsNullOrEmpty(container.WorkingPath) && Compiler.BurnUXContainerId != container.Id) + { + writer.WriteStartElement("Container"); + this.WriteBurnManifestContainerAttributes(writer, this.ExecutableName, container); + writer.WriteEndElement(); + } + } + + foreach (WixBundlePayloadRow payload in this.Payloads.Values) + { + if (PackagingType.Embedded == payload.Packaging && Compiler.BurnUXContainerId != payload.Container) + { + writer.WriteStartElement("Payload"); + this.WriteBurnManifestPayloadAttributes(writer, payload, true, this.Payloads); + writer.WriteEndElement(); + } + else if (PackagingType.External == payload.Packaging) + { + writer.WriteStartElement("Payload"); + this.WriteBurnManifestPayloadAttributes(writer, payload, false, this.Payloads); + writer.WriteEndElement(); + } + } + + foreach (WixBundleRollbackBoundaryRow 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.WriteEndElement(); + } + + // Write the registration information... + writer.WriteStartElement("Registration"); + + writer.WriteAttributeString("Id", this.BundleInfo.BundleId.ToString("B")); + 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.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); + + if (!String.IsNullOrEmpty(this.BundleInfo.Publisher)) + { + writer.WriteAttributeString("Publisher", this.BundleInfo.Publisher); + } + + if (!String.IsNullOrEmpty(this.BundleInfo.HelpLink)) + { + writer.WriteAttributeString("HelpLink", this.BundleInfo.HelpLink); + } + + if (!String.IsNullOrEmpty(this.BundleInfo.HelpTelephone)) + { + writer.WriteAttributeString("HelpTelephone", this.BundleInfo.HelpTelephone); + } + + if (!String.IsNullOrEmpty(this.BundleInfo.AboutUrl)) + { + writer.WriteAttributeString("AboutUrl", this.BundleInfo.AboutUrl); + } + + if (!String.IsNullOrEmpty(this.BundleInfo.UpdateUrl)) + { + writer.WriteAttributeString("UpdateUrl", this.BundleInfo.UpdateUrl); + } + + if (!String.IsNullOrEmpty(this.BundleInfo.ParentName)) + { + writer.WriteAttributeString("ParentDisplayName", this.BundleInfo.ParentName); + } + + if (1 == this.BundleInfo.DisableModify) + { + writer.WriteAttributeString("DisableModify", "yes"); + } + else if (2 == this.BundleInfo.DisableModify) + { + writer.WriteAttributeString("DisableModify", "button"); + } + + if (this.BundleInfo.DisableRemove) + { + writer.WriteAttributeString("DisableRemove", "yes"); + } + writer.WriteEndElement(); // + + // Get update registration if specified. + WixUpdateRegistrationRow updateRegistrationInfo = this.Output.Tables["WixUpdateRegistration"].RowsAs().FirstOrDefault(); + + if (null != updateRegistrationInfo) + { + writer.WriteStartElement("Update"); // + writer.WriteAttributeString("Manufacturer", updateRegistrationInfo.Manufacturer); + + if (!String.IsNullOrEmpty(updateRegistrationInfo.Department)) + { + writer.WriteAttributeString("Department", updateRegistrationInfo.Department); + } + + if (!String.IsNullOrEmpty(updateRegistrationInfo.ProductFamily)) + { + writer.WriteAttributeString("ProductFamily", updateRegistrationInfo.ProductFamily); + } + + writer.WriteAttributeString("Name", updateRegistrationInfo.Name); + writer.WriteAttributeString("Classification", updateRegistrationInfo.Classification); + writer.WriteEndElement(); // + } + + IEnumerable bundleTags = this.Output.Tables["WixBundleTag"].RowsAs(); + + foreach (Row row in bundleTags) + { + writer.WriteStartElement("SoftwareTag"); + writer.WriteAttributeString("Filename", (string)row[0]); + writer.WriteAttributeString("Regid", (string)row[1]); + writer.WriteCData((string)row[4]); + writer.WriteEndElement(); + } + + writer.WriteEndElement(); // + + // write the Chain... + writer.WriteStartElement("Chain"); + if (this.Chain.DisableRollback) + { + writer.WriteAttributeString("DisableRollback", "yes"); + } + + if (this.Chain.DisableSystemRestore) + { + writer.WriteAttributeString("DisableSystemRestore", "yes"); + } + + if (this.Chain.ParallelCache) + { + writer.WriteAttributeString("ParallelCache", "yes"); + } + + // 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); + + // Build up the list of target codes from all the MSPs in the chain. + List targetCodes = new List(); + + foreach (PackageFacade package in this.OrderedPackages) + { + writer.WriteStartElement(String.Format(CultureInfo.InvariantCulture, "{0}Package", package.Package.Type)); + + writer.WriteAttributeString("Id", package.Package.WixChainItemId); + + switch (package.Package.Cache) + { + case YesNoAlwaysType.No: + writer.WriteAttributeString("Cache", "no"); + break; + case YesNoAlwaysType.Yes: + writer.WriteAttributeString("Cache", "yes"); + break; + case YesNoAlwaysType.Always: + writer.WriteAttributeString("Cache", "always"); + 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"); + + if (null != package.Package.RollbackBoundary) + { + writer.WriteAttributeString("RollbackBoundaryForward", package.Package.RollbackBoundary); + } + + if (!String.IsNullOrEmpty(package.Package.RollbackBoundaryBackward)) + { + writer.WriteAttributeString("RollbackBoundaryBackward", package.Package.RollbackBoundaryBackward); + } + + if (!String.IsNullOrEmpty(package.Package.LogPathVariable)) + { + writer.WriteAttributeString("LogPathVariable", package.Package.LogPathVariable); + } + + if (!String.IsNullOrEmpty(package.Package.RollbackLogPathVariable)) + { + writer.WriteAttributeString("RollbackLogPathVariable", package.Package.RollbackLogPathVariable); + } + + if (!String.IsNullOrEmpty(package.Package.InstallCondition)) + { + writer.WriteAttributeString("InstallCondition", package.Package.InstallCondition); + } + + if (WixBundlePackageType.Exe == package.Package.Type) + { + 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("Protocol", package.ExePackage.ExeProtocol); + } + } + else if (WixBundlePackageType.Msi == package.Package.Type) + { + 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("UpgradeCode", package.MsiPackage.UpgradeCode); + } + } + else if (WixBundlePackageType.Msp == package.Package.Type) + { + writer.WriteAttributeString("PatchCode", package.MspPackage.PatchCode); + writer.WriteAttributeString("PatchXml", package.MspPackage.PatchXml); + writer.WriteAttributeString("DisplayInternalUI", package.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) + { + IEnumerable patchTargetCodes = targetCodesByPatch[package.MspPackage.ChainPackageId]; + + targetCodes.AddRange(patchTargetCodes); + } + else // we have a patch that targets the world, so throw the whole list away. + { + targetCodes = null; + } + } + } + else if (WixBundlePackageType.Msu == package.Package.Type) + { + writer.WriteAttributeString("DetectCondition", package.MsuPackage.DetectCondition); + writer.WriteAttributeString("KB", package.MsuPackage.MsuKB); + } + + IEnumerable packageMsiFeatures = msiFeaturesByPackage[package.Package.WixChainItemId]; + + foreach (WixBundleMsiFeatureRow feature in packageMsiFeatures) + { + writer.WriteStartElement("MsiFeature"); + writer.WriteAttributeString("Id", feature.Name); + writer.WriteEndElement(); + } + + IEnumerable packageMsiProperties = msiPropertiesByPackage[package.Package.WixChainItemId]; + + foreach (WixBundleMsiPropertyRow msiProperty in packageMsiProperties) + { + writer.WriteStartElement("MsiProperty"); + writer.WriteAttributeString("Id", msiProperty.Name); + writer.WriteAttributeString("Value", msiProperty.Value); + if (!String.IsNullOrEmpty(msiProperty.Condition)) + { + writer.WriteAttributeString("Condition", msiProperty.Condition); + } + writer.WriteEndElement(); + } + + IEnumerable packageSlipstreamMsps = slipstreamMspsByPackage[package.Package.WixChainItemId]; + + foreach (WixBundleSlipstreamMspRow slipstreamMsp in packageSlipstreamMsps) + { + writer.WriteStartElement("SlipstreamMsp"); + writer.WriteAttributeString("Id", slipstreamMsp.MspPackageId); + writer.WriteEndElement(); + } + + IEnumerable packageExitCodes = exitCodesByPackage[package.Package.WixChainItemId]; + + foreach (WixBundlePackageExitCodeRow exitCode in packageExitCodes) + { + writer.WriteStartElement("ExitCode"); + + if (exitCode.Code.HasValue) + { + writer.WriteAttributeString("Code", unchecked((uint)exitCode.Code).ToString(CultureInfo.InvariantCulture)); + } + else + { + writer.WriteAttributeString("Code", "*"); + } + + writer.WriteAttributeString("Type", ((int)exitCode.Behavior).ToString(CultureInfo.InvariantCulture)); + writer.WriteEndElement(); + } + + IEnumerable packageCommandLines = commandLinesByPackage[package.Package.WixChainItemId]; + + foreach (WixBundlePackageCommandLineRow commandLine in packageCommandLines) + { + writer.WriteStartElement("CommandLine"); + writer.WriteAttributeString("InstallArgument", commandLine.InstallArgument); + writer.WriteAttributeString("UninstallArgument", commandLine.UninstallArgument); + writer.WriteAttributeString("RepairArgument", commandLine.RepairArgument); + writer.WriteAttributeString("Condition", commandLine.Condition); + writer.WriteEndElement(); + } + + // Output the dependency information. + foreach (ProvidesDependency dependency in package.Provides) + { + // TODO: Add to wixpdb as an imported table, or link package wixpdbs to bundle wixpdbs. + dependency.WriteXml(writer); + } + + IEnumerable packageRelatedPackages = relatedPackagesByPackage[package.Package.WixChainItemId]; + + foreach (WixBundleRelatedPackageRow related in packageRelatedPackages) + { + writer.WriteStartElement("RelatedPackage"); + writer.WriteAttributeString("Id", related.Id); + if (!String.IsNullOrEmpty(related.MinVersion)) + { + writer.WriteAttributeString("MinVersion", related.MinVersion); + writer.WriteAttributeString("MinInclusive", related.MinInclusive ? "yes" : "no"); + } + if (!String.IsNullOrEmpty(related.MaxVersion)) + { + writer.WriteAttributeString("MaxVersion", related.MaxVersion); + writer.WriteAttributeString("MaxInclusive", related.MaxInclusive ? "yes" : "no"); + } + writer.WriteAttributeString("OnlyDetect", related.OnlyDetect ? "yes" : "no"); + + string[] relatedLanguages = related.Languages.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + if (0 < relatedLanguages.Length) + { + writer.WriteAttributeString("LangInclusive", related.LangInclusive ? "yes" : "no"); + foreach (string language in relatedLanguages) + { + writer.WriteStartElement("Language"); + writer.WriteAttributeString("Id", language); + writer.WriteEndElement(); + } + } + writer.WriteEndElement(); + } + + // Write any contained Payloads with the PackagePayload being first + writer.WriteStartElement("PayloadRef"); + writer.WriteAttributeString("Id", package.Package.PackagePayload); + writer.WriteEndElement(); + + IEnumerable packagePayloads = payloadsByPackage[package.Package.WixChainItemId]; + + foreach (WixBundlePayloadRow payload in packagePayloads) + { + if (payload.Id != package.Package.PackagePayload) + { + writer.WriteStartElement("PayloadRef"); + writer.WriteAttributeString("Id", payload.Id); + writer.WriteEndElement(); + } + } + + writer.WriteEndElement(); // + } + writer.WriteEndElement(); // + + if (null != targetCodes) + { + foreach (WixBundlePatchTargetCodeRow targetCode in targetCodes) + { + writer.WriteStartElement("PatchTargetCode"); + writer.WriteAttributeString("TargetCode", targetCode.TargetCode); + writer.WriteAttributeString("Product", targetCode.TargetsProductCode ? "yes" : "no"); + writer.WriteEndElement(); + } + } + + // Write the ApprovedExeForElevation elements. + IEnumerable approvedExesForElevation = this.Output.Tables["WixApprovedExeForElevation"].RowsAs(); + + foreach (WixApprovedExeForElevationRow approvedExeForElevation in approvedExesForElevation) + { + writer.WriteStartElement("ApprovedExeForElevation"); + writer.WriteAttributeString("Id", approvedExeForElevation.Id); + writer.WriteAttributeString("Key", approvedExeForElevation.Key); + + if (!String.IsNullOrEmpty(approvedExeForElevation.ValueName)) + { + writer.WriteAttributeString("ValueName", approvedExeForElevation.ValueName); + } + + if (approvedExeForElevation.Win64) + { + writer.WriteAttributeString("Win64", "yes"); + } + + writer.WriteEndElement(); + } + + writer.WriteEndDocument(); // + } + } + + private void WriteBurnManifestContainerAttributes(XmlTextWriter writer, string executableName, WixBundleContainerRow container) + { + writer.WriteAttributeString("Id", container.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); + if (!String.IsNullOrEmpty(resolvedUrl)) + { + writer.WriteAttributeString("DownloadUrl", resolvedUrl); + } + else if (!String.IsNullOrEmpty(container.DownloadUrl)) + { + writer.WriteAttributeString("DownloadUrl", container.DownloadUrl); + } + + writer.WriteAttributeString("FilePath", container.Name); + } + else if (ContainerType.Attached == container.Type) + { + if (!String.IsNullOrEmpty(container.DownloadUrl)) + { + Messaging.Instance.OnMessage(WixWarnings.DownloadUrlNotSupportedForAttachedContainers(container.SourceLineNumbers, container.Id)); + } + + writer.WriteAttributeString("FilePath", executableName); // attached containers use the name of the bundle since they are attached to the executable. + writer.WriteAttributeString("AttachedIndex", container.AttachedContainerIndex.ToString(CultureInfo.InvariantCulture)); + writer.WriteAttributeString("Attached", "yes"); + writer.WriteAttributeString("Primary", "yes"); + } + } + + private void WriteBurnManifestPayloadAttributes(XmlTextWriter writer, WixBundlePayloadRow payload, bool embeddedOnly, Dictionary allPayloads) + { + Debug.Assert(!embeddedOnly || PackagingType.Embedded == payload.Packaging); + + writer.WriteAttributeString("Id", payload.Id); + writer.WriteAttributeString("FilePath", payload.Name); + writer.WriteAttributeString("FileSize", payload.FileSize.ToString(CultureInfo.InvariantCulture)); + writer.WriteAttributeString("Hash", payload.Hash); + + if (payload.LayoutOnly) + { + writer.WriteAttributeString("LayoutOnly", "yes"); + } + + if (!String.IsNullOrEmpty(payload.PublicKey)) + { + writer.WriteAttributeString("CertificateRootPublicKeyIdentifier", payload.PublicKey); + } + + if (!String.IsNullOrEmpty(payload.Thumbprint)) + { + writer.WriteAttributeString("CertificateRootThumbprint", payload.Thumbprint); + } + + switch (payload.Packaging) + { + case PackagingType.Embedded: // this means it's in a container. + if (!String.IsNullOrEmpty(payload.DownloadUrl)) + { + Messaging.Instance.OnMessage(WixWarnings.DownloadUrlNotSupportedForEmbeddedPayloads(payload.SourceLineNumbers, payload.Id)); + } + + writer.WriteAttributeString("Packaging", "embedded"); + writer.WriteAttributeString("SourcePath", payload.EmbeddedId); + + if (Compiler.BurnUXContainerId != payload.Container) + { + writer.WriteAttributeString("Container", payload.Container); + } + 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); + if (!String.IsNullOrEmpty(resolvedUrl)) + { + writer.WriteAttributeString("DownloadUrl", resolvedUrl); + } + else if (!String.IsNullOrEmpty(payload.DownloadUrl)) + { + writer.WriteAttributeString("DownloadUrl", payload.DownloadUrl); + } + + writer.WriteAttributeString("Packaging", "external"); + writer.WriteAttributeString("SourcePath", payload.Name); + break; + } + + if (!String.IsNullOrEmpty(payload.Catalog)) + { + writer.WriteAttributeString("Catalog", payload.Catalog); + } + } + + private string ResolveUrl(string url, string fallbackUrl, string packageId, string payloadId, string fileName) + { + string resolved = null; + foreach (var extension in this.BackendExtensions) + { + resolved = extension.ResolveUrl(url, fallbackUrl, packageId, payloadId, fileName); + if (!String.IsNullOrEmpty(resolved)) + { + break; + } + } + + return resolved; + } + } +} -- cgit v1.2.3-55-g6feb