// 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
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using WixToolset.Data;
using WixToolset.Data.Burn;
using WixToolset.Data.Symbols;
using WixToolset.Extensibility;
///
/// Compiler of the WiX toolset.
///
internal partial class Compiler : ICompiler
{
private static readonly Identifier BurnUXContainerId = new Identifier(AccessModifier.Private, BurnConstants.BurnUXContainerName);
private static readonly Identifier BurnDefaultAttachedContainerId = new Identifier(AccessModifier.Private, BurnConstants.BurnDefaultAttachedContainerName);
private static readonly Identifier BundleLayoutOnlyPayloads = new Identifier(AccessModifier.Private, BurnConstants.BundleLayoutOnlyPayloadsName);
///
/// Parses an ApprovedExeForElevation element.
///
/// Element to parse
private void ParseApprovedExeForElevation(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier id = null;
string key = null;
string valueName = null;
var win64 = YesNoType.NotSet;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
break;
case "Key":
key = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Value":
valueName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Win64":
win64 = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == id)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
}
if (null == key)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
}
var attributes = WixApprovedExeForElevationAttributes.None;
if (win64 == YesNoType.Yes)
{
attributes |= WixApprovedExeForElevationAttributes.Win64;
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new WixApprovedExeForElevationSymbol(sourceLineNumbers, id)
{
Key = key,
ValueName = valueName,
Attributes = attributes,
});
}
}
///
/// Parses a Bundle element.
///
/// Element to parse
private void ParseBundleElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string copyright = null;
string aboutUrl = null;
var compressed = YesNoDefaultType.Default;
WixBundleAttributes attributes = 0;
string helpTelephone = null;
string helpUrl = null;
string manufacturer = null;
string name = null;
string tag = null;
string updateUrl = null;
string upgradeCode = null;
string version = null;
string condition = null;
string parentName = null;
string fileSystemSafeBundleName = null;
string logVariablePrefixAndExtension = null;
string iconSourceFile = null;
string splashScreenSourceFile = null;
// Process only standard attributes until the active section is initialized.
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "AboutUrl":
aboutUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Compressed":
compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
break;
case "Condition":
condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Copyright":
copyright = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "DisableModify":
var value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
switch (value)
{
case "button":
attributes |= WixBundleAttributes.SingleChangeUninstallButton;
break;
case "yes":
attributes |= WixBundleAttributes.DisableModify;
break;
case "no":
break;
default:
this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, value, "button", "yes", "no"));
break;
}
break;
case "DisableRemove":
if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
{
attributes |= WixBundleAttributes.DisableRemove;
}
break;
case "HelpTelephone":
helpTelephone = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "HelpUrl":
helpUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Manufacturer":
manufacturer = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "IconSourceFile":
iconSourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Name":
name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "ParentName":
parentName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "SplashScreenSourceFile":
splashScreenSourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Tag":
tag = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "UpdateUrl":
updateUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "UpgradeCode":
upgradeCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
break;
case "Version":
version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
}
if (String.IsNullOrEmpty(version))
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
}
else if (!CompilerCore.IsValidModuleOrBundleVersion(version))
{
this.Core.Write(WarningMessages.InvalidModuleOrBundleVersion(sourceLineNumbers, "Bundle", version));
}
if (String.IsNullOrEmpty(upgradeCode))
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "UpgradeCode"));
}
if (String.IsNullOrEmpty(copyright))
{
if (String.IsNullOrEmpty(manufacturer))
{
copyright = "Copyright (c). All rights reserved.";
}
else
{
copyright = String.Format("Copyright (c) {0}. All rights reserved.", manufacturer);
}
}
if (String.IsNullOrEmpty(name))
{
logVariablePrefixAndExtension = String.Concat("WixBundleLog:Setup:log");
}
else
{
// Ensure only allowable path characters are in "name" (and change spaces to underscores).
fileSystemSafeBundleName = CompilerCore.MakeValidLongFileName(name.Replace(' ', '_'), '_');
logVariablePrefixAndExtension = String.Concat("WixBundleLog:", fileSystemSafeBundleName, ":log");
}
this.activeName = String.IsNullOrEmpty(name) ? Common.GenerateGuid() : name;
this.Core.CreateActiveSection(this.activeName, SectionType.Bundle, 0, this.Context.CompilationId);
// Now that the active section is initialized, process only extension attributes.
foreach (var attrib in node.Attributes())
{
if (!String.IsNullOrEmpty(attrib.Name.NamespaceName) && CompilerCore.WixNamespace != attrib.Name.Namespace)
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
var baSeen = false;
var chainSeen = false;
var logSeen = false;
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "ApprovedExeForElevation":
this.ParseApprovedExeForElevation(child);
break;
case "BootstrapperApplication":
if (baSeen)
{
var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "BootstrapperApplication"));
}
this.ParseBootstrapperApplicationElement(child);
baSeen = true;
break;
case "BootstrapperApplicationRef":
this.ParseBootstrapperApplicationRefElement(child);
break;
case "BundleCustomData":
this.ParseBundleCustomDataElement(child);
break;
case "BundleCustomDataRef":
this.ParseBundleCustomDataRefElement(child);
break;
case "BundleExtension":
this.ParseBundleExtensionElement(child);
break;
case "BundleExtensionRef":
this.ParseSimpleRefElement(child, SymbolDefinitions.WixBundleExtension);
break;
case "OptionalUpdateRegistration":
this.ParseOptionalUpdateRegistrationElement(child, manufacturer, parentName, name);
break;
case "Catalog":
this.ParseCatalogElement(child);
break;
case "Chain":
if (chainSeen)
{
var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "Chain"));
}
this.ParseChainElement(child);
chainSeen = true;
break;
case "Container":
this.ParseContainerElement(child);
break;
case "ContainerRef":
this.ParseSimpleRefElement(child, SymbolDefinitions.WixBundleContainer);
break;
case "Log":
if (logSeen)
{
var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "Log"));
}
logVariablePrefixAndExtension = this.ParseLogElement(child, fileSystemSafeBundleName);
logSeen = true;
break;
case "PayloadGroup":
this.ParsePayloadGroupElement(child, ComplexReferenceParentType.Layout, Compiler.BundleLayoutOnlyPayloads);
break;
case "PayloadGroupRef":
this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Layout, Compiler.BundleLayoutOnlyPayloads, ComplexReferenceChildType.Unknown, null);
break;
case "RelatedBundle":
this.ParseRelatedBundleElement(child);
break;
case "SetVariable":
this.ParseSetVariableElement(child);
break;
case "SetVariableRef":
this.ParseSimpleRefElement(child, SymbolDefinitions.WixSetVariable);
break;
case "Update":
this.ParseUpdateElement(child);
break;
case "Variable":
this.ParseVariableElement(child);
break;
case "WixVariable":
this.ParseWixVariableElement(child);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (!chainSeen)
{
this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "Chain"));
}
if (!this.Core.EncounteredError)
{
var symbol = this.Core.AddSymbol(new WixBundleSymbol(sourceLineNumbers)
{
UpgradeCode = upgradeCode,
Version = version,
Copyright = copyright,
Name = name,
Manufacturer = manufacturer,
Attributes = attributes,
AboutUrl = aboutUrl,
HelpUrl = helpUrl,
HelpTelephone = helpTelephone,
UpdateUrl = updateUrl,
Compressed = YesNoDefaultType.Yes == compressed ? true : YesNoDefaultType.No == compressed ? (bool?)false : null,
IconSourceFile = iconSourceFile,
SplashScreenSourceFile = splashScreenSourceFile,
Condition = condition,
Tag = tag,
Platform = this.CurrentPlatform,
ParentName = parentName,
});
if (!String.IsNullOrEmpty(logVariablePrefixAndExtension))
{
var split = logVariablePrefixAndExtension.Split(':');
symbol.LogPathVariable = split[0];
symbol.LogPrefix = split[1];
symbol.LogExtension = split[2];
}
if (null != upgradeCode)
{
this.Core.AddSymbol(new WixRelatedBundleSymbol(sourceLineNumbers)
{
BundleId = upgradeCode,
Action = RelatedBundleActionType.Upgrade,
});
}
this.Core.AddSymbol(new WixBundleContainerSymbol(sourceLineNumbers, Compiler.BurnDefaultAttachedContainerId)
{
Name = "bundle-attached.cab",
Type = ContainerType.Attached,
});
// Ensure that the bundle stores the well-known persisted values.
this.Core.AddSymbol(new WixBundleVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Private, BurnConstants.BURN_BUNDLE_NAME))
{
Hidden = false,
Persisted = true,
});
this.Core.AddSymbol(new WixBundleVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Private, BurnConstants.BURN_BUNDLE_ORIGINAL_SOURCE))
{
Hidden = false,
Persisted = true,
});
this.Core.AddSymbol(new WixBundleVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Private, BurnConstants.BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER))
{
Hidden = false,
Persisted = true,
});
this.Core.AddSymbol(new WixBundleVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Private, BurnConstants.BURN_BUNDLE_LAST_USED_SOURCE))
{
Hidden = false,
Persisted = true,
});
}
}
///
/// Parse a Container element.
///
/// Element to parse
///
private string ParseLogElement(XElement node, string fileSystemSafeBundleName)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
var disableLog = YesNoType.NotSet;
var variable = "WixBundleLog";
var logPrefix = fileSystemSafeBundleName ?? "Setup";
var logExtension = ".log";
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Disable":
disableLog = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "PathVariable":
variable = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
break;
case "Prefix":
logPrefix = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Extension":
logExtension = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (!logExtension.StartsWith(".", StringComparison.Ordinal))
{
logExtension = String.Concat(".", logExtension);
}
this.Core.ParseForExtensionElements(node);
return YesNoType.Yes == disableLog ? null : String.Join(":", variable, logPrefix, logExtension);
}
///
/// Parse a Catalog element.
///
/// Element to parse
private void ParseCatalogElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier id = null;
string sourceFile = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
break;
case "SourceFile":
sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
}
if (null == id)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
}
if (null == sourceFile)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
}
this.Core.ParseForExtensionElements(node);
// Create catalog row
if (!this.Core.EncounteredError)
{
this.CreatePayloadRow(sourceLineNumbers, id, Path.GetFileName(sourceFile), sourceFile, null, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, ComplexReferenceChildType.Unknown, null, YesNoDefaultType.Yes, YesNoType.Yes, null, null, null);
this.Core.AddSymbol(new WixBundleCatalogSymbol(sourceLineNumbers, id)
{
PayloadRef = id.Id,
});
}
}
///
/// Parse a Container element.
///
/// Element to parse
private void ParseContainerElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier id = null;
string downloadUrl = null;
string name = null;
var type = ContainerType.Detached;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
break;
case "DownloadUrl":
downloadUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Name":
name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Type":
var typeString = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
switch (typeString)
{
case "attached":
type = ContainerType.Attached;
break;
case "detached":
type = ContainerType.Detached;
break;
default:
this.Core.Write(ErrorMessages.IllegalAttributeValueWithLegalList(sourceLineNumbers, node.Name.LocalName, "Type", typeString, "attached, detached"));
break;
}
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == id)
{
if (!String.IsNullOrEmpty(name))
{
id = this.Core.CreateIdentifierFromFilename(name);
}
if (null == id)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
id = Identifier.Invalid;
}
else if (!Common.IsIdentifier(id.Id))
{
this.Core.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
}
}
else if (null == name)
{
name = id.Id;
}
if (!String.IsNullOrEmpty(downloadUrl) && ContainerType.Detached != type)
{
this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DownloadUrl", "Type", "attached"));
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "PackageGroupRef":
this.ParsePackageGroupRefElement(child, ComplexReferenceParentType.Container, id.Id);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new WixBundleContainerSymbol(sourceLineNumbers, id)
{
Name = name,
Type = type,
DownloadUrl = downloadUrl
});
}
}
///
/// Parse the BoostrapperApplication element.
///
/// Element to parse
private void ParseBootstrapperApplicationElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier id = null;
Identifier previousId = null;
var previousType = ComplexReferenceChildType.Unknown;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "BootstrapperApplicationDll":
previousId = this.ParseBootstrapperApplicationDllElement(child, previousType, previousId);
previousType = ComplexReferenceChildType.Payload;
break;
case "Payload":
previousId = this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId);
previousType = ComplexReferenceChildType.Payload;
break;
case "PayloadGroupRef":
previousId = this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId);
previousType = ComplexReferenceChildType.PayloadGroup;
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (id != null)
{
this.Core.AddSymbol(new WixBootstrapperApplicationSymbol(sourceLineNumbers, id));
}
}
///
/// Parse the BoostrapperApplication element.
///
/// Element to parse
///
///
private Identifier ParseBootstrapperApplicationDllElement(XElement node, ComplexReferenceChildType previousType, Identifier previousId)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
var dpiAwareness = WixBootstrapperApplicationDpiAwarenessType.PerMonitorV2;
// The BootstrapperApplicationDll element acts like a Payload element so delegate to the "Payload" attribute parsing code to parse and create a Payload entry.
this.ParsePayloadElementContent(node, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId, true, out var id);
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "DpiAwareness":
var dpiAwarenessValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
switch (dpiAwarenessValue)
{
case "gdiScaled":
dpiAwareness = WixBootstrapperApplicationDpiAwarenessType.GdiScaled;
break;
case "perMonitor":
dpiAwareness = WixBootstrapperApplicationDpiAwarenessType.PerMonitor;
break;
case "perMonitorV2":
dpiAwareness = WixBootstrapperApplicationDpiAwarenessType.PerMonitorV2;
break;
case "system":
dpiAwareness = WixBootstrapperApplicationDpiAwarenessType.System;
break;
case "unaware":
dpiAwareness = WixBootstrapperApplicationDpiAwarenessType.Unaware;
break;
default:
this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "DpiAwareness", dpiAwarenessValue, "gdiScaled", "perMonitor", "perMonitorV2", "system", "unaware"));
break;
}
break;
}
}
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new WixBundleContainerSymbol(sourceLineNumbers, Compiler.BurnUXContainerId)
{
Name = "bundle-ux.cab",
Type = ContainerType.Attached
});
this.Core.AddSymbol(new WixBootstrapperApplicationDllSymbol(sourceLineNumbers, id)
{
DpiAwareness = dpiAwareness,
});
}
return id;
}
///
/// Parse the BoostrapperApplicationRef element.
///
/// Element to parse
private void ParseBootstrapperApplicationRefElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string id = null;
Identifier previousId = null;
var previousType = ComplexReferenceChildType.Unknown;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "Payload":
previousId = this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId);
previousType = ComplexReferenceChildType.Payload;
break;
case "PayloadGroupRef":
previousId = this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId);
previousType = ComplexReferenceChildType.PayloadGroup;
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (String.IsNullOrEmpty(id))
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
}
else
{
this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixBootstrapperApplication, id);
}
}
///
/// Parses a BundleCustomData element.
///
/// Element to parse.
private void ParseBundleCustomDataElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string customDataId = null;
WixBundleCustomDataType? customDataType = null;
string extensionId = null;
var attributeDefinitions = new List();
var foundAttributeDefinitions = false;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
customDataId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
break;
case "Type":
var typeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
switch (typeValue)
{
case "bootstrapperApplication":
customDataType = WixBundleCustomDataType.BootstrapperApplication;
break;
case "bundleExtension":
customDataType = WixBundleCustomDataType.BundleExtension;
break;
default:
this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Type", typeValue, "bootstrapperApplication", "bundleExtension"));
customDataType = WixBundleCustomDataType.Unknown; // set a value to prevent expected attribute error below.
break;
}
break;
case "ExtensionId":
extensionId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixBundleExtension, extensionId);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == customDataId)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
}
var hasExtensionId = null != extensionId;
if (!customDataType.HasValue)
{
customDataType = hasExtensionId ? WixBundleCustomDataType.BundleExtension : WixBundleCustomDataType.BootstrapperApplication;
}
if (!customDataType.HasValue)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Type"));
}
else if (hasExtensionId)
{
if (customDataType.Value == WixBundleCustomDataType.BootstrapperApplication)
{
this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "ExtensonId", "Type", "bootstrapperApplication"));
}
}
else if (customDataType.Value == WixBundleCustomDataType.BundleExtension)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ExtensionId", "Type", "bundleExtension"));
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
switch (child.Name.LocalName)
{
case "BundleAttributeDefinition":
foundAttributeDefinitions = true;
var attributeDefinition = this.ParseBundleAttributeDefinitionElement(child, childSourceLineNumbers, customDataId);
if (attributeDefinition != null)
{
attributeDefinitions.Add(attributeDefinition);
}
break;
case "BundleElement":
this.ParseBundleElementElement(child, childSourceLineNumbers, customDataId);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (attributeDefinitions.Count > 0)
{
if (!this.Core.EncounteredError)
{
var attributeNames = String.Join(new string(WixBundleCustomDataSymbol.AttributeNamesSeparator, 1), attributeDefinitions.Select(c => c.Name));
this.Core.AddSymbol(new WixBundleCustomDataSymbol(sourceLineNumbers, new Identifier(AccessModifier.Public, customDataId))
{
AttributeNames = attributeNames,
Type = customDataType.Value,
BundleExtensionRef = extensionId,
});
}
}
else if (!foundAttributeDefinitions)
{
this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "BundleAttributeDefinition"));
}
}
///
/// Parses a BundleCustomDataRef element.
///
/// Element to parse.
private void ParseBundleCustomDataRefElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string customDataId = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
customDataId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixBundleCustomData, customDataId);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == customDataId)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
switch (child.Name.LocalName)
{
case "BundleElement":
this.ParseBundleElementElement(child, childSourceLineNumbers, customDataId);
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
}
///
/// Parses a BundleAttributeDefinition element.
///
/// Element to parse.
/// Element's SourceLineNumbers.
/// BundleCustomData Id.
private WixBundleCustomDataAttributeSymbol ParseBundleAttributeDefinitionElement(XElement node, SourceLineNumber sourceLineNumbers, string customDataId)
{
string attributeName = null;
foreach (var attrib in node.Attributes())
{
switch (attrib.Name.LocalName)
{
case "Id":
attributeName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
if (null == attributeName)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
}
this.Core.ParseForExtensionElements(node);
if (this.Core.EncounteredError)
{
return null;
}
var customDataAttribute = this.Core.AddSymbol(new WixBundleCustomDataAttributeSymbol(sourceLineNumbers, new Identifier(AccessModifier.Private, customDataId, attributeName))
{
CustomDataRef = customDataId,
Name = attributeName,
});
return customDataAttribute;
}
///
/// Parses a BundleElement element.
///
/// Element to parse.
/// Element's SourceLineNumbers.
/// BundleCustomData Id.
private void ParseBundleElementElement(XElement node, SourceLineNumber sourceLineNumbers, string customDataId)
{
var elementId = Guid.NewGuid().ToString("N").ToUpperInvariant();
foreach (var attrib in node.Attributes())
{
this.Core.ParseExtensionAttribute(node, attrib);
}
foreach (var child in node.Elements())
{
var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
switch (child.Name.LocalName)
{
case "BundleAttribute":
string attributeName = null;
string value = null;
foreach (var attrib in child.Attributes())
{
switch (attrib.Name.LocalName)
{
case "Id":
attributeName = this.Core.GetAttributeValue(childSourceLineNumbers, attrib);
break;
case "Value":
value = this.Core.GetAttributeValue(childSourceLineNumbers, attrib);
break;
default:
this.Core.ParseExtensionAttribute(child, attrib);
break;
}
}
if (null == attributeName)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Id"));
}
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new WixBundleCustomDataCellSymbol(childSourceLineNumbers, new Identifier(AccessModifier.Private, customDataId, elementId, attributeName))
{
ElementId = elementId,
AttributeRef = attributeName,
CustomDataRef = customDataId,
Value = value,
});
}
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
if (!this.Core.EncounteredError)
{
this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixBundleCustomData, customDataId);
}
}
///
/// Parse the BundleExtension element.
///
/// Element to parse
private void ParseBundleExtensionElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier previousId = null;
var previousType = ComplexReferenceChildType.Unknown;
// The BundleExtension element acts like a Payload element so delegate to the "Payload" attribute parsing code to parse and create a Payload entry.
if (this.ParsePayloadElementContent(node, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId, true, out var id))
{
previousId = id;
previousType = ComplexReferenceChildType.Payload;
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "Payload":
previousId = this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId);
previousType = ComplexReferenceChildType.Payload;
break;
case "PayloadGroupRef":
previousId = this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId);
previousType = ComplexReferenceChildType.PayloadGroup;
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
// Add the BundleExtension.
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new WixBundleExtensionSymbol(sourceLineNumbers, id)
{
PayloadRef = id.Id,
});
}
}
///
/// Parse the OptionalUpdateRegistration element.
///
/// The element to parse.
/// The manufacturer.
/// The product family.
/// The bundle name.
private void ParseOptionalUpdateRegistrationElement(XElement node, string defaultManufacturer, string defaultProductFamily, string defaultName)
{
const string defaultClassification = "Update";
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string manufacturer = null;
string department = null;
string productFamily = null;
string name = null;
var classification = defaultClassification;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Manufacturer":
manufacturer = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Department":
department = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "ProductFamily":
productFamily = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Name":
name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Classification":
classification = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (String.IsNullOrEmpty(manufacturer))
{
if (!String.IsNullOrEmpty(defaultManufacturer))
{
manufacturer = defaultManufacturer;
}
else
{
this.Core.Write(ErrorMessages.ExpectedAttributeInElementOrParent(sourceLineNumbers, node.Name.LocalName, "Manufacturer", node.Parent.Name.LocalName));
}
}
if (String.IsNullOrEmpty(productFamily))
{
if (!String.IsNullOrEmpty(defaultProductFamily))
{
productFamily = defaultProductFamily;
}
}
if (String.IsNullOrEmpty(name))
{
if (!String.IsNullOrEmpty(defaultName))
{
name = defaultName;
}
else
{
this.Core.Write(ErrorMessages.ExpectedAttributeInElementOrParent(sourceLineNumbers, node.Name.LocalName, "Name", node.Parent.Name.LocalName));
}
}
if (String.IsNullOrEmpty(classification))
{
this.Core.Write(ErrorMessages.IllegalEmptyAttributeValue(sourceLineNumbers, node.Name.LocalName, "Classification", defaultClassification));
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new WixUpdateRegistrationSymbol(sourceLineNumbers)
{
Manufacturer = manufacturer,
Department = department,
ProductFamily = productFamily,
Name = name,
Classification = classification
});
}
}
///
/// Parse Payload element.
///
/// Element to parse
/// ComplexReferenceParentType of parent element. (BA or PayloadGroup)
/// Identifier of parent element.
///
///
private Identifier ParsePayloadElement(XElement node, ComplexReferenceParentType parentType, Identifier parentId, ComplexReferenceChildType previousType, Identifier previousId)
{
Debug.Assert(ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType);
Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PayloadGroup == previousType || ComplexReferenceChildType.Payload == previousType);
this.ParsePayloadElementContent(node, parentType, parentId, previousType, previousId, true, out var id);
var context = new Dictionary
{
["Id"] = id?.Id
};
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child, context);
}
}
return id;
}
///
/// Parse the attributes of the Payload element.
///
/// Element to parse
/// ComplexReferenceParentType of parent element.
/// Identifier of parent element.
///
///
///
///
/// Whether SourceFile was specified.
private bool ParsePayloadElementContent(XElement node, ComplexReferenceParentType parentType, Identifier parentId, ComplexReferenceChildType previousType, Identifier previousId, bool required, out Identifier id)
{
Debug.Assert(ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType);
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
var compressed = YesNoDefaultType.Default;
var enableSignatureVerification = YesNoType.No;
id = null;
string name = null;
string sourceFile = null;
string downloadUrl = null;
// This list lets us evaluate extension attributes *after* all core attributes
// have been parsed and dealt with, regardless of authoring order.
var extensionAttributes = new List();
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
break;
case "Compressed":
compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
break;
case "Name":
name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false, true);
break;
case "SourceFile":
sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "DownloadUrl":
downloadUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "EnableSignatureVerification":
enableSignatureVerification = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "DpiAwareness":
if (node.Name.LocalName != "BootstrapperApplication")
{
this.Core.UnexpectedAttribute(node, attrib);
}
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
extensionAttributes.Add(attrib);
}
}
if (null == id)
{
id = this.Core.CreateIdentifier("pay", sourceFile?.ToUpperInvariant() ?? String.Empty);
}
// Now that the PayloadId is known, we can parse the extension attributes.
var context = new Dictionary
{
["Id"] = id.Id
};
foreach (var extensionAttribute in extensionAttributes)
{
this.Core.ParseExtensionAttribute(node, extensionAttribute, context);
}
// Let caller handle the children.
if (Compiler.BurnUXContainerId == parentId)
{
if (compressed == YesNoDefaultType.No)
{
this.Core.Write(WarningMessages.UxPayloadsOnlySupportEmbedding(sourceLineNumbers, sourceFile));
}
compressed = YesNoDefaultType.Yes;
}
if (sourceFile == null)
{
if (required)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
}
return false;
}
this.CreatePayloadRow(sourceLineNumbers, id, name, sourceFile, downloadUrl, parentType, parentId, previousType, previousId, compressed, enableSignatureVerification, null, null, null);
return true;
}
private RemotePayload ParseRemotePayloadElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
var remotePayload = new RemotePayload();
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "CertificatePublicKey":
remotePayload.CertificatePublicKey = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "CertificateThumbprint":
remotePayload.CertificateThumbprint = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Description":
remotePayload.Description = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Hash":
remotePayload.Hash = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "ProductName":
remotePayload.ProductName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Size":
remotePayload.Size = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
break;
case "Version":
remotePayload.Version = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (String.IsNullOrEmpty(remotePayload.ProductName))
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ProductName"));
}
if (String.IsNullOrEmpty(remotePayload.Description))
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Description"));
}
if (String.IsNullOrEmpty(remotePayload.Hash))
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Hash"));
}
if (0 == remotePayload.Size)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Size"));
}
if (String.IsNullOrEmpty(remotePayload.Version))
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
}
return remotePayload;
}
///
/// Creates the row for a Payload.
///
///
///
///
///
///
/// ComplexReferenceParentType of parent element
/// Identifier of parent element.
///
///
///
///
///
///
///
///
private WixBundlePayloadSymbol CreatePayloadRow(SourceLineNumber sourceLineNumbers, Identifier id, string name, string sourceFile, string downloadUrl, ComplexReferenceParentType parentType,
Identifier parentId, ComplexReferenceChildType previousType, Identifier previousId, YesNoDefaultType compressed, YesNoType enableSignatureVerification, string displayName, string description,
RemotePayload remotePayload)
{
WixBundlePayloadSymbol symbol = null;
if (!this.Core.EncounteredError)
{
symbol = this.Core.AddSymbol(new WixBundlePayloadSymbol(sourceLineNumbers, id)
{
Name = String.IsNullOrEmpty(name) ? Path.GetFileName(sourceFile) : name,
SourceFile = new IntermediateFieldPathValue { Path = sourceFile },
DownloadUrl = downloadUrl,
Compressed = (compressed == YesNoDefaultType.Yes) ? true : (compressed == YesNoDefaultType.No) ? (bool?)false : null,
UnresolvedSourceFile = sourceFile, // duplicate of sourceFile but in a string column so it won't get resolved to a full path during binding.
DisplayName = displayName,
Description = description,
EnableSignatureValidation = (YesNoType.Yes == enableSignatureVerification)
});
if (null != remotePayload)
{
symbol.Description = remotePayload.Description;
symbol.DisplayName = remotePayload.ProductName;
symbol.Hash = remotePayload.Hash;
symbol.PublicKey = remotePayload.CertificatePublicKey;
symbol.Thumbprint = remotePayload.CertificateThumbprint;
symbol.FileSize = remotePayload.Size;
symbol.Version = remotePayload.Version;
}
this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId.Id, ComplexReferenceChildType.Payload, id.Id, previousType, previousId?.Id);
}
return symbol;
}
///
/// Parse PayloadGroup element.
///
/// Element to parse
/// Optional ComplexReferenceParentType of parent element. (typically another PayloadGroup)
/// Identifier of parent element.
private void ParsePayloadGroupElement(XElement node, ComplexReferenceParentType parentType, Identifier parentId)
{
Debug.Assert(ComplexReferenceParentType.Unknown == parentType || ComplexReferenceParentType.Layout == parentType || ComplexReferenceParentType.PayloadGroup == parentType);
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier id = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == id)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
id = Identifier.Invalid;
}
var previousType = ComplexReferenceChildType.Unknown;
Identifier previousId = null;
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "Payload":
previousId = this.ParsePayloadElement(child, ComplexReferenceParentType.PayloadGroup, id, previousType, previousId);
previousType = ComplexReferenceChildType.Payload;
break;
case "PayloadGroupRef":
previousId = this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.PayloadGroup, id, previousType, previousId);
previousType = ComplexReferenceChildType.PayloadGroup;
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new WixBundlePayloadGroupSymbol(sourceLineNumbers, id));
this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId?.Id, ComplexReferenceChildType.PayloadGroup, id.Id, ComplexReferenceChildType.Unknown, null);
}
}
///
/// Parses a payload group reference element.
///
/// Element to parse.
/// ComplexReferenceParentType of parent element (BA or PayloadGroup).
/// Identifier of parent element.
///
///
private Identifier ParsePayloadGroupRefElement(XElement node, ComplexReferenceParentType parentType, Identifier parentId, ComplexReferenceChildType previousType, Identifier previousId)
{
Debug.Assert(ComplexReferenceParentType.Layout == parentType || ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType);
Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PayloadGroup == previousType || ComplexReferenceChildType.Payload == previousType);
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier id = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixBundlePayloadGroup, id.Id);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == id)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
}
this.Core.ParseForExtensionElements(node);
this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId?.Id, ComplexReferenceChildType.PayloadGroup, id?.Id, previousType, previousId?.Id);
return id;
}
///
/// Creates group and ordering information.
///
/// Source line numbers.
/// Type of parent group, if known.
/// Identifier of parent group, if known.
/// Type of this item.
/// Identifier for this item.
/// Type of previous item, if known.
/// Identifier of previous item, if known
private void CreateGroupAndOrderingRows(SourceLineNumber sourceLineNumbers,
ComplexReferenceParentType parentType, string parentId,
ComplexReferenceChildType type, string id,
ComplexReferenceChildType previousType, string previousId)
{
if (this.Core.EncounteredError)
{
return;
}
if (ComplexReferenceParentType.Unknown != parentType && null != parentId)
{
this.Core.CreateWixGroupRow(sourceLineNumbers, parentType, parentId, type, id);
}
if (ComplexReferenceChildType.Unknown != previousType && null != previousId)
{
// TODO: Should we define our own enum for this, just to ensure there's no "cross-contamination"?
// TODO: Also, we could potentially include an 'Attributes' field to track things like
// 'before' vs. 'after', and explicit vs. inferred dependencies.
this.Core.AddSymbol(new WixOrderingSymbol(sourceLineNumbers)
{
ItemType = type,
ItemIdRef = id,
DependsOnType = previousType,
DependsOnIdRef = previousId
});
}
}
///
/// Parse ExitCode element.
///
/// Element to parse
/// Id of parent element
private void ParseExitCodeElement(XElement node, string packageId)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
var value = CompilerConstants.IntegerNotSet;
var behavior = ExitCodeBehaviorType.NotSet;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Value":
value = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, Int32.MinValue + 2, Int32.MaxValue);
break;
case "Behavior":
var behaviorString = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
switch (behaviorString)
{
case "error":
behavior = ExitCodeBehaviorType.Error;
break;
case "forceReboot":
behavior = ExitCodeBehaviorType.ForceReboot;
break;
case "scheduleReboot":
behavior = ExitCodeBehaviorType.ScheduleReboot;
break;
case "success":
behavior = ExitCodeBehaviorType.Success;
break;
default:
this.Core.Write(ErrorMessages.IllegalAttributeValueWithLegalList(sourceLineNumbers, node.Name.LocalName, "Behavior", behaviorString, "success, error, scheduleReboot, forceReboot"));
behavior = ExitCodeBehaviorType.Success; // set value to avoid ExpectedAttribute below.
break;
}
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (ExitCodeBehaviorType.NotSet == behavior)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Behavior"));
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new WixBundlePackageExitCodeSymbol(sourceLineNumbers)
{
ChainPackageId = packageId,
Code = value,
Behavior = behavior
});
}
}
///
/// Parse Chain element.
///
/// Element to parse
private void ParseChainElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
var attributes = WixChainAttributes.None;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "DisableRollback":
if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
{
attributes |= WixChainAttributes.DisableRollback;
}
break;
case "DisableSystemRestore":
if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
{
attributes |= WixChainAttributes.DisableSystemRestore;
}
break;
case "ParallelCache":
if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
{
attributes |= WixChainAttributes.ParallelCache;
}
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
// Ensure there is always a rollback boundary at the beginning of the chain.
this.CreateRollbackBoundary(sourceLineNumbers, new Identifier(AccessModifier.Public, "WixDefaultBoundary"), YesNoType.Yes, YesNoType.No, ComplexReferenceParentType.PackageGroup, "WixChain", ComplexReferenceChildType.Unknown, null);
var previousId = "WixDefaultBoundary";
var previousType = ComplexReferenceChildType.Package;
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "MsiPackage":
previousId = this.ParseMsiPackageElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
previousType = ComplexReferenceChildType.Package;
break;
case "MspPackage":
previousId = this.ParseMspPackageElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
previousType = ComplexReferenceChildType.Package;
break;
case "MsuPackage":
previousId = this.ParseMsuPackageElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
previousType = ComplexReferenceChildType.Package;
break;
case "ExePackage":
previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
previousType = ComplexReferenceChildType.Package;
break;
case "RollbackBoundary":
previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
previousType = ComplexReferenceChildType.Package;
break;
case "PackageGroupRef":
previousId = this.ParsePackageGroupRefElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
previousType = ComplexReferenceChildType.PackageGroup;
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (null == previousId)
{
this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "MsiPackage", "ExePackage", "PackageGroupRef"));
}
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new WixChainSymbol(sourceLineNumbers)
{
Attributes = attributes
});
}
}
///
/// Parse MsiPackage element
///
/// Element to parse
/// Type of parent group, if known.
/// Identifier of parent group, if known.
/// Type of previous item, if known.
/// Identifier of previous item, if known
/// Identifier for package element.
private string ParseMsiPackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
{
return this.ParseChainPackage(node, WixBundlePackageType.Msi, parentType, parentId, previousType, previousId);
}
///
/// Parse MspPackage element
///
/// Element to parse
/// Type of parent group, if known.
/// Identifier of parent group, if known.
/// Type of previous item, if known.
/// Identifier of previous item, if known
/// Identifier for package element.
private string ParseMspPackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
{
return this.ParseChainPackage(node, WixBundlePackageType.Msp, parentType, parentId, previousType, previousId);
}
///
/// Parse MsuPackage element
///
/// Element to parse
/// Type of parent group, if known.
/// Identifier of parent group, if known.
/// Type of previous item, if known.
/// Identifier of previous item, if known
/// Identifier for package element.
private string ParseMsuPackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
{
return this.ParseChainPackage(node, WixBundlePackageType.Msu, parentType, parentId, previousType, previousId);
}
///
/// Parse ExePackage element
///
/// Element to parse
/// Type of parent group, if known.
/// Identifier of parent group, if known.
/// Type of previous item, if known.
/// Identifier of previous item, if known
/// Identifier for package element.
private string ParseExePackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
{
return this.ParseChainPackage(node, WixBundlePackageType.Exe, parentType, parentId, previousType, previousId);
}
///
/// Parse RollbackBoundary element
///
/// Element to parse
/// Type of parent group, if known.
/// Identifier of parent group, if known.
/// Type of previous item, if known.
/// Identifier of previous item, if known
/// Identifier for package element.
private string ParseRollbackBoundaryElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
{
Debug.Assert(ComplexReferenceParentType.PackageGroup == parentType);
Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType);
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier id = null;
var vital = YesNoType.Yes;
var transaction = YesNoType.No;
// This list lets us evaluate extension attributes *after* all core attributes
// have been parsed and dealt with, regardless of authoring order.
var extensionAttributes = new List();
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
var allowed = true;
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
break;
case "Vital":
vital = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "Transaction":
transaction = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
default:
allowed = false;
break;
}
if (!allowed)
{
this.Core.UnexpectedAttribute(node, attrib);
}
}
else
{
// Save the extension attributes for later...
extensionAttributes.Add(attrib);
}
}
if (null == id)
{
if (!String.IsNullOrEmpty(previousId))
{
id = this.Core.CreateIdentifier("rba", previousId);
}
if (null == id)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
id = Identifier.Invalid;
}
else if (!Common.IsIdentifier(id.Id))
{
this.Core.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
}
}
// Now that the rollback identifier is known, we can parse the extension attributes...
var contextValues = new Dictionary
{
["RollbackBoundaryId"] = id.Id
};
foreach (var attribute in extensionAttributes)
{
this.Core.ParseExtensionAttribute(node, attribute, contextValues);
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
this.CreateRollbackBoundary(sourceLineNumbers, id, vital, transaction, parentType, parentId, previousType, previousId);
}
return id.Id;
}
///
/// Parses one of the ChainPackage elements
///
/// Element to parse
/// Type of package to parse
/// Type of parent group, if known.
/// Identifier of parent group, if known.
/// Type of previous item, if known.
/// Identifier of previous item, if known
/// Identifier for package element.
/// This method contains the shared logic for parsing all of the ChainPackage
/// types, as there is more in common between them than different.
private string ParseChainPackage(XElement node, WixBundlePackageType packageType, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
{
Debug.Assert(ComplexReferenceParentType.PackageGroup == parentType);
Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType);
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier id = null;
string name = null;
string sourceFile = null;
string downloadUrl = null;
string after = null;
string installCondition = null;
var cache = YesNoAlwaysType.Yes; // the default is to cache everything in tradeoff for stability over disk space.
string cacheId = null;
string description = null;
string displayName = null;
var logPathVariable = (packageType == WixBundlePackageType.Msu) ? String.Empty : null;
var rollbackPathVariable = (packageType == WixBundlePackageType.Msu) ? String.Empty : null;
var permanent = YesNoType.NotSet;
var visible = YesNoType.NotSet;
var vital = YesNoType.Yes;
string installCommand = null;
string repairCommand = null;
string uninstallCommand = null;
var perMachine = YesNoDefaultType.NotSet;
string detectCondition = null;
string protocol = null;
var installSize = CompilerConstants.IntegerNotSet;
string msuKB = null;
var enableSignatureVerification = YesNoType.No;
var compressed = YesNoDefaultType.Default;
var enableFeatureSelection = YesNoType.NotSet;
var forcePerMachine = YesNoType.NotSet;
RemotePayload remotePayload = null;
var slipstream = YesNoType.NotSet;
var expectedNetFx4Args = new string[] { "/q", "/norestart", "/chainingpackage" };
// This list lets us evaluate extension attributes *after* all core attributes
// have been parsed and dealt with, regardless of authoring order.
var extensionAttributes = new List();
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
var allowed = true;
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
break;
case "Name":
name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false, true);
if (!this.Core.IsValidLongFilename(name, false, true))
{
this.Core.Write(ErrorMessages.IllegalLongFilename(sourceLineNumbers, node.Name.LocalName, "Name", name));
}
break;
case "SourceFile":
sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "DownloadUrl":
downloadUrl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "After":
after = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "InstallCondition":
installCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Cache":
var value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
switch (value)
{
case "always":
cache = YesNoAlwaysType.Always;
break;
case "yes":
cache = YesNoAlwaysType.Yes;
break;
case "no":
cache = YesNoAlwaysType.No;
break;
case "":
break;
default:
this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, value, "always", "yes", "no"));
break;
}
break;
case "CacheId":
cacheId = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Description":
description = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "DisplayName":
displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "EnableFeatureSelection":
enableFeatureSelection = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
allowed = (packageType == WixBundlePackageType.Msi);
break;
case "ForcePerMachine":
forcePerMachine = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
allowed = (packageType == WixBundlePackageType.Msi);
break;
case "LogPathVariable":
logPathVariable = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
break;
case "RollbackLogPathVariable":
rollbackPathVariable = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
break;
case "Permanent":
permanent = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "Visible":
visible = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
allowed = (packageType == WixBundlePackageType.Msi);
break;
case "Vital":
vital = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "InstallCommand":
installCommand = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
allowed = (packageType == WixBundlePackageType.Exe);
break;
case "RepairCommand":
repairCommand = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
allowed = (packageType == WixBundlePackageType.Exe);
break;
case "UninstallCommand":
uninstallCommand = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
allowed = (packageType == WixBundlePackageType.Exe);
break;
case "PerMachine":
perMachine = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
allowed = (packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msp);
break;
case "DetectCondition":
detectCondition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
allowed = (packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msu);
break;
case "Protocol":
protocol = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
allowed = (packageType == WixBundlePackageType.Exe);
break;
case "InstallSize":
installSize = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
break;
case "KB":
msuKB = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
allowed = (packageType == WixBundlePackageType.Msu);
break;
case "Compressed":
compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
break;
case "EnableSignatureVerification":
enableSignatureVerification = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
break;
case "Slipstream":
slipstream = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
allowed = (packageType == WixBundlePackageType.Msp);
break;
default:
allowed = false;
break;
}
if (!allowed)
{
this.Core.UnexpectedAttribute(node, attrib);
}
}
else
{
// Save the extension attributes for later...
extensionAttributes.Add(attrib);
}
}
// We need to handle RemotePayload up front because it effects value of sourceFile which is used in Id generation. Id is needed by other child elements.
foreach (var child in node.Elements(CompilerCore.WixNamespace + "RemotePayload"))
{
var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
if (CompilerCore.WixNamespace == node.Name.Namespace && node.Name.LocalName != "ExePackage" && node.Name.LocalName != "MsuPackage")
{
this.Core.Write(ErrorMessages.RemotePayloadUnsupported(childSourceLineNumbers));
continue;
}
if (null != remotePayload)
{
this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName));
}
remotePayload = this.ParseRemotePayloadElement(child);
}
if (String.IsNullOrEmpty(sourceFile))
{
if (String.IsNullOrEmpty(name))
{
this.Core.Write(ErrorMessages.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Name", "SourceFile"));
}
else if (null == remotePayload)
{
sourceFile = Path.Combine("SourceDir", name);
}
}
else if (null != remotePayload)
{
this.Core.Write(ErrorMessages.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, "RemotePayload", "SourceFile"));
}
else if (sourceFile.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
{
if (String.IsNullOrEmpty(name))
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name", "SourceFile", sourceFile));
}
else
{
sourceFile = Path.Combine(sourceFile, Path.GetFileName(name));
}
}
if (null == downloadUrl && null != remotePayload)
{
this.Core.Write(ErrorMessages.ExpectedAttributeWithElement(sourceLineNumbers, node.Name.LocalName, "DownloadUrl", "RemotePayload"));
}
if (YesNoDefaultType.No != compressed && null != remotePayload)
{
compressed = YesNoDefaultType.No;
this.Core.Write(WarningMessages.RemotePayloadsMustNotAlsoBeCompressed(sourceLineNumbers, node.Name.LocalName));
}
if (null == id)
{
if (!String.IsNullOrEmpty(name))
{
id = this.Core.CreateIdentifierFromFilename(Path.GetFileName(name));
}
else if (!String.IsNullOrEmpty(sourceFile))
{
id = this.Core.CreateIdentifierFromFilename(Path.GetFileName(sourceFile));
}
if (null == id)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
id = Identifier.Invalid;
}
else if (!Common.IsIdentifier(id.Id))
{
this.Core.Write(ErrorMessages.IllegalIdentifier(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
}
}
if (null == logPathVariable)
{
logPathVariable = String.Concat("WixBundleLog_", id.Id);
}
if (null == rollbackPathVariable)
{
rollbackPathVariable = String.Concat("WixBundleRollbackLog_", id.Id);
}
if (!String.IsNullOrEmpty(protocol) && !protocol.Equals("burn", StringComparison.Ordinal) && !protocol.Equals("netfx4", StringComparison.Ordinal) && !protocol.Equals("none", StringComparison.Ordinal))
{
this.Core.Write(ErrorMessages.IllegalAttributeValueWithLegalList(sourceLineNumbers, node.Name.LocalName, "Protocol", protocol, "none, burn, netfx4"));
}
if (!String.IsNullOrEmpty(protocol) && protocol.Equals("netfx4", StringComparison.Ordinal))
{
foreach (var expectedArgument in expectedNetFx4Args)
{
if (null == installCommand || -1 == installCommand.IndexOf(expectedArgument, StringComparison.OrdinalIgnoreCase))
{
this.Core.Write(WarningMessages.AttributeShouldContain(sourceLineNumbers, node.Name.LocalName, "InstallCommand", installCommand, expectedArgument, "Protocol", "netfx4"));
}
if (!String.IsNullOrEmpty(repairCommand) && -1 == repairCommand.IndexOf(expectedArgument, StringComparison.OrdinalIgnoreCase))
{
this.Core.Write(WarningMessages.AttributeShouldContain(sourceLineNumbers, node.Name.LocalName, "RepairCommand", repairCommand, expectedArgument, "Protocol", "netfx4"));
}
if (!String.IsNullOrEmpty(uninstallCommand) && -1 == uninstallCommand.IndexOf(expectedArgument, StringComparison.OrdinalIgnoreCase))
{
this.Core.Write(WarningMessages.AttributeShouldContain(sourceLineNumbers, node.Name.LocalName, "UninstallCommand", uninstallCommand, expectedArgument, "Protocol", "netfx4"));
}
}
}
// Only set default scope for EXEs and MSPs if not already set.
if ((WixBundlePackageType.Exe == packageType || WixBundlePackageType.Msp == packageType) && YesNoDefaultType.NotSet == perMachine)
{
perMachine = YesNoDefaultType.Default;
}
// Now that the package ID is known, we can parse the extension attributes...
var contextValues = new Dictionary() { { "PackageId", id.Id } };
foreach (var attribute in extensionAttributes)
{
this.Core.ParseExtensionAttribute(node, attribute, contextValues);
}
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
var allowed = true;
switch (child.Name.LocalName)
{
case "SlipstreamMsp":
allowed = (packageType == WixBundlePackageType.Msi);
if (allowed)
{
this.ParseSlipstreamMspElement(child, id.Id);
}
break;
case "MsiProperty":
allowed = (packageType == WixBundlePackageType.Msi || packageType == WixBundlePackageType.Msp);
if (allowed)
{
this.ParseMsiPropertyElement(child, id.Id);
}
break;
case "Payload":
this.ParsePayloadElement(child, ComplexReferenceParentType.Package, id, ComplexReferenceChildType.Unknown, null);
break;
case "PayloadGroupRef":
this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Package, id, ComplexReferenceChildType.Unknown, null);
break;
case "ExitCode":
allowed = (packageType == WixBundlePackageType.Exe);
if (allowed)
{
this.ParseExitCodeElement(child, id.Id);
}
break;
case "CommandLine":
allowed = (packageType == WixBundlePackageType.Exe);
if (allowed)
{
this.ParseCommandLineElement(child, id.Id);
}
break;
case "RemotePayload":
// Handled previously
break;
default:
allowed = false;
break;
}
if (!allowed)
{
this.Core.UnexpectedElement(node, child);
}
}
else
{
var context = new Dictionary() { { "Id", id?.Id } };
this.Core.ParseExtensionElement(node, child, context);
}
}
if (!this.Core.EncounteredError)
{
// We create the package contents as a payload with this package as the parent
this.CreatePayloadRow(sourceLineNumbers, id, name, sourceFile, downloadUrl, ComplexReferenceParentType.Package, id,
ComplexReferenceChildType.Unknown, null, compressed, enableSignatureVerification, displayName, description, remotePayload);
this.Core.AddSymbol(new WixChainItemSymbol(sourceLineNumbers, id));
WixBundlePackageAttributes attributes = 0;
attributes |= (YesNoType.Yes == permanent) ? WixBundlePackageAttributes.Permanent : 0;
attributes |= (YesNoType.Yes == visible) ? WixBundlePackageAttributes.Visible : 0;
var chainPackageSymbol = this.Core.AddSymbol(new WixBundlePackageSymbol(sourceLineNumbers, id)
{
Type = packageType,
PayloadRef = id.Id,
Attributes = attributes,
InstallCondition = installCondition,
CacheId = cacheId,
LogPathVariable = logPathVariable,
RollbackLogPathVariable = rollbackPathVariable,
});
if (YesNoAlwaysType.NotSet != cache)
{
chainPackageSymbol.Cache = cache;
}
if (YesNoType.NotSet != vital)
{
chainPackageSymbol.Vital = (vital == YesNoType.Yes);
}
if (YesNoDefaultType.NotSet != perMachine)
{
chainPackageSymbol.PerMachine = perMachine;
}
if (CompilerConstants.IntegerNotSet != installSize)
{
chainPackageSymbol.InstallSize = installSize;
}
switch (packageType)
{
case WixBundlePackageType.Exe:
this.Core.AddSymbol(new WixBundleExePackageSymbol(sourceLineNumbers, id)
{
Attributes = WixBundleExePackageAttributes.None,
DetectCondition = detectCondition,
InstallCommand = installCommand,
RepairCommand = repairCommand,
UninstallCommand = uninstallCommand,
ExeProtocol = protocol
});
break;
case WixBundlePackageType.Msi:
WixBundleMsiPackageAttributes msiAttributes = 0;
msiAttributes |= (YesNoType.Yes == enableFeatureSelection) ? WixBundleMsiPackageAttributes.EnableFeatureSelection : 0;
msiAttributes |= (YesNoType.Yes == forcePerMachine) ? WixBundleMsiPackageAttributes.ForcePerMachine : 0;
this.Core.AddSymbol(new WixBundleMsiPackageSymbol(sourceLineNumbers, id)
{
Attributes = msiAttributes
});
break;
case WixBundlePackageType.Msp:
WixBundleMspPackageAttributes mspAttributes = 0;
mspAttributes |= (YesNoType.Yes == slipstream) ? WixBundleMspPackageAttributes.Slipstream : 0;
this.Core.AddSymbol(new WixBundleMspPackageSymbol(sourceLineNumbers, id)
{
Attributes = mspAttributes
});
break;
case WixBundlePackageType.Msu:
this.Core.AddSymbol(new WixBundleMsuPackageSymbol(sourceLineNumbers, id)
{
DetectCondition = detectCondition,
MsuKB = msuKB
});
break;
}
this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Package, id.Id, previousType, previousId, after);
}
return id.Id;
}
///
/// Parse CommandLine element.
///
/// Element to parse
/// Parent packageId
private void ParseCommandLineElement(XElement node, string packageId)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string installArgument = null;
string uninstallArgument = null;
string repairArgument = null;
string condition = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "InstallArgument":
installArgument = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "UninstallArgument":
uninstallArgument = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "RepairArgument":
repairArgument = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Condition":
condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (String.IsNullOrEmpty(condition))
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Condition"));
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new WixBundlePackageCommandLineSymbol(sourceLineNumbers)
{
WixBundlePackageRef = packageId,
InstallArgument = installArgument,
UninstallArgument = uninstallArgument,
RepairArgument = repairArgument,
Condition = condition
});
}
}
///
/// Parse PackageGroup element.
///
/// Element to parse
private void ParsePackageGroupElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier id = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == id)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
id = Identifier.Invalid;
}
var previousType = ComplexReferenceChildType.Unknown;
string previousId = null;
foreach (var child in node.Elements())
{
if (CompilerCore.WixNamespace == child.Name.Namespace)
{
switch (child.Name.LocalName)
{
case "MsiPackage":
previousId = this.ParseMsiPackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
previousType = ComplexReferenceChildType.Package;
break;
case "MspPackage":
previousId = this.ParseMspPackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
previousType = ComplexReferenceChildType.Package;
break;
case "MsuPackage":
previousId = this.ParseMsuPackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
previousType = ComplexReferenceChildType.Package;
break;
case "ExePackage":
previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
previousType = ComplexReferenceChildType.Package;
break;
case "RollbackBoundary":
previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
previousType = ComplexReferenceChildType.Package;
break;
case "PackageGroupRef":
previousId = this.ParsePackageGroupRefElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
previousType = ComplexReferenceChildType.PackageGroup;
break;
default:
this.Core.UnexpectedElement(node, child);
break;
}
}
else
{
this.Core.ParseExtensionElement(node, child);
}
}
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new WixBundlePackageGroupSymbol(sourceLineNumbers, id));
}
}
///
/// Parses a package group reference element.
///
/// Element to parse.
/// ComplexReferenceParentType of parent element (Unknown or PackageGroup).
/// Identifier of parent element.
/// Identifier for package group element.
private string ParsePackageGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId)
{
return this.ParsePackageGroupRefElement(node, parentType, parentId, ComplexReferenceChildType.Unknown, null);
}
///
/// Parses a package group reference element.
///
/// Element to parse.
/// ComplexReferenceParentType of parent element (Unknown or PackageGroup).
/// Identifier of parent element.
///
///
/// Identifier for package group element.
private string ParsePackageGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
{
Debug.Assert(ComplexReferenceParentType.Unknown == parentType || ComplexReferenceParentType.PackageGroup == parentType || ComplexReferenceParentType.Container == parentType);
Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType);
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string id = null;
string after = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixBundlePackageGroup, id);
break;
case "After":
after = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == id)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
}
if (null != after && ComplexReferenceParentType.Container == parentType)
{
this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "After", parentId));
}
this.Core.ParseForExtensionElements(node);
if (ComplexReferenceParentType.Container == parentType)
{
this.Core.CreateWixGroupRow(sourceLineNumbers, ComplexReferenceParentType.Container, parentId, ComplexReferenceChildType.PackageGroup, id);
}
else
{
this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.PackageGroup, id, previousType, previousId, after);
}
return id;
}
///
/// Creates rollback boundary.
///
/// Source line numbers.
/// Identifier for the rollback boundary.
/// Indicates whether the rollback boundary is vital or not.
/// Indicates whether the rollback boundary will use an MSI transaction.
/// Type of parent group.
/// Identifier of parent group.
/// Type of previous item, if any.
/// Identifier of previous item, if any.
private void CreateRollbackBoundary(SourceLineNumber sourceLineNumbers, Identifier id, YesNoType vital, YesNoType transaction, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
{
this.Core.AddSymbol(new WixChainItemSymbol(sourceLineNumbers, id));
var rollbackBoundary = this.Core.AddSymbol(new WixBundleRollbackBoundarySymbol(sourceLineNumbers, id));
if (YesNoType.NotSet != vital)
{
rollbackBoundary.Vital = (vital == YesNoType.Yes);
}
if (YesNoType.NotSet != transaction)
{
rollbackBoundary.Transaction = (transaction == YesNoType.Yes);
}
this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Package, id.Id, previousType, previousId, null);
}
///
/// Creates group and ordering information for packages
///
/// Source line numbers.
/// Type of parent group, if known.
/// Identifier of parent group, if known.
/// Type of this item.
/// Identifier for this item.
/// Type of previous item, if known.
/// Identifier of previous item, if known
/// Identifier of explicit 'After' attribute, if given.
private void CreateChainPackageMetaRows(SourceLineNumber sourceLineNumbers,
ComplexReferenceParentType parentType, string parentId,
ComplexReferenceChildType type, string id,
ComplexReferenceChildType previousType, string previousId, string afterId)
{
// If there's an explicit 'After' attribute, it overrides the inferred previous item.
if (null != afterId)
{
previousType = ComplexReferenceChildType.Package;
previousId = afterId;
}
this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId, type, id, previousType, previousId);
}
///
/// Parse MsiProperty element
///
/// Element to parse
/// Id of parent element
private void ParseMsiPropertyElement(XElement node, string packageId)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string name = null;
string value = null;
string condition = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Name":
name = this.Core.GetAttributeMsiPropertyNameValue(sourceLineNumbers, attrib);
break;
case "Value":
value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Condition":
condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == name)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
}
if (null == value)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
var symbol = this.Core.AddSymbol(new WixBundleMsiPropertySymbol(sourceLineNumbers, new Identifier(AccessModifier.Private, packageId, name))
{
PackageRef = packageId,
Name = name,
Value = value
});
if (!String.IsNullOrEmpty(condition))
{
symbol.Condition = condition;
}
}
}
///
/// Parse SlipstreamMsp element
///
/// Element to parse
/// Id of parent element
private void ParseSlipstreamMspElement(XElement node, string packageId)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string id = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixBundlePackage, id);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == id)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new WixBundleSlipstreamMspSymbol(sourceLineNumbers, new Identifier(AccessModifier.Private, packageId, id))
{
TargetPackageRef = packageId,
MspPackageRef = id
});
}
}
///
/// Parse RelatedBundle element
///
/// Element to parse
private void ParseRelatedBundleElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string id = null;
var actionType = RelatedBundleActionType.Detect;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
break;
case "Action":
var action = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
switch (action)
{
case "Detect":
case "detect":
actionType = RelatedBundleActionType.Detect;
break;
case "Upgrade":
case "upgrade":
actionType = RelatedBundleActionType.Upgrade;
break;
case "Addon":
case "addon":
actionType = RelatedBundleActionType.Addon;
break;
case "Patch":
case "patch":
actionType = RelatedBundleActionType.Patch;
break;
case "":
break;
default:
this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Action", action, "Detect", "Upgrade", "Addon", "Patch"));
break;
}
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == id)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new WixRelatedBundleSymbol(sourceLineNumbers)
{
BundleId = id,
Action = actionType,
});
}
}
///
/// Parse Update element
///
/// Element to parse
private void ParseUpdateElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
string location = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Location":
location = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == location)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Location"));
}
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new WixBundleUpdateSymbol(sourceLineNumbers)
{
Location = location
});
}
}
///
/// Parse SetVariable element
///
/// Element to parse
private void ParseSetVariableElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
Identifier id = null;
string variable = null;
string condition = null;
string after = null;
string value = null;
string typeValue = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Id":
id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
break;
case "Variable":
variable = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Condition":
condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "After":
after = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Value":
value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
case "Type":
typeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib, null);
}
}
var type = this.ValidateVariableTypeWithValue(sourceLineNumbers, node, typeValue, value);
this.Core.ParseForExtensionElements(node);
if (id == null)
{
id = this.Core.CreateIdentifier("sbv", variable, condition, after, value, type.ToString());
}
this.Core.CreateWixSearchSymbol(sourceLineNumbers, node.Name.LocalName, id, variable, condition, after);
if (!this.Messaging.EncounteredError)
{
this.Core.AddSymbol(new WixSetVariableSymbol(sourceLineNumbers, id)
{
Value = value,
Type = type,
});
}
}
///
/// Parse Variable element
///
/// Element to parse
private void ParseVariableElement(XElement node)
{
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
var hidden = false;
string name = null;
var persisted = false;
string value = null;
string typeValue = null;
foreach (var attrib in node.Attributes())
{
if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
{
switch (attrib.Name.LocalName)
{
case "Hidden":
if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
{
hidden = true;
}
break;
case "Name":
name = this.Core.GetAttributeBundleVariableValue(sourceLineNumbers, attrib);
break;
case "Persisted":
if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
{
persisted = true;
}
break;
case "Value":
value = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
break;
case "Type":
typeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
break;
default:
this.Core.UnexpectedAttribute(node, attrib);
break;
}
}
else
{
this.Core.ParseExtensionAttribute(node, attrib);
}
}
if (null == name)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
}
else if (name.StartsWith("Wix", StringComparison.OrdinalIgnoreCase))
{
this.Core.Write(ErrorMessages.ReservedNamespaceViolation(sourceLineNumbers, node.Name.LocalName, "Name", "Wix"));
}
if (hidden && persisted)
{
this.Core.Write(ErrorMessages.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Hidden", "yes", "Persisted"));
}
var type = this.ValidateVariableTypeWithValue(sourceLineNumbers, node, typeValue, value);
this.Core.ParseForExtensionElements(node);
if (!this.Core.EncounteredError)
{
this.Core.AddSymbol(new WixBundleVariableSymbol(sourceLineNumbers, new Identifier(AccessModifier.Private, name))
{
Value = value,
Type = type,
Hidden = hidden,
Persisted = persisted
});
}
}
private WixBundleVariableType ValidateVariableTypeWithValue(SourceLineNumber sourceLineNumbers, XElement node, string typeValue, string value)
{
WixBundleVariableType type;
switch (typeValue)
{
case "formatted":
type = WixBundleVariableType.Formatted;
break;
case "numeric":
type = WixBundleVariableType.Numeric;
break;
case "string":
type = WixBundleVariableType.String;
break;
case "version":
type = WixBundleVariableType.Version;
break;
case null:
type = WixBundleVariableType.Unknown;
break;
default:
this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Type", typeValue, "formatted", "numeric", "string", "version"));
return WixBundleVariableType.Unknown;
}
if (type != WixBundleVariableType.Unknown)
{
if (value == null)
{
this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, "Variable", "Value", "Type"));
}
return type;
}
else if (value == null)
{
return type;
}
// Infer the type from the current value...
if (value.StartsWith("v", StringComparison.OrdinalIgnoreCase))
{
// Version constructor does not support simple "v#" syntax so check to see if the value is
// non-negative real quick.
if (Int32.TryParse(value.Substring(1), NumberStyles.None, CultureInfo.InvariantCulture.NumberFormat, out var _))
{
return WixBundleVariableType.Version;
}
else if (Version.TryParse(value.Substring(1), out var _))
{
return WixBundleVariableType.Version;
}
}
// Not a version, check for numeric.
if (Int64.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture.NumberFormat, out var _))
{
return WixBundleVariableType.Numeric;
}
return WixBundleVariableType.String;
}
private class RemotePayload
{
public string CertificatePublicKey { get; set; }
public string CertificateThumbprint { get; set; }
public string Description { get; set; }
public string Hash { get; set; }
public string ProductName { get; set; }
public int Size { get; set; }
public string Version { get; set; }
}
}
}