From 91259637be1eccd149093eb49c7ff68826d1d331 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sun, 19 Dec 2021 12:06:58 -0800 Subject: Significant rewrite of wix.targets to improve compatibility This is still a work in progress but the wix.targets are now much more compatible with other project systems. The focus has been on removing customizations to leverage MS.Common.targets. --- .../CreateProjectReferenceDefineConstants.cs | 271 ------- ...eProjectReferenceDefineConstantsAndBindPaths.cs | 269 +++++++ .../GenerateCompileWithObjectPath.cs | 145 ---- .../UpdateProjectReferenceMetadata.cs | 95 +++ .../WixToolset.BuildTasks.csproj | 4 +- src/wix/WixToolset.Sdk/Sdk/Sdk.props | 16 +- src/wix/WixToolset.Sdk/Sdk/Sdk.targets | 13 +- src/wix/WixToolset.Sdk/WixToolset.Sdk.csproj | 2 + src/wix/WixToolset.Sdk/build/WixToolset.Sdk.props | 7 +- .../WixToolset.Sdk/build/WixToolset.Sdk.targets | 10 + src/wix/WixToolset.Sdk/tools/wix.harvest.targets | 14 +- src/wix/WixToolset.Sdk/tools/wix.props | 53 ++ src/wix/WixToolset.Sdk/tools/wix.signing.targets | 16 +- src/wix/WixToolset.Sdk/tools/wix.targets | 881 ++++++++++----------- 14 files changed, 852 insertions(+), 944 deletions(-) delete mode 100644 src/wix/WixToolset.BuildTasks/CreateProjectReferenceDefineConstants.cs create mode 100644 src/wix/WixToolset.BuildTasks/CreateProjectReferenceDefineConstantsAndBindPaths.cs delete mode 100644 src/wix/WixToolset.BuildTasks/GenerateCompileWithObjectPath.cs create mode 100644 src/wix/WixToolset.BuildTasks/UpdateProjectReferenceMetadata.cs create mode 100644 src/wix/WixToolset.Sdk/build/WixToolset.Sdk.targets create mode 100644 src/wix/WixToolset.Sdk/tools/wix.props diff --git a/src/wix/WixToolset.BuildTasks/CreateProjectReferenceDefineConstants.cs b/src/wix/WixToolset.BuildTasks/CreateProjectReferenceDefineConstants.cs deleted file mode 100644 index 7cda6b01..00000000 --- a/src/wix/WixToolset.BuildTasks/CreateProjectReferenceDefineConstants.cs +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.BuildTasks -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.IO; - using Microsoft.Build.Framework; - using Microsoft.Build.Utilities; - - /// - /// An MSBuild task to create a list of preprocessor defines to be passed to candle from the - /// list of referenced projects. - /// - public sealed class CreateProjectReferenceDefineConstants : Task - { - private ITaskItem[] defineConstants; - private ITaskItem[] projectConfigurations; - private ITaskItem[] projectReferencePaths; - - [Output] - public ITaskItem[] DefineConstants - { - get { return this.defineConstants; } - } - - [Required] - public ITaskItem[] ProjectReferencePaths - { - get { return this.projectReferencePaths; } - set { this.projectReferencePaths = value; } - } - - public ITaskItem[] ProjectConfigurations - { - get { return this.projectConfigurations; } - set { this.projectConfigurations = value; } - } - - public override bool Execute() - { - List outputItems = new List(); - Dictionary defineConstants = new Dictionary(); - - for (int i = 0; i < this.ProjectReferencePaths.Length; i++) - { - ITaskItem item = this.ProjectReferencePaths[i]; - - string configuration = item.GetMetadata("Configuration"); - string fullConfiguration = item.GetMetadata("FullConfiguration"); - string platform = item.GetMetadata("Platform"); - - string projectPath = CreateProjectReferenceDefineConstants.GetProjectPath(this.ProjectReferencePaths, i); - string projectDir = Path.GetDirectoryName(projectPath) + Path.DirectorySeparatorChar; - string projectExt = Path.GetExtension(projectPath); - string projectFileName = Path.GetFileName(projectPath); - string projectName = Path.GetFileNameWithoutExtension(projectPath); - - string referenceName = CreateProjectReferenceDefineConstants.GetReferenceName(item, projectName); - - string targetPath = item.GetMetadata("FullPath"); - string targetDir = Path.GetDirectoryName(targetPath) + Path.DirectorySeparatorChar; - string targetExt = Path.GetExtension(targetPath); - string targetFileName = Path.GetFileName(targetPath); - string targetName = Path.GetFileNameWithoutExtension(targetPath); - - // If there is no configuration metadata on the project reference task item, - // check for any additional configuration data provided in the optional task property. - if (String.IsNullOrEmpty(fullConfiguration)) - { - fullConfiguration = this.FindProjectConfiguration(projectName); - if (!String.IsNullOrEmpty(fullConfiguration)) - { - string[] typeAndPlatform = fullConfiguration.Split('|'); - configuration = typeAndPlatform[0]; - platform = (typeAndPlatform.Length > 1 ? typeAndPlatform[1] : String.Empty); - } - } - - // write out the platform/configuration defines - defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.Configuration", referenceName)] = configuration; - defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.FullConfiguration", referenceName)] = fullConfiguration; - defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.Platform", referenceName)] = platform; - - // write out the ProjectX defines - defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.ProjectDir", referenceName)] = projectDir; - defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.ProjectExt", referenceName)] = projectExt; - defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.ProjectFileName", referenceName)] = projectFileName; - defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.ProjectName", referenceName)] = projectName; - defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.ProjectPath", referenceName)] = projectPath; - - // write out the TargetX defines - string targetDirDefine = String.Format(CultureInfo.InvariantCulture, "{0}.TargetDir", referenceName); - if (defineConstants.ContainsKey(targetDirDefine)) - { - //if target dir was already defined, redefine it as the common root shared by multiple references from the same project - string commonDir = FindCommonRoot(targetDir, defineConstants[targetDirDefine]); - if (!String.IsNullOrEmpty(commonDir)) - { - targetDir = commonDir; - } - } - defineConstants[targetDirDefine] = CreateProjectReferenceDefineConstants.EnsureEndsWithBackslash(targetDir); - - defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.TargetExt", referenceName)] = targetExt; - defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.TargetFileName", referenceName)] = targetFileName; - defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.TargetName", referenceName)] = targetName; - - //if target path was already defined, append to it creating a list of multiple references from the same project - string targetPathDefine = String.Format(CultureInfo.InvariantCulture, "{0}.TargetPath", referenceName); - if (defineConstants.ContainsKey(targetPathDefine)) - { - string oldTargetPath = defineConstants[targetPathDefine]; - if (!targetPath.Equals(oldTargetPath, StringComparison.OrdinalIgnoreCase)) - { - defineConstants[targetPathDefine] += ";" + targetPath; - } - - //If there was only one targetpath we need to create its culture specific define - if (!oldTargetPath.Contains(";")) - { - string oldSubFolder = FindSubfolder(oldTargetPath, targetDir, targetFileName); - if (!String.IsNullOrEmpty(oldSubFolder)) - { - defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.{1}.TargetPath", referenceName, oldSubFolder.Replace('\\', '_'))] = oldTargetPath; - } - } - - // Create a culture specific define - string subFolder = FindSubfolder(targetPath, targetDir, targetFileName); - if (!String.IsNullOrEmpty(subFolder)) - { - defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.{1}.TargetPath", referenceName, subFolder.Replace('\\', '_'))] = targetPath; - } - - } - else - { - defineConstants[targetPathDefine] = targetPath; - } - } - - foreach (KeyValuePair define in defineConstants) - { - outputItems.Add(new TaskItem(String.Format(CultureInfo.InvariantCulture, "{0}={1}", define.Key, define.Value))); - } - - this.defineConstants = outputItems.ToArray(); - - return true; - } - - public static string GetProjectPath(ITaskItem[] projectReferencePaths, int i) - { - return projectReferencePaths[i].GetMetadata("MSBuildSourceProjectFile"); - } - - public static string GetReferenceName(ITaskItem item, string projectName) - { - string referenceName = item.GetMetadata("Name"); - if (String.IsNullOrEmpty(referenceName)) - { - referenceName = projectName; - } - - // We cannot have an equals sign in the variable name because it - // messes with the preprocessor definitions on the command line. - referenceName = referenceName.Replace('=', '_'); - - // We cannot have a double quote on the command line because it - // there is no way to escape it on the command line. - referenceName = referenceName.Replace('\"', '_'); - - // We cannot have parens in the variable name because the WiX - // preprocessor will not be able to parse it. - referenceName = referenceName.Replace('(', '_'); - referenceName = referenceName.Replace(')', '_'); - - return referenceName; - } - - /// - /// Look through the configuration data in the ProjectConfigurations property - /// to find the configuration for a project, if available. - /// - /// Name of the project that is being searched for. - /// Full configuration spec, for example "Release|Win32". - private string FindProjectConfiguration(string projectName) - { - string configuration = String.Empty; - - if (this.ProjectConfigurations != null) - { - foreach (ITaskItem configItem in this.ProjectConfigurations) - { - string configProject = configItem.ItemSpec; - if (configProject.Length > projectName.Length && - configProject.StartsWith(projectName) && - configProject[projectName.Length] == '=') - { - configuration = configProject.Substring(projectName.Length + 1); - break; - } - } - } - - return configuration; - } - - /// - /// Finds the common root between two paths - /// - /// - /// - /// common root on success, empty string on failure - private static string FindCommonRoot(string path1, string path2) - { - path1 = path1.TrimEnd(Path.DirectorySeparatorChar); - path2 = path2.TrimEnd(Path.DirectorySeparatorChar); - - while (!String.IsNullOrEmpty(path1)) - { - for (string searchPath = path2; !String.IsNullOrEmpty(searchPath); searchPath = Path.GetDirectoryName(searchPath)) - { - if (path1.Equals(searchPath, StringComparison.OrdinalIgnoreCase)) - { - return searchPath; - } - } - - path1 = Path.GetDirectoryName(path1); - } - - return path1; - } - - /// - /// Finds the subfolder of a path, excluding a root and filename. - /// - /// Path to examine - /// Root that must be present - /// - /// - private static string FindSubfolder(string path, string rootPath, string fileName) - { - if (Path.GetFileName(path).Equals(fileName, StringComparison.OrdinalIgnoreCase)) - { - path = Path.GetDirectoryName(path); - } - - if (path.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase)) - { - // cut out the root and return the subpath - return path.Substring(rootPath.Length).Trim(Path.DirectorySeparatorChar); - } - - return String.Empty; - } - - private static string EnsureEndsWithBackslash(string dir) - { - if (dir[dir.Length - 1] != Path.DirectorySeparatorChar) - { - dir += Path.DirectorySeparatorChar; - } - - return dir; - } - } -} diff --git a/src/wix/WixToolset.BuildTasks/CreateProjectReferenceDefineConstantsAndBindPaths.cs b/src/wix/WixToolset.BuildTasks/CreateProjectReferenceDefineConstantsAndBindPaths.cs new file mode 100644 index 00000000..af4bbd0c --- /dev/null +++ b/src/wix/WixToolset.BuildTasks/CreateProjectReferenceDefineConstantsAndBindPaths.cs @@ -0,0 +1,269 @@ +// 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.BuildTasks +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + + /// + /// MSBuild task to create a list of preprocessor defines and bind paths from resolved + /// project references. + /// + public sealed class CreateProjectReferenceDefineConstantsAndBindPaths : Task + { + private static readonly string DirectorySeparatorString = Path.DirectorySeparatorChar.ToString(); + + [Required] + public ITaskItem[] ResolvedProjectReferences { get; set; } + + public ITaskItem[] ProjectConfigurations { get; set; } + + [Output] + public ITaskItem[] BindPaths { get; private set; } + + [Output] + public ITaskItem[] DefineConstants { get; private set; } + + public override bool Execute() + { + var bindPaths = new Dictionary>(StringComparer.OrdinalIgnoreCase); + var defineConstants = new Dictionary(); + + foreach (var resolvedReference in this.ResolvedProjectReferences) + { + this.AddBindPathsForResolvedReference(bindPaths, resolvedReference); + + this.AddDefineConstantsForResolvedReference(defineConstants, resolvedReference); + } + + this.BindPaths = bindPaths.Values.SelectMany(bp => bp).ToArray(); + this.DefineConstants = defineConstants.Select(define => new TaskItem(define.Key + "=" + define.Value)).ToArray(); + + return true; + } + + private void AddBindPathsForResolvedReference(Dictionary> bindPathByPaths, ITaskItem resolvedReference) + { + // If the BindName was not explicitly provided, try to use the source project's filename + // as the bind name. + var name = resolvedReference.GetMetadata("BindName"); + if (String.IsNullOrWhiteSpace(name)) + { + var projectPath = resolvedReference.GetMetadata("MSBuildSourceProjectFile"); + name = String.IsNullOrWhiteSpace(projectPath) ? String.Empty : Path.GetFileNameWithoutExtension(projectPath); + } + + var path = resolvedReference.GetMetadata("BindPath"); + if (String.IsNullOrWhiteSpace(path)) + { + path = Path.GetDirectoryName(resolvedReference.GetMetadata("FullPath")); + } + + if (!bindPathByPaths.TryGetValue(path, out var bindPathsForPath) || + !bindPathsForPath.Any(bp => bp.GetMetadata("BindName").Equals(name, StringComparison.OrdinalIgnoreCase))) + { + if (bindPathsForPath == null) + { + bindPathsForPath = new List + { + new TaskItem(path) + }; + + bindPathByPaths.Add(path, bindPathsForPath); + } + + if (!String.IsNullOrWhiteSpace(name)) + { + var metadata = new Dictionary { ["BindName"] = name }; + bindPathsForPath.Add(new TaskItem(path, metadata)); + } + } + } + + private void AddDefineConstantsForResolvedReference(Dictionary defineConstants, ITaskItem resolvedReference) + { + var configuration = resolvedReference.GetMetadata("Configuration"); + var fullConfiguration = resolvedReference.GetMetadata("FullConfiguration"); + var platform = resolvedReference.GetMetadata("Platform"); + + var projectPath = resolvedReference.GetMetadata("MSBuildSourceProjectFile"); + var projectDir = Path.GetDirectoryName(projectPath) + Path.DirectorySeparatorChar; + var projectExt = Path.GetExtension(projectPath); + var projectFileName = Path.GetFileName(projectPath); + var projectName = Path.GetFileNameWithoutExtension(projectPath); + + var referenceName = ToolsCommon.GetIdentifierFromName(ToolsCommon.GetMetadataOrDefault(resolvedReference, "Name", projectName)); + + var targetPath = resolvedReference.GetMetadata("FullPath"); + var targetDir = Path.GetDirectoryName(targetPath) + Path.DirectorySeparatorChar; + var targetExt = Path.GetExtension(targetPath); + var targetFileName = Path.GetFileName(targetPath); + var targetName = Path.GetFileNameWithoutExtension(targetPath); + + // If there is no configuration metadata on the project reference task item, + // check for any additional configuration data provided in the optional task property. + if (String.IsNullOrWhiteSpace(fullConfiguration)) + { + fullConfiguration = this.FindProjectConfiguration(projectName); + if (!String.IsNullOrWhiteSpace(fullConfiguration)) + { + var typeAndPlatform = fullConfiguration.Split('|'); + configuration = typeAndPlatform[0]; + platform = (typeAndPlatform.Length > 1 ? typeAndPlatform[1] : String.Empty); + } + } + + // write out the platform/configuration defines + defineConstants[referenceName + ".Configuration"] = configuration; + defineConstants[referenceName + ".FullConfiguration"] = fullConfiguration; + defineConstants[referenceName + ".Platform"] = platform; + + // write out the ProjectX defines + defineConstants[referenceName + ".ProjectDir"] = projectDir; + defineConstants[referenceName + ".ProjectExt"] = projectExt; + defineConstants[referenceName + ".ProjectFileName"] = projectFileName; + defineConstants[referenceName + ".ProjectName"] = projectName; + defineConstants[referenceName + ".ProjectPath"] = projectPath; + + // write out the TargetX defines + var targetDirDefine = referenceName + ".TargetDir"; + if (defineConstants.ContainsKey(targetDirDefine)) + { + //if target dir was already defined, redefine it as the common root shared by multiple references from the same project + var commonDir = FindCommonRoot(targetDir, defineConstants[targetDirDefine]); + if (!String.IsNullOrEmpty(commonDir)) + { + targetDir = commonDir; + } + } + defineConstants[targetDirDefine] = CreateProjectReferenceDefineConstantsAndBindPaths.EnsureEndsWithBackslash(targetDir); + + defineConstants[referenceName + ".TargetExt"] = targetExt; + defineConstants[referenceName + ".TargetFileName"] = targetFileName; + defineConstants[referenceName + ".TargetName"] = targetName; + + // If target path was already defined, append to it creating a list of multiple references from the same project + var targetPathDefine = referenceName + ".TargetPath"; + if (defineConstants.TryGetValue(targetPathDefine, out var oldTargetPath)) + { + if (!targetPath.Equals(oldTargetPath, StringComparison.OrdinalIgnoreCase)) + { + defineConstants[targetPathDefine] += ";" + targetPath; + } + + // If there was only one targetpath we need to create its culture specific define + if (!oldTargetPath.Contains(";")) + { + var oldSubFolder = FindSubfolder(oldTargetPath, targetDir, targetFileName); + if (!String.IsNullOrEmpty(oldSubFolder)) + { + defineConstants[referenceName + "." + oldSubFolder.Replace('\\', '_') + ".TargetPath"] = oldTargetPath; + } + } + + // Create a culture specific define + var subFolder = FindSubfolder(targetPath, targetDir, targetFileName); + if (!String.IsNullOrEmpty(subFolder)) + { + defineConstants[referenceName + "." + subFolder.Replace('\\', '_') + ".TargetPath"] = targetPath; + } + } + else + { + defineConstants[targetPathDefine] = targetPath; + } + } + + /// + /// Look through the configuration data in the ProjectConfigurations property + /// to find the configuration for a project, if available. + /// + /// Name of the project that is being searched for. + /// Full configuration spec, for example "Release|Win32". + private string FindProjectConfiguration(string projectName) + { + var configuration = String.Empty; + + if (this.ProjectConfigurations != null) + { + foreach (var configItem in this.ProjectConfigurations) + { + var configProject = configItem.ItemSpec; + if (configProject.Length > projectName.Length && + configProject.StartsWith(projectName) && + configProject[projectName.Length] == '=') + { + configuration = configProject.Substring(projectName.Length + 1); + break; + } + } + } + + return configuration; + } + + /// + /// Finds the common root between two paths + /// + /// + /// + /// common root on success, empty string on failure + private static string FindCommonRoot(string path1, string path2) + { + path1 = path1.TrimEnd(Path.DirectorySeparatorChar); + path2 = path2.TrimEnd(Path.DirectorySeparatorChar); + + while (!String.IsNullOrEmpty(path1)) + { + for (var searchPath = path2; !String.IsNullOrEmpty(searchPath); searchPath = Path.GetDirectoryName(searchPath)) + { + if (path1.Equals(searchPath, StringComparison.OrdinalIgnoreCase)) + { + return searchPath; + } + } + + path1 = Path.GetDirectoryName(path1); + } + + return path1; + } + + /// + /// Finds the subfolder of a path, excluding a root and filename. + /// + /// Path to examine + /// Root that must be present + /// + /// + private static string FindSubfolder(string path, string rootPath, string fileName) + { + if (Path.GetFileName(path).Equals(fileName, StringComparison.OrdinalIgnoreCase)) + { + path = Path.GetDirectoryName(path); + } + + if (path.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase)) + { + // cut out the root and return the subpath + return path.Substring(rootPath.Length).Trim(Path.DirectorySeparatorChar); + } + + return String.Empty; + } + + private static string EnsureEndsWithBackslash(string dir) + { + if (!dir.EndsWith(DirectorySeparatorString)) + { + dir += Path.DirectorySeparatorChar; + } + + return dir; + } + } +} diff --git a/src/wix/WixToolset.BuildTasks/GenerateCompileWithObjectPath.cs b/src/wix/WixToolset.BuildTasks/GenerateCompileWithObjectPath.cs deleted file mode 100644 index a5f76618..00000000 --- a/src/wix/WixToolset.BuildTasks/GenerateCompileWithObjectPath.cs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.BuildTasks -{ - using System; - using System.Diagnostics.CodeAnalysis; - using System.Globalization; - using System.IO; - using System.Security.Cryptography; - using System.Text; - using Microsoft.Build.Framework; - using Microsoft.Build.Utilities; - - /// - /// This task generates metadata on the for compile output objects. - /// - public class GenerateCompileWithObjectPath : Task - { - /// - /// The list of files to generate outputs for. - /// - [Required] - public ITaskItem[] Compile - { - get; - set; - } - - /// - /// The list of files with ObjectPath metadata. - /// - [Output] - public ITaskItem[] CompileWithObjectPath - { - get; - private set; - } - - /// - /// The folder under which all ObjectPaths should reside. - /// - [Required] - public string IntermediateOutputPath - { - get; - set; - } - - /// - /// Generate an identifier by hashing data from the row. - /// - /// Three letter or less prefix for generated row identifier. - /// Information to hash. - /// The generated identifier. - [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.InvalidOperationException.#ctor(System.String)")] - public static string GenerateIdentifier(string prefix, params string[] args) - { - string stringData = String.Join("|", args); - byte[] data = Encoding.Unicode.GetBytes(stringData); - - // hash the data - byte[] hash; - - using (SHA1 sha1 = new SHA1CryptoServiceProvider()) - { - hash = sha1.ComputeHash(data); - } - - // build up the identifier - StringBuilder identifier = new StringBuilder(35, 35); - identifier.Append(prefix); - - // hard coded to 16 as that is the most bytes that can be used to meet the length requirements. SHA1 is 20 bytes. - for (int i = 0; i < 16; i++) - { - identifier.Append(hash[i].ToString("X2", CultureInfo.InvariantCulture.NumberFormat)); - } - - return identifier.ToString(); - } - - /// - /// Gets the full path of the directory in which the file is found. - /// - /// The file from which to extract the directory. - /// The generated identifier. - private static string GetDirectory(ITaskItem file) - { - return file.GetMetadata("RootDir") + file.GetMetadata("Directory"); - } - - /// - /// Sets the object path to use for the file. - /// - /// The file on which to set the ObjectPath metadata. - /// - /// For the same input path it will return the same ObjectPath. Case is not ignored, however that isn't a problem. - /// - private void SetObjectPath(ITaskItem file) - { - // If the source file is in the project directory or in the intermediate directory, use the intermediate directory. - if (string.IsNullOrEmpty(file.GetMetadata("RelativeDir")) || string.Compare(file.GetMetadata("RelativeDir"), this.IntermediateOutputPath, StringComparison.OrdinalIgnoreCase) == 0) - { - file.SetMetadata("ObjectPath", this.IntermediateOutputPath); - } - // Otherwise use a subdirectory of the intermediate directory. The subfolder's name is based on the full path of the folder containing the source file. - else - { - file.SetMetadata("ObjectPath", Path.Combine(this.IntermediateOutputPath, GenerateIdentifier("pth", GetDirectory(file))) + Path.DirectorySeparatorChar); - } - } - - /// - /// Gets a complete list of external cabs referenced by the given installer database file. - /// - /// True upon completion of the task execution. - public override bool Execute() - { - if (string.IsNullOrEmpty(this.IntermediateOutputPath)) - { - this.Log.LogError("IntermediateOutputPath parameter is required and cannot be empty"); - return false; - } - - if (this.Compile == null || this.Compile.Length == 0) - { - return true; - } - - this.CompileWithObjectPath = new ITaskItem[this.Compile.Length]; - for (int i = 0; i < this.Compile.Length; ++i) - { - this.CompileWithObjectPath[i] = new TaskItem(this.Compile[i].ItemSpec, this.Compile[i].CloneCustomMetadata()); - - // Do not overwrite the ObjectPath metadata if it already was set. - if (string.IsNullOrEmpty(this.CompileWithObjectPath[i].GetMetadata("ObjectPath"))) - { - this.SetObjectPath(this.CompileWithObjectPath[i]); - } - } - - return true; - } - } -} diff --git a/src/wix/WixToolset.BuildTasks/UpdateProjectReferenceMetadata.cs b/src/wix/WixToolset.BuildTasks/UpdateProjectReferenceMetadata.cs new file mode 100644 index 00000000..4de948ba --- /dev/null +++ b/src/wix/WixToolset.BuildTasks/UpdateProjectReferenceMetadata.cs @@ -0,0 +1,95 @@ +// 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.BuildTasks +{ + using System; + using System.Collections.Generic; + using System.IO; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + + /// + /// This task adds publish metadata to the appropriate project references. + /// + public class UpdateProjectReferenceMetadata : Task + { + /// + /// The list of project references that exist. + /// + [Required] + public ITaskItem[] ProjectReferences { get; set; } + + [Required] + public string IntermediateFolder { get; set; } + + [Output] + public ITaskItem[] UpdatedProjectReferences { get; private set; } + + /// + /// Finds all project references requesting publishing and updates them to publish instead of build. + /// + /// True upon completion of the task execution. + public override bool Execute() + { + var publishProjectReferences = new List(); + var intermediateFolder = Path.GetFullPath(this.IntermediateFolder); + + foreach (var projectReference in this.ProjectReferences) + { + var publish = projectReference.GetMetadata("Publish"); + var publishDir = projectReference.GetMetadata("PublishDir"); + + if (publish.Equals("true", StringComparison.OrdinalIgnoreCase) || + (String.IsNullOrWhiteSpace(publish) && !String.IsNullOrWhiteSpace(publishDir))) + { + publishDir = String.IsNullOrWhiteSpace(publishDir) ? this.CalculatePublishDirFromProjectReference(projectReference, intermediateFolder) : Path.GetFullPath(publishDir); + + this.AddPublishPropertiesToProjectReference(projectReference, publishDir); + + publishProjectReferences.Add(projectReference); + } + } + + this.UpdatedProjectReferences = publishProjectReferences.ToArray(); + + return true; + } + + private string CalculatePublishDirFromProjectReference(ITaskItem projectReference, string intermediateFolder) + { + var publishDir = Path.Combine("publish", Path.GetFileNameWithoutExtension(projectReference.ItemSpec)); + + return Path.Combine(intermediateFolder, publishDir); + } + + private void AddPublishPropertiesToProjectReference(ITaskItem projectReference, string publishDir) + { + var additionalProperties = projectReference.GetMetadata("AdditionalProperties"); + if (!String.IsNullOrWhiteSpace(additionalProperties)) + { + additionalProperties += ";"; + } + + additionalProperties += "PublishDir=" + publishDir; + + var bindPath = ToolsCommon.GetMetadataOrDefault(projectReference, "BindPath", publishDir); + + var publishTargets = projectReference.GetMetadata("PublishTargets"); + if (String.IsNullOrWhiteSpace(publishTargets)) + { + publishTargets = "Publish;GetTargetPath"; + } + else if (!publishTargets.EndsWith(";GetTargetsPath", StringComparison.OrdinalIgnoreCase)) + { + publishTargets += ";GetTargetsPath"; + } + + projectReference.SetMetadata("AdditionalProperties", additionalProperties); + projectReference.SetMetadata("BindPath", bindPath); + projectReference.SetMetadata("Targets", publishTargets); + + this.Log.LogMessage(MessageImportance.Low, "Adding publish metadata to project reference {0} Targets {1}, BindPath {2}, AdditionalProperties: {3}", + projectReference.ItemSpec, projectReference.GetMetadata("Targets"), projectReference.GetMetadata("BindPath"), projectReference.GetMetadata("AdditionalProperties")); + } + } +} diff --git a/src/wix/WixToolset.BuildTasks/WixToolset.BuildTasks.csproj b/src/wix/WixToolset.BuildTasks/WixToolset.BuildTasks.csproj index b35e68d2..56c7b016 100644 --- a/src/wix/WixToolset.BuildTasks/WixToolset.BuildTasks.csproj +++ b/src/wix/WixToolset.BuildTasks/WixToolset.BuildTasks.csproj @@ -15,17 +15,15 @@ + - - - diff --git a/src/wix/WixToolset.Sdk/Sdk/Sdk.props b/src/wix/WixToolset.Sdk/Sdk/Sdk.props index c517a1cf..b293f911 100644 --- a/src/wix/WixToolset.Sdk/Sdk/Sdk.props +++ b/src/wix/WixToolset.Sdk/Sdk/Sdk.props @@ -1,23 +1,17 @@ + - + - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + $([MSBuild]::NormalizePath($(MSBuildThisFileDirectory), '..\tools\wix.props')) + $([MSBuild]::NormalizePath($(MSBuildThisFileDirectory), '..\tools\wix.targets')) - $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\tools\wix.targets')) - - - - .NETFramework,Version=v4.8 true true true - - - - + diff --git a/src/wix/WixToolset.Sdk/Sdk/Sdk.targets b/src/wix/WixToolset.Sdk/Sdk/Sdk.targets index e21cf5c6..3940d6d5 100644 --- a/src/wix/WixToolset.Sdk/Sdk/Sdk.targets +++ b/src/wix/WixToolset.Sdk/Sdk/Sdk.targets @@ -1,15 +1,6 @@ + - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - + - - - $(DefaultItemExcludes);$(BaseOutputPath)** - $(DefaultItemExcludes);$(BaseIntermediateOutputPath)** - $(DefaultItemExcludesInProjectFolder);**/.*/** - diff --git a/src/wix/WixToolset.Sdk/WixToolset.Sdk.csproj b/src/wix/WixToolset.Sdk/WixToolset.Sdk.csproj index e25ebaa8..33fdc7b2 100644 --- a/src/wix/WixToolset.Sdk/WixToolset.Sdk.csproj +++ b/src/wix/WixToolset.Sdk/WixToolset.Sdk.csproj @@ -13,9 +13,11 @@ + + diff --git a/src/wix/WixToolset.Sdk/build/WixToolset.Sdk.props b/src/wix/WixToolset.Sdk/build/WixToolset.Sdk.props index b453361b..e70d4024 100644 --- a/src/wix/WixToolset.Sdk/build/WixToolset.Sdk.props +++ b/src/wix/WixToolset.Sdk/build/WixToolset.Sdk.props @@ -1,8 +1,11 @@ - + - $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\tools\wix.targets')) + $([MSBuild]::NormalizePath($(MSBuildThisFileDirectory), '..\tools\wix.props')) + $([MSBuild]::NormalizePath($(MSBuildThisFileDirectory), '..\tools\wix.targets')) + + diff --git a/src/wix/WixToolset.Sdk/build/WixToolset.Sdk.targets b/src/wix/WixToolset.Sdk/build/WixToolset.Sdk.targets new file mode 100644 index 00000000..ca722959 --- /dev/null +++ b/src/wix/WixToolset.Sdk/build/WixToolset.Sdk.targets @@ -0,0 +1,10 @@ + + + + + + $([MSBuild]::NormalizePath($(MSBuildThisFileDirectory), '..\tools\wix.targets')) + + + + diff --git a/src/wix/WixToolset.Sdk/tools/wix.harvest.targets b/src/wix/WixToolset.Sdk/tools/wix.harvest.targets index f3ec760b..23e4a68e 100644 --- a/src/wix/WixToolset.Sdk/tools/wix.harvest.targets +++ b/src/wix/WixToolset.Sdk/tools/wix.harvest.targets @@ -87,6 +87,10 @@ ================================================================================================== --> + + $(CoreCompileDependsOn); + Harvest + ConvertReferences; ConvertBundleReferences; @@ -136,10 +140,6 @@ - - @@ -168,10 +168,6 @@ - - @@ -254,7 +250,7 @@ diff --git a/src/wix/WixToolset.Sdk/tools/wix.props b/src/wix/WixToolset.Sdk/tools/wix.props new file mode 100644 index 00000000..2d88bc10 --- /dev/null +++ b/src/wix/WixToolset.Sdk/tools/wix.props @@ -0,0 +1,53 @@ + + + + + + true + + + + + + B7DD6F7E-DEF8-4E67-B5B7-07EF123DB6F0 + false + true + + + + + PackageReference + $(MSBuildProjectExtensionsPath)/project.assets.json + $([MSBuild]::NormalizePath($(MSBuildProjectDirectory), $(ProjectAssetsFile))) + + false + false + + + + native + v0.0 + native,Version=v0.0 + + + + $(NoWarn);NU1702 + + + + $(AssetTargetFallback);net6.0-windows;net6.0;net5.0-windows;net5.0;netcoreapp3.1;netcoreapp3.0;netcoreapp2.0;net462;net47;net471;net472;net48;native + + + + false + + + + + false + + + Never + + + diff --git a/src/wix/WixToolset.Sdk/tools/wix.signing.targets b/src/wix/WixToolset.Sdk/tools/wix.signing.targets index 7c25175f..5a4ea98d 100644 --- a/src/wix/WixToolset.Sdk/tools/wix.signing.targets +++ b/src/wix/WixToolset.Sdk/tools/wix.signing.targets @@ -27,31 +27,31 @@ ================================================================================================== --> - + <_InternalSignDependsOn Condition=" '$(OutputType)' == 'Module' "> GetMsmsToSign; InternalSignMsm; - - + + <_InternalSignDependsOn Condition=" '$(OutputType)' == 'Package' "> GetCabsToSign; GetMsiToSign; InternalSignCabs; InscribeMsi; InternalSignMsi; - - + + <_InternalSignDependsOn Condition=" '$(OutputType)' == 'Bundle' "> GetContainersToSign; InternalSignContainers; InscribeBundleEngine; InternalSignBundleEngine; InscribeBundle; InternalSignBundle; - + - WixBuild; + CoreCompile; CalculateSignTargetFiles; BeforeSigning; - $(InternalSignDependsOn); + $(_InternalSignDependsOn); AfterSigning diff --git a/src/wix/WixToolset.Sdk/tools/wix.targets b/src/wix/WixToolset.Sdk/tools/wix.targets index 6b15769a..7497a9f8 100644 --- a/src/wix/WixToolset.Sdk/tools/wix.targets +++ b/src/wix/WixToolset.Sdk/tools/wix.targets @@ -1,17 +1,19 @@ - + true + + @@ -31,21 +33,17 @@ $(MSBuildThisFileDirectory)wix.signing.targets - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - $(MSBuildAllProjects);$(WixHarvestTargetsPath) - $(MSBuildAllProjects);$(WixSigningTargetsPath) - $(MSBuildAllProjects);$(CustomBeforeWixTargets) - $(MSBuildAllProjects);$(CustomAfterWixTargets) - + + + + @@ -59,11 +57,16 @@ Package + x86,x64,ARM,ARM64,AnyCPU full + + <_VCLibCurrentVersion>14.0 + + + + + + + + + + + <_TargetFrameworkDirectories /> + + + + true + + + + + + false + + $(TargetDir) @@ -96,11 +125,11 @@ @@ -113,9 +142,13 @@ - - - + + + + + + + @@ -125,10 +158,6 @@ - - - - $(MSBuildProjectFile).BindContentsFileList.txt $(MSBuildProjectFile).BindOutputsFileList.txt @@ -144,11 +173,11 @@ @@ -208,231 +237,89 @@ - - - - <_PleaseSetThisInProjectFile>Please set this in the project file before the <Import> of the wix.targets file. - <_OutputTypeDescription>Possible values are: 'Package', 'Module', 'Library', 'Bundle', 'IntermediatePostLink'. $(_PleaseSetThisInProjectFile) - - - - - - - - - - - - - - - - - BuildOnlySettings; - PrepareForBuild; - PreBuildEvent; - - WixBuild; - Signing; - - GetTargetPath; - PrepareForRun; - IncrementalClean; - PostBuildEvent - - - - - + *********************************************************************************************** + *********************************************************************************************** + Resolve References Section + *********************************************************************************************** + *********************************************************************************************** + --> + ================================================================================================ + --> BeforeResolveReferences; AssignProjectConfiguration; ResolveProjectReferences; + FindInvalidProjectReferences; + ResolveNativeProjectReferences; + _ConvertResolvedProjectReferencesIntoWixConstructs; ResolveWixLibraryReferences; ResolveWixExtensionReferences; + _CreateProjectDefineConstants; AfterResolveReferences - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + + - - + + <_MSBuildProjectReferenceExistent Remove='@(_WixPublishProjectReferences)' /> + <_MSBuildProjectReferenceExistent Include='@(_WixPublishProjectReferences)' /> + - - + + <_MSBuildProjectReferenceExistent Condition="'%(_MSBuildProjectReferenceExistent.SetTargetFramework)' == ''"> + true + %(_MSBuildProjectReferenceExistent.UndefineProperties);TargetFramework + + + - - + - - - - + - - + - - - - - + - + Converts _ResolvedProjectReferencePaths (which are the outputs of the ProjectReferences that + normally would be passed through ResolveAssemblyReferences then be passed to the C# compiler + as references) into the appropriate WiX constructs. For example, references to .wixlibs are + converted into WixLibrary items. In the end, _ResolvedProjectReferencePaths is emptied to + prevent ResolveAssemblyReferences and other non-sensical targets (from a WiX point of view) + from doing work. + + [IN] + @(_ResolvedProjectReferencePaths) - Resolved project references. + $(VSProjectConfigurations) - map of project names to configurations, provided by VS when building in the IDE. + + [OUT] + $(ProjectReferenceDefineConstants) - Define constants from project references. + @(LinkerBindInputPaths) - Bind paths from project references. + @(WixLibrary) - Target paths from .vcxproj outputs added. + @(_WixReferencedProjectOutputs) - Copy of _ResolvedProjectReferencePaths for use in up-to-date checks. + @(_ResolvedProjectReferencePaths) - All resolved reference paths emptied. + ================================================================================================ + --> + + <_ConvertResolvedProjectReferencesIntoWixConstructs> + + + + + + <_WixReferencedProjectOutputs Include="@(_ResolvedProjectReferencePaths)" /> + - - + + + <_ResolvedProjectReferencePaths Remove="@(WixLibrary)" /> + - + + + + + + + + <_ResolvedProjectReferencePaths Remove="@(_ResolvedProjectReferencePaths)" /> + + ================================================================================================ + --> + DependsOnTargets="$(ResolveWixLibraryReferencesDependsOn)"> - - - - + + + $(ReferencePaths); + {HintPathFromItem}; + {RawFileName}; + $(WixExtDir) + + + WixReferences="@(WixLibrary)" + SearchPaths="$(WixLibrarySearchPaths)" + SearchFilenameExtensions=".wixlib" + Condition=" '@(WixLibrary)' != ''"> - - + ================================================================================================ + --> @@ -540,14 +470,6 @@ DependsOnTargets="$(ResolveWixExtensionReferencesDependsOn)" Condition=" '@(WixExtension)' != ''"> - + + + + + + + + <_CulturedTargetPath Include="$(TargetDir)%(CultureGroup.OutputFolder)$(TargetFileName)" /> + <_CulturedTargetPdbPath Include="$(TargetPdbDir)%(CultureGroup.OutputFolder)$(TargetPdbFileName)" /> + + + + @(_CulturedTargetPath) + @(_CulturedTargetPdbPath) + + + ================================================================================================ + CreateManifestResourceNames + + Converts the EmbeddedResource into _WixLocalizationFile to disable satellite assembly + generation in common targets. + ================================================================================================ + --> + + + + <_WixLocalizationFile Include="@(EmbeddedResource)"> + wxl + + + + + + ================================================================================================ + GetTargetPath - OVERRIDE DependsOn + + This stand-alone target returns the name of the build product (i.e. MSI, MSM) that would be + produced if we built this project. + ================================================================================================ + --> - - ResolveReferences; - BeforeCompile; - _TimeStampBeforeCompile; + $(GetTargetPathDependsOn);AssignTargetPaths + - CalculateDefineConstants; - Harvest; + - GenerateCompileWithObjectPath; + + + AssignTargetPaths; ReadPreviousBindInputsAndBuiltOutputs; - - CoreWixBuild; - - UpdateLinkFileWrites; - _TimeStampAfterCompile; - AfterCompile - + _CreateProjectDefineConstants; + $(CoreCompileDependsOn) + - - - $([System.IO.Path]::GetFullPath($(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(TargetFileName))) - $(IntermediateOutputPath)%(CultureGroup.OutputFolder)$(TargetPdbFileName) - + Adds properties as define constants passed into the compiler. + ================================================================================================ + --> - ResolveReferences + <_CreateProjectDefineConstantsDependsOn> + Name="_CreateProjectDefineConstants" + DependsOnTargets="$(_CreateProjectDefineConstantsDependsOn)"> @@ -748,100 +726,11 @@ $(SolutionDefineConstants);SolutionName=$(SolutionName) $(SolutionDefineConstants);SolutionPath=$(SolutionPath) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_CulturedTargetPath Include="$(TargetDir)%(CultureGroup.OutputFolder)$(TargetFileName)" /> - <_CulturedTargetPdbPath Include="$(TargetPdbDir)%(CultureGroup.OutputFolder)$(TargetPdbFileName)" /> - - - - @(_CulturedTargetPath) - @(_CulturedTargetPdbPath) - + ================================================================================================ + --> @@ -878,26 +767,27 @@ + ================================================================================================ + --> + Name="UpdateFileWritesWithBindInformation" + AfterTargets="CoreCompile"> @@ -913,19 +803,30 @@ + *********************************************************************************************** + *********************************************************************************************** + AllProjectOutputGroups Section + *********************************************************************************************** + *********************************************************************************************** + --> + The targets below drive output groups, which provide generic information about a + project's inputs (e.g., content files, compilation sources, etc.) and built outputs + (e.g., built EXE/MSI, wixpdb, content files, etc.) + + Each target may produce two kinds of items: outputs and dependencies. Outputs are + items from the current project; dependencies are items that are brought into the + current project as a result of referencing other projects or components. + + For both outputs and dependencies, the Include attribute + specifies the location of the output/dependency; it must be a full path. Any number + of additional attributes may be placed on an output/dependency item. + ================================================================================================ + --> + This is the key output for the BuiltProjectOutputGroup and is meant to be read directly from the IDE. + Reading an item is faster than invoking a target. + --> true @@ -947,10 +848,10 @@ + ================================================================================================ + BuiltProjectOutputGroup - OVERRIDE Target + ================================================================================================ + --> PrepareForBuild;AssignTargetPaths @@ -961,7 +862,7 @@ - + @@ -981,12 +882,12 @@ + ================================================================================================ + --> AssignTargetPaths @@ -1001,20 +902,29 @@ + + Copy all build outputs, satellites and other necessary files to the final directory. + ================================================================================================ + --> true + false + false @@ -1041,16 +951,15 @@ Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)" UseHardlinksIfPossible="$(CreateHardLinksForCopyFilesToOutputDirectoryIfPossible)" - Condition="'$(CopyBuildOutputToOutputDirectory)' == 'true' and '$(SkipCopyBuildProduct)' != 'true'" - > + UseSymboliclinksIfPossible="$(CreateSymbolicLinksForCopyFilesToOutputDirectoryIfPossible)" + ErrorIfLinkFails="$(ErrorIfLinkFailsForCopyFilesToOutputDirectory)" + Condition="'$(CopyBuildOutputToOutputDirectory)' == 'true' and '$(SkipCopyBuildProduct)' != 'true'"> - - - + + $(DefaultItemExcludes);$(BaseOutputPath)** + $(DefaultItemExcludes);$(BaseIntermediateOutputPath)** + $(DefaultItemExcludesInProjectFolder);**/.*/** + -- cgit v1.2.3-55-g6feb