// 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.Netfx { using System; using System.Collections.Generic; using System.Xml.Linq; using WixToolset.Data; using WixToolset.Extensibility; using WixToolset.Extensibility.Data; using WixToolset.Netfx.Symbols; /// /// The compiler for the WiX Toolset .NET Framework Extension. /// public sealed class NetfxCompiler : BaseCompilerExtension { public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/netfx"; /// /// 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) { switch (parentElement.Name.LocalName) { case "File": string fileId = context["FileId"]; switch (element.Name.LocalName) { case "NativeImage": this.ParseNativeImageElement(intermediate, section, element, fileId); break; default: this.ParseHelper.UnexpectedElement(parentElement, element); break; } break; case "Bundle": case "Fragment": switch (element.Name.LocalName) { case "DotNetCoreSearch": this.ParseDotNetCoreSearchElement(intermediate, section, element); break; case "DotNetCoreSearchRef": this.ParseDotNetCoreSearchRefElement(intermediate, section, element); break; } break; default: this.ParseHelper.UnexpectedElement(parentElement, element); break; } } private void ParseDotNetCoreSearchElement(Intermediate intermediate, IntermediateSection section, XElement element) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; string variable = null; string condition = null; string after = null; NetCoreSearchRuntimeType? runtimeType = null; NetCoreSearchPlatform? platform = null; var majorVersion = CompilerConstants.IntegerNotSet; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "Variable": variable = this.ParseHelper.GetAttributeBundleVariableNameValue(sourceLineNumbers, attrib); break; case "Condition": condition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "After": after = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "RuntimeType": var runtimeTypeValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (runtimeTypeValue) { case "aspnet": runtimeType = NetCoreSearchRuntimeType.Aspnet; break; case "core": runtimeType = NetCoreSearchRuntimeType.Core; break; case "desktop": runtimeType = NetCoreSearchRuntimeType.Desktop; break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "RuntimeType", runtimeTypeValue, "aspnet", "core", "desktop")); break; } break; case "Platform": var platformValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (platformValue) { case "arm64": platform = NetCoreSearchPlatform.Arm64; break; case "x64": platform = NetCoreSearchPlatform.X64; break; case "x86": platform = NetCoreSearchPlatform.X86; break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Platform", platformValue, "arm64", "x64", "x86")); break; } break; case "MajorVersion": // .NET Core had a different deployment strategy before .NET Core 3.0 which would require different detection logic. majorVersion = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 3, Int32.MaxValue); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (id == null) { id = this.ParseHelper.CreateIdentifier("dncs", variable, condition, after); } if (!runtimeType.HasValue) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "RuntimeType")); } if (!platform.HasValue) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Platform")); } if (majorVersion == CompilerConstants.IntegerNotSet) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "MajorVersion")); } else if (majorVersion == 4) { this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "MajorVersion", "4", "3", "5+")); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); var bundleExtensionId = this.ParseHelper.CreateIdentifierValueFromPlatform("Wix4NetfxBundleExtension", this.Context.Platform, BurnPlatforms.X86 | BurnPlatforms.X64 | BurnPlatforms.ARM64); if (bundleExtensionId == null) { this.Messaging.Write(ErrorMessages.UnsupportedPlatformForElement(sourceLineNumbers, this.Context.Platform.ToString(), element.Name.LocalName)); } if (!this.Messaging.EncounteredError) { this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, bundleExtensionId); section.AddSymbol(new NetFxNetCoreSearchSymbol(sourceLineNumbers, id) { RuntimeType = runtimeType.Value, Platform = platform.Value, MajorVersion = majorVersion, }); } } private void ParseDotNetCoreSearchRefElement(Intermediate intermediate, IntermediateSection section, XElement element) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": var refId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, NetfxSymbolDefinitions.NetFxNetCoreSearch, refId); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); } /// /// Parses a NativeImage element. /// /// The element to parse. /// The file identifier of the parent element. private void ParseNativeImageElement(Intermediate intermediate, IntermediateSection section, XElement element, string fileId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; string appBaseDirectory = null; string assemblyApplication = null; int attributes = 0x8; // 32bit is on by default int priority = 3; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "AppBaseDirectory": appBaseDirectory = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); // See if a formatted value is specified. if (-1 == appBaseDirectory.IndexOf("[", StringComparison.Ordinal)) { this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.Directory, appBaseDirectory); } break; case "AssemblyApplication": assemblyApplication = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); // See if a formatted value is specified. if (-1 == assemblyApplication.IndexOf("[", StringComparison.Ordinal)) { this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.File, assemblyApplication); } break; case "Debug": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= 0x1; } break; case "Dependencies": if (YesNoType.No == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= 0x2; } break; case "Platform": string platformValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); if (0 < platformValue.Length) { switch (platformValue) { case "32bit": // 0x8 is already on by default break; case "64bit": attributes &= ~0x8; attributes |= 0x10; break; case "all": attributes |= 0x10; break; } } break; case "Priority": priority = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, 3); break; case "Profile": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= 0x4; } break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (null == id) { id = this.ParseHelper.CreateIdentifier("nni", fileId); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4NetFxScheduleNativeImage", this.Context.Platform, CustomActionPlatforms.ARM64 | CustomActionPlatforms.X64 | CustomActionPlatforms.X86); if (!this.Messaging.EncounteredError) { section.AddSymbol(new NetFxNativeImageSymbol(sourceLineNumbers, id) { FileRef = fileId, Priority = priority, Attributes = attributes, ApplicationFileRef = assemblyApplication, ApplicationBaseDirectoryRef = appBaseDirectory, }); } } } }