// 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.BootstrapperApplications { using System; using System.Collections.Generic; using System.Xml.Linq; using WixToolset.BootstrapperApplications.Symbols; using WixToolset.Data; using WixToolset.Data.Burn; using WixToolset.Data.Symbols; using WixToolset.Extensibility; using WixToolset.Extensibility.Data; /// /// The compiler for the WiX Toolset Bal Extension. /// public sealed class BalCompiler : BaseCompilerExtension { private readonly Dictionary packageInfoSymbolsByPackageId = new Dictionary(); private readonly Dictionary prereqInfoSymbolsByPackageId = new Dictionary(); private enum WixPrerequisiteBootstrapperApplicationTheme { Unknown, None, Standard, } private enum WixInternalUIBootstrapperApplicationTheme { Unknown, None, Standard, } private enum WixStandardBootstrapperApplicationTheme { Unknown, HyperlinkLargeLicense, HyperlinkLicense, HyperlinkSidebarLicense, None, RtfLargeLicense, RtfLicense, } public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/bal"; /// /// Processes an element for the Compiler. /// /// /// /// Parent element of element to process. /// Element to process. /// Extra information about the context in which this element is being parsed. public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary context) { this.ParsePossibleKeyPathElement(intermediate, section, parentElement, element, context); } /// /// Processes an element for the Compiler. /// /// Source line number for the parent element. /// Parent element of element to process. /// Element to process. /// Extra information about the context in which this element is being parsed. public override IComponentKeyPath ParsePossibleKeyPathElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary context) { IComponentKeyPath exePayloadRef = null; switch (parentElement.Name.LocalName) { case "Bundle": case "Fragment": switch (element.Name.LocalName) { case "BootstrapperApplicationPrerequisiteInformation": this.ParseBootstrapperApplicationPrerequisiteInformationElement(intermediate, section, element); break; case "Condition": this.ParseConditionElement(intermediate, section, element); break; case "ManagedBootstrapperApplicationPrereqInformation": this.Messaging.Write(WarningMessages.DeprecatedElement(this.ParseHelper.GetSourceLineNumbers(element), element.Name.LocalName, "BootstrapperApplicationPrerequisiteInformation")); this.ParseBootstrapperApplicationPrerequisiteInformationElement(intermediate, section, element); break; default: this.ParseHelper.UnexpectedElement(parentElement, element); break; } break; case "BootstrapperApplication": switch (element.Name.LocalName) { case "WixInternalUIBootstrapperApplication": exePayloadRef = this.ParseWixInternalUIBootstrapperApplicationElement(intermediate, section, element); break; case "WixPrerequisiteBootstrapperApplication": this.ParseWixPrerequisiteBootstrapperApplicationElement(intermediate, section, element, context); break; case "WixStandardBootstrapperApplication": exePayloadRef = this.ParseWixStandardBootstrapperApplicationElement(intermediate, section, element); break; case "WixManagedBootstrapperApplicationHost": this.Messaging.Write(WarningMessages.DeprecatedElement(this.ParseHelper.GetSourceLineNumbers(element), element.Name.LocalName)); break; case "WixDotNetCoreBootstrapperApplicationHost": this.Messaging.Write(WarningMessages.DeprecatedElement(this.ParseHelper.GetSourceLineNumbers(element), element.Name.LocalName)); break; default: this.ParseHelper.UnexpectedElement(parentElement, element); break; } break; default: this.ParseHelper.UnexpectedElement(parentElement, element); break; } return exePayloadRef; } /// /// Processes an attribute for the Compiler. /// /// Source line number for the parent element. /// Parent element of element to process. /// Attribute to process. /// Extra information about the context in which this element is being parsed. public override void ParseAttribute(Intermediate intermediate, IntermediateSection section, XElement parentElement, XAttribute attribute, IDictionary context) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(parentElement); switch (parentElement.Name.LocalName) { case "Bundle": switch (attribute.Name.LocalName) { case "CommandLineVariables": WixStdbaCommandLineVariableType? variableType = null; var commandLineVariablesValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute); switch (commandLineVariablesValue) { case "caseInsensitive": variableType = WixStdbaCommandLineVariableType.CaseInsensitive; break; case "caseSensitive": variableType = WixStdbaCommandLineVariableType.CaseSensitive; break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, parentElement.Name.LocalName, attribute.Name.LocalName, commandLineVariablesValue, "caseInsensitive", "caseSensitive")); break; } if (variableType.HasValue) { // There can only be one. var id = new Identifier(AccessModifier.Global, "WixStdbaCommandLineVariableType"); section.AddSymbol(new WixStdbaCommandLineSymbol(sourceLineNumbers, id) { VariableType = variableType.Value, }); } break; default: this.ParseHelper.UnexpectedAttribute(parentElement, attribute); break; } break; case "BundlePackage": case "ExePackage": case "MsiPackage": case "MspPackage": case "MsuPackage": string packageId; if (!context.TryGetValue("PackageId", out packageId) || String.IsNullOrEmpty(packageId)) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, parentElement.Name.LocalName, "Id", attribute.Name.LocalName)); } else { switch (attribute.Name.LocalName) { case "DisplayInternalUICondition": switch (parentElement.Name.LocalName) { case "MsiPackage": case "MspPackage": var displayInternalUICondition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute); var packageInfo = this.GetBalPackageInfoSymbol(section, sourceLineNumbers, packageId); packageInfo.DisplayInternalUICondition = displayInternalUICondition; break; default: this.ParseHelper.UnexpectedAttribute(parentElement, attribute); break; } break; case "PrimaryPackageType": { var primaryPackageType = BalPrimaryPackageType.None; var primaryPackageTypeValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute); switch (primaryPackageTypeValue) { case "default": primaryPackageType = BalPrimaryPackageType.Default; break; case "x86": primaryPackageType = BalPrimaryPackageType.X86; break; case "x64": primaryPackageType = BalPrimaryPackageType.X64; break; case "arm64": primaryPackageType = BalPrimaryPackageType.ARM64; break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, parentElement.Name.LocalName, "PrimaryPackageType", primaryPackageTypeValue, "default", "x86", "x64", "arm64")); break; } // at the time the extension attribute is parsed, the compiler might not yet have // parsed the PrereqPackage attribute, so we need to get it directly from the parent element. var prereqPackage = parentElement.Attribute(this.Namespace + "PrereqPackage"); var prereqInfo = this.GetMbaPrereqInformationSymbol(section, sourceLineNumbers, prereqPackage, packageId); if (prereqInfo != null) { this.Messaging.Write(ErrorMessages.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, parentElement.Name.LocalName, "PrereqPackage", "yes", "PrimaryPackageType")); } else { var packageInfo = this.GetBalPackageInfoSymbol(section, sourceLineNumbers, packageId); packageInfo.PrimaryPackageType = primaryPackageType; } break; } case "PrereqLicenseFile": { // at the time the extension attribute is parsed, the compiler might not yet have // parsed the PrereqPackage attribute, so we need to get it directly from the parent element. var prereqPackage = parentElement.Attribute(this.Namespace + "PrereqPackage"); var prereqInfo = this.GetMbaPrereqInformationSymbol(section, sourceLineNumbers, prereqPackage, packageId); if (prereqInfo == null) { this.Messaging.Write(BalErrors.AttributeRequiresPrereqPackage(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseFile")); } else if (null != prereqInfo.LicenseUrl) { this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseFile", "PrereqLicenseUrl")); } else { prereqInfo.LicenseFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute); } break; } case "PrereqLicenseUrl": { // at the time the extension attribute is parsed, the compiler might not yet have // parsed the PrereqPackage attribute, so we need to get it directly from the parent element. var prereqPackage = parentElement.Attribute(this.Namespace + "PrereqPackage"); var prereqInfo = this.GetMbaPrereqInformationSymbol(section, sourceLineNumbers, prereqPackage, packageId); if (prereqInfo == null) { this.Messaging.Write(BalErrors.AttributeRequiresPrereqPackage(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseUrl")); } else if (null != prereqInfo.LicenseFile) { this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, parentElement.Name.LocalName, "PrereqLicenseUrl", "PrereqLicenseFile")); } else { prereqInfo.LicenseUrl = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute); } break; } case "PrereqPackage": this.GetMbaPrereqInformationSymbol(section, sourceLineNumbers, attribute, packageId); break; default: this.ParseHelper.UnexpectedAttribute(parentElement, attribute); break; } } break; case "Payload": string payloadId; if (!context.TryGetValue("Id", out payloadId) || String.IsNullOrEmpty(payloadId)) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, parentElement.Name.LocalName, "Id", attribute.Name.LocalName)); } else { switch (attribute.Name.LocalName) { case "BAFactoryAssembly": this.Messaging.Write(BalWarnings.DeprecatedBAFactoryAssemblyAttribute(this.ParseHelper.GetSourceLineNumbers(parentElement), parentElement.Name.LocalName, attribute.Name.LocalName)); break; case "BAFunctions": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attribute)) { section.AddSymbol(new WixBalBAFunctionsSymbol(sourceLineNumbers) { PayloadId = payloadId, }); } break; default: this.ParseHelper.UnexpectedAttribute(parentElement, attribute); break; } } break; case "Variable": // at the time the extension attribute is parsed, the compiler might not yet have // parsed the Name attribute, so we need to get it directly from the parent element. var variableName = parentElement.Attribute("Name"); if (null == variableName) { this.Messaging.Write(ErrorMessages.ExpectedParentWithAttribute(sourceLineNumbers, "Variable", "Overridable", "Name")); } else { switch (attribute.Name.LocalName) { case "Overridable": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attribute)) { section.AddSymbol(new WixStdbaOverridableVariableSymbol(sourceLineNumbers) { Name = variableName.Value, }); } break; default: this.ParseHelper.UnexpectedAttribute(parentElement, attribute); break; } } break; } } private WixBalPackageInfoSymbol GetBalPackageInfoSymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, string packageId) { if (!this.packageInfoSymbolsByPackageId.TryGetValue(packageId, out var packageInfo)) { packageInfo = section.AddSymbol(new WixBalPackageInfoSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, packageId)) { PackageId = packageId, }); this.packageInfoSymbolsByPackageId.Add(packageId, packageInfo); } return packageInfo; } private WixPrereqInformationSymbol GetMbaPrereqInformationSymbol(IntermediateSection section, SourceLineNumber sourceLineNumbers, XAttribute prereqAttribute, string packageId) { WixPrereqInformationSymbol prereqInfo = null; if (prereqAttribute != null && YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, prereqAttribute)) { if (!this.prereqInfoSymbolsByPackageId.TryGetValue(packageId, out prereqInfo)) { prereqInfo = section.AddSymbol(new WixPrereqInformationSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, packageId)) { PackageId = packageId, }); this.prereqInfoSymbolsByPackageId.Add(packageId, prereqInfo); } } return prereqInfo; } /// /// Parses a Condition element for Bundles. /// /// The element to parse. private void ParseConditionElement(Intermediate intermediate, IntermediateSection section, XElement node) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); string condition = null; string message = null; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Message": message = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Condition": condition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.ParseHelper.UnexpectedAttribute(node, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib); } } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node); // Error check the values. if (String.IsNullOrEmpty(condition)) { this.Messaging.Write(ErrorMessages.ConditionExpected(sourceLineNumbers, node.Name.LocalName)); } if (null == message) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Message")); } if (!this.Messaging.EncounteredError) { section.AddSymbol(new WixBalConditionSymbol(sourceLineNumbers) { Condition = condition, Message = message, }); } } /// /// Parses a Condition element for Bundles. /// /// The element to parse. private void ParseBootstrapperApplicationPrerequisiteInformationElement(Intermediate intermediate, IntermediateSection section, XElement node) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); string packageId = null; string licenseFile = null; string licenseUrl = null; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "LicenseFile": licenseFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "LicenseUrl": licenseUrl = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "PackageId": packageId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); break; default: this.ParseHelper.UnexpectedAttribute(node, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib); } } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node); if (null == packageId) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "PackageId")); } if (null == licenseFile && null == licenseUrl || null != licenseFile && null != licenseUrl) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "LicenseFile", "LicenseUrl", true)); } if (!this.Messaging.EncounteredError) { section.AddSymbol(new WixPrereqInformationSymbol(sourceLineNumbers) { PackageId = packageId, LicenseFile = licenseFile, LicenseUrl = licenseUrl, }); this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.WixBundlePackage, packageId); } } private IComponentKeyPath ParseWixInternalUIBootstrapperApplicationElement(Intermediate intermediate, IntermediateSection section, XElement node) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); var theme = WixInternalUIBootstrapperApplicationTheme.Standard; string themeFile = null; string logoFile = null; string localizationFile = null; IComponentKeyPath exePayloadRef = null; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "LogoFile": logoFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "ThemeFile": themeFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "LocalizationFile": localizationFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Theme": var themeValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (themeValue) { case "none": theme = WixInternalUIBootstrapperApplicationTheme.None; break; case "standard": theme = WixInternalUIBootstrapperApplicationTheme.Standard; break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Theme", themeValue, "none", "standard")); theme = WixInternalUIBootstrapperApplicationTheme.Unknown; break; } break; default: this.ParseHelper.UnexpectedAttribute(node, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib); } } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node); if (!this.Messaging.EncounteredError) { if (!String.IsNullOrEmpty(logoFile)) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixIuibaLogo")) { Value = logoFile, }); } if (!String.IsNullOrEmpty(themeFile)) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixIuibaThemeXml")) { Value = themeFile, }); } if (!String.IsNullOrEmpty(localizationFile)) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixIuibaThemeWxl")) { Value = localizationFile, }); } switch (theme) { case WixInternalUIBootstrapperApplicationTheme.Standard: this.CreatePayloadGroupRef(section, sourceLineNumbers, node, "WixIuibaStandardPayloads", platformSpecific: false); break; } section.AddSymbol(new WixBalBootstrapperApplicationSymbol(sourceLineNumbers) { Type = WixBalBootstrapperApplicationType.InternalUi, }); section.AddSymbol(new WixPrereqOptionsSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixPrereqOptions")) { Primary = 1, HandleHelp = 1, HandleLayout = 1, }); var exePayloadId = this.CreatePayloadGroupRef(section, sourceLineNumbers, node, "WixInternalUIBootstrapperApplication", platformSpecific: true); exePayloadRef = this.CreateComponentKeyPath(); exePayloadRef.Id = new Identifier(AccessModifier.Section, exePayloadId); exePayloadRef.Explicit = true; // Internal UI BA is always secondary because the PrereqBA is always primary to handle the help and layout options. exePayloadRef.Type = PossibleKeyPathType.File; } return exePayloadRef; } /// /// Parses a WixStandardBootstrapperApplication element for Bundles. /// /// The element to parse. private IComponentKeyPath ParseWixStandardBootstrapperApplicationElement(Intermediate intermediate, IntermediateSection section, XElement node) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); string launchTarget = null; string launchTargetElevatedId = null; string launchArguments = null; var launchHidden = YesNoType.NotSet; string launchWorkingDir = null; string licenseFile = null; string licenseUrl = null; string logoFile = null; string logoSideFile = null; WixStandardBootstrapperApplicationTheme? theme = null; string themeFile = null; string localizationFile = null; var suppressOptionsUI = YesNoType.NotSet; var suppressDowngradeFailure = YesNoType.NotSet; var suppressRepair = YesNoType.NotSet; var showVersion = YesNoType.NotSet; var supportCacheOnly = YesNoType.NotSet; IComponentKeyPath exePayloadRef = null; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "LaunchTarget": launchTarget = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "LaunchTargetElevatedId": launchTargetElevatedId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); break; case "LaunchArguments": launchArguments = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "LaunchHidden": launchHidden = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "LaunchWorkingFolder": launchWorkingDir = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "LicenseFile": licenseFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "LicenseUrl": licenseUrl = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); break; case "LogoFile": logoFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "LogoSideFile": logoSideFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "ThemeFile": themeFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "LocalizationFile": localizationFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "SuppressOptionsUI": suppressOptionsUI = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "SuppressDowngradeFailure": suppressDowngradeFailure = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "SuppressRepair": suppressRepair = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "ShowVersion": showVersion = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "SupportCacheOnly": supportCacheOnly = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "Theme": var themeValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (themeValue) { case "hyperlinkLargeLicense": theme = WixStandardBootstrapperApplicationTheme.HyperlinkLargeLicense; break; case "hyperlinkLicense": theme = WixStandardBootstrapperApplicationTheme.HyperlinkLicense; break; case "hyperlinkSidebarLicense": theme = WixStandardBootstrapperApplicationTheme.HyperlinkSidebarLicense; break; case "none": theme = WixStandardBootstrapperApplicationTheme.None; break; case "rtfLargeLicense": theme = WixStandardBootstrapperApplicationTheme.RtfLargeLicense; break; case "rtfLicense": theme = WixStandardBootstrapperApplicationTheme.RtfLicense; break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Theme", themeValue, "hyperlinkLargeLicense", "hyperlinkLicense", "hyperlinkSidebarLicense", "none", "rtfLargeLicense", "rtfLicense")); theme = WixStandardBootstrapperApplicationTheme.Unknown; // set a value to prevent expected attribute error below. break; } break; default: this.ParseHelper.UnexpectedAttribute(node, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib); } } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node); if (!theme.HasValue) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Theme")); } if (theme != WixStandardBootstrapperApplicationTheme.None && String.IsNullOrEmpty(licenseFile) && null == licenseUrl) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "LicenseFile", "LicenseUrl", true)); } if (!this.Messaging.EncounteredError) { if (!String.IsNullOrEmpty(launchTarget)) { section.AddSymbol(new WixBundleVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "LaunchTarget")) { Value = launchTarget, Type = WixBundleVariableType.Formatted, }); } if (!String.IsNullOrEmpty(launchTargetElevatedId)) { section.AddSymbol(new WixBundleVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "LaunchTargetElevatedId")) { Value = launchTargetElevatedId, Type = WixBundleVariableType.Formatted, }); } if (!String.IsNullOrEmpty(launchArguments)) { section.AddSymbol(new WixBundleVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "LaunchArguments")) { Value = launchArguments, Type = WixBundleVariableType.Formatted, }); } if (YesNoType.Yes == launchHidden) { section.AddSymbol(new WixBundleVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "LaunchHidden")) { Value = "yes", Type = WixBundleVariableType.Formatted, }); } if (!String.IsNullOrEmpty(launchWorkingDir)) { section.AddSymbol(new WixBundleVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "LaunchWorkingFolder")) { Value = launchWorkingDir, Type = WixBundleVariableType.Formatted, }); } if (!String.IsNullOrEmpty(licenseFile)) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixStdbaLicenseRtf")) { Value = licenseFile, }); } if (null != licenseUrl) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixStdbaLicenseUrl")) { Value = licenseUrl, }); } if (!String.IsNullOrEmpty(logoFile)) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixStdbaLogo")) { Value = logoFile, }); } if (!String.IsNullOrEmpty(logoSideFile)) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixStdbaLogoSide")) { Value = logoSideFile, }); } if (!String.IsNullOrEmpty(themeFile)) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixStdbaThemeXml")) { Value = themeFile, }); } if (!String.IsNullOrEmpty(localizationFile)) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixStdbaThemeWxl")) { Value = localizationFile, }); } if (YesNoType.Yes == suppressOptionsUI || YesNoType.Yes == suppressDowngradeFailure || YesNoType.Yes == suppressRepair || YesNoType.Yes == showVersion || YesNoType.Yes == supportCacheOnly) { var symbol = section.AddSymbol(new WixStdbaOptionsSymbol(sourceLineNumbers)); if (YesNoType.Yes == suppressOptionsUI) { symbol.SuppressOptionsUI = 1; } if (YesNoType.Yes == suppressDowngradeFailure) { symbol.SuppressDowngradeFailure = 1; } if (YesNoType.Yes == suppressRepair) { symbol.SuppressRepair = 1; } if (YesNoType.Yes == showVersion) { symbol.ShowVersion = 1; } if (YesNoType.Yes == supportCacheOnly) { symbol.SupportCacheOnly = 1; } } switch (theme) { case WixStandardBootstrapperApplicationTheme.HyperlinkLargeLicense: this.CreatePayloadGroupRef(section, sourceLineNumbers, node, "WixStdbaHyperlinkLargeLicensePayloads", platformSpecific: false); break; case WixStandardBootstrapperApplicationTheme.HyperlinkLicense: this.CreatePayloadGroupRef(section, sourceLineNumbers, node, "WixStdbaHyperlinkLicensePayloads", platformSpecific: false); break; case WixStandardBootstrapperApplicationTheme.HyperlinkSidebarLicense: this.CreatePayloadGroupRef(section, sourceLineNumbers, node, "WixStdbaHyperlinkSidebarLicensePayloads", platformSpecific: false); break; case WixStandardBootstrapperApplicationTheme.RtfLargeLicense: this.CreatePayloadGroupRef(section, sourceLineNumbers, node, "WixStdbaRtfLargeLicensePayloads", platformSpecific: false); break; case WixStandardBootstrapperApplicationTheme.RtfLicense: this.CreatePayloadGroupRef(section, sourceLineNumbers, node, "WixStdbaRtfLicensePayloads", platformSpecific: false); break; } section.AddSymbol(new WixBalBootstrapperApplicationSymbol(sourceLineNumbers) { Type = WixBalBootstrapperApplicationType.Standard, }); var exePayloadId = this.CreatePayloadGroupRef(section, sourceLineNumbers, node, "WixStandardBootstrapperApplication", platformSpecific: true); exePayloadRef = this.CreateComponentKeyPath(); exePayloadRef.Id = new Identifier(AccessModifier.Section, exePayloadId); exePayloadRef.Type = PossibleKeyPathType.File; } return exePayloadRef; } /// /// Parses a WixManagedBootstrapperApplicationHost element for Bundles. /// /// The element to parse. private void ParseWixPrerequisiteBootstrapperApplicationElement(Intermediate intermediate, IntermediateSection section, XElement node, IDictionary context) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); string logoFile = null; string themeFile = null; string localizationFile = null; var theme = WixPrerequisiteBootstrapperApplicationTheme.Standard; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "LogoFile": logoFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "ThemeFile": themeFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "LocalizationFile": localizationFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Theme": var themeValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (themeValue) { case "none": theme = WixPrerequisiteBootstrapperApplicationTheme.None; break; case "standard": theme = WixPrerequisiteBootstrapperApplicationTheme.Standard; break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Theme", themeValue, "none", "standard")); theme = WixPrerequisiteBootstrapperApplicationTheme.Unknown; break; } break; default: this.ParseHelper.UnexpectedAttribute(node, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib); } } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node); if (!this.Messaging.EncounteredError) { if (!String.IsNullOrEmpty(logoFile)) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixPreqbaLogo")) { Value = logoFile, }); } if (!String.IsNullOrEmpty(themeFile)) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixPreqbaThemeXml")) { Value = themeFile, }); } if (!String.IsNullOrEmpty(localizationFile)) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixPreqbaThemeWxl")) { Value = localizationFile, }); } switch (theme) { case WixPrerequisiteBootstrapperApplicationTheme.Standard: this.CreatePayloadGroupRef(section, sourceLineNumbers, node, "WixPreqbaStandardPayloads", platformSpecific: false); break; } section.AddSymbol(new WixBalBootstrapperApplicationSymbol(sourceLineNumbers) { Type = WixBalBootstrapperApplicationType.Prerequisite, }); var primary = context.TryGetValue("Secondary", out var parentSecondaryValue) && "True".Equals(parentSecondaryValue, StringComparison.OrdinalIgnoreCase) ? true : false; var baId = this.CreateIdentifierFromPlatform(sourceLineNumbers, node, primary ? "WixPrereqBootstrapperApplication.Primary" : "WixPrereqBootstrapperApplication.Secondary"); if (!String.IsNullOrEmpty(baId)) { this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.WixBootstrapperApplication, baId); } if (primary) { section.AddSymbol(new WixPrereqOptionsSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixPrereqOptions")) { Primary = 1 }); } } } #if DELETE /// /// Parses a WixManagedBootstrapperApplicationHost element for Bundles. /// /// The element to parse. private IComponentKeyPath ParseWixManagedBootstrapperApplicationHostElement(Intermediate intermediate, IntermediateSection section, XElement node) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); bool alwaysInstallPrereqs = false; string logoFile = null; string themeFile = null; string localizationFile = null; WixManagedBootstrapperApplicationHostTheme? theme = null; IComponentKeyPath exePayloadRef = null; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "AlwaysInstallPrereqs": alwaysInstallPrereqs = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib) == YesNoType.Yes; break; case "LogoFile": logoFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "ThemeFile": themeFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "LocalizationFile": localizationFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Theme": var themeValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (themeValue) { case "none": theme = WixManagedBootstrapperApplicationHostTheme.None; break; case "standard": theme = WixManagedBootstrapperApplicationHostTheme.Standard; break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Theme", themeValue, "none", "standard")); theme = WixManagedBootstrapperApplicationHostTheme.Unknown; break; } break; default: this.ParseHelper.UnexpectedAttribute(node, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib); } } if (!theme.HasValue) { theme = WixManagedBootstrapperApplicationHostTheme.Standard; } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node); if (!this.Messaging.EncounteredError) { if (!String.IsNullOrEmpty(logoFile)) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "PreqbaLogo")) { Value = logoFile, }); } if (!String.IsNullOrEmpty(themeFile)) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "PreqbaThemeXml")) { Value = themeFile, }); } if (!String.IsNullOrEmpty(localizationFile)) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "PreqbaThemeWxl")) { Value = localizationFile, }); } var baId = "WixManagedBootstrapperApplicationHost"; switch (theme) { case WixManagedBootstrapperApplicationHostTheme.Standard: baId = "WixManagedBootstrapperApplicationHost.Standard"; break; } exePayloadRef = this.CreatePayloadGroupRef(section, sourceLineNumbers, node, "WixManagedBootstrapperApplicationHost", baId, WixBalBootstrapperApplicationType.ManagedHost); if (alwaysInstallPrereqs) { section.AddSymbol(new WixPrereqOptionsSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixPrereqOptions")) { AlwaysInstallPrereqs = 1, }); } } return exePayloadRef; } /// /// Parses a WixDotNetCoreBootstrapperApplication element for Bundles. /// /// The element to parse. private IComponentKeyPath ParseWixDotNetCoreBootstrapperApplicationHostElement(Intermediate intermediate, IntermediateSection section, XElement node) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); bool alwaysInstallPrereqs = false; string logoFile = null; string themeFile = null; string localizationFile = null; var selfContainedDeployment = YesNoType.NotSet; WixDotNetCoreBootstrapperApplicationHostTheme? theme = null; IComponentKeyPath exePayloadRef = null; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "AlwaysInstallPrereqs": alwaysInstallPrereqs = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib) == YesNoType.Yes; break; case "LogoFile": logoFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "ThemeFile": themeFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "LocalizationFile": localizationFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "SelfContainedDeployment": selfContainedDeployment = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "Theme": var themeValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (themeValue) { case "none": theme = WixDotNetCoreBootstrapperApplicationHostTheme.None; break; case "standard": theme = WixDotNetCoreBootstrapperApplicationHostTheme.Standard; break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Theme", themeValue, "none", "standard")); theme = WixDotNetCoreBootstrapperApplicationHostTheme.Unknown; break; } break; default: this.ParseHelper.UnexpectedAttribute(node, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib); } } if (!theme.HasValue) { theme = WixDotNetCoreBootstrapperApplicationHostTheme.Standard; } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node); if (!this.Messaging.EncounteredError) { if (!String.IsNullOrEmpty(logoFile)) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "DncPreqbaLogo")) { Value = logoFile, }); } if (!String.IsNullOrEmpty(themeFile)) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "DncPreqbaThemeXml")) { Value = themeFile, }); } if (!String.IsNullOrEmpty(localizationFile)) { section.AddSymbol(new WixVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "DncPreqbaThemeWxl")) { Value = localizationFile, }); } if (YesNoType.Yes == selfContainedDeployment) { section.AddSymbol(new WixDncOptionsSymbol(sourceLineNumbers) { SelfContainedDeployment = 1, }); } var baId = "WixDotNetCoreBootstrapperApplicationHost"; switch (theme) { case WixDotNetCoreBootstrapperApplicationHostTheme.Standard: baId = "WixDotNetCoreBootstrapperApplicationHost.Standard"; break; } exePayloadRef = this.CreatePayloadGroupRef(section, sourceLineNumbers, node, "WixDotNetCoreBootstrapperApplicationHost", baId, WixBalBootstrapperApplicationType.DotNetCoreHost); if (alwaysInstallPrereqs) { section.AddSymbol(new WixPrereqOptionsSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, "WixPrereqOptions")) { AlwaysInstallPrereqs = 1, }); } } return exePayloadRef; } #endif private string CreatePayloadGroupRef(IntermediateSection section, SourceLineNumber sourceLineNumbers, XElement node, string basePayloadGroupId, bool platformSpecific) { var id = platformSpecific ? this.CreateIdentifierFromPlatform(sourceLineNumbers, node, basePayloadGroupId) : basePayloadGroupId; if (!String.IsNullOrEmpty(id)) { this.ParseHelper.CreateWixGroupSymbol(section, sourceLineNumbers, ComplexReferenceParentType.Container, BurnConstants.BurnUXContainerName, ComplexReferenceChildType.PayloadGroup, id); this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.WixBundlePayloadGroup, id); } return id; } private string CreateIdentifierFromPlatform(SourceLineNumber sourceLineNumbers, XElement node, string name) { var id = this.ParseHelper.CreateIdentifierValueFromPlatform(name, this.Context.Platform, BurnPlatforms.X86 | BurnPlatforms.X64 | BurnPlatforms.ARM64); if (id == null) { this.Messaging.Write(ErrorMessages.UnsupportedPlatformForElement(sourceLineNumbers, this.Context.Platform.ToString(), node.Name.LocalName)); } return id; } } }