diff --git a/src/internal/WixInternal.MSTestSupport/MsbuildUtilities.cs b/src/internal/WixInternal.MSTestSupport/MsbuildUtilities.cs index 8f3fecd9f..516143dd0 100644 --- a/src/internal/WixInternal.MSTestSupport/MsbuildUtilities.cs +++ b/src/internal/WixInternal.MSTestSupport/MsbuildUtilities.cs @@ -30,7 +30,8 @@ public static MsbuildRunnerResult BuildProject(BuildSystem buildSystem, string p if (binlog) { - MsbuildUtilities.GetQuotedSwitch(buildSystem, "bl", Path.ChangeExtension(projectPath, ".binlog")); + var binlogSwitch = MsbuildUtilities.GetQuotedSwitch(buildSystem, "bl", Path.ChangeExtension(projectPath, ".binlog")); + allArgs.Add(binlogSwitch); } if (arguments != null) diff --git a/src/test/wix/TestData/CsprojWpfNetCore/CsprojWpfNetCore.csproj b/src/test/wix/TestData/CsprojWpfNetCore/CsprojWpfNetCore.csproj index e7061e747..b1bf87815 100644 --- a/src/test/wix/TestData/CsprojWpfNetCore/CsprojWpfNetCore.csproj +++ b/src/test/wix/TestData/CsprojWpfNetCore/CsprojWpfNetCore.csproj @@ -2,7 +2,7 @@ WinExe - net5.0-windows7.0 + net8.0-windows7.0 enable true diff --git a/src/test/wix/TestData/WixprojPackageCsprojWpfNetCore/Package.wxs b/src/test/wix/TestData/WixprojPackageCsprojWpfNetCore/Package.wxs index 8bd191d8b..3671c0ca8 100644 --- a/src/test/wix/TestData/WixprojPackageCsprojWpfNetCore/Package.wxs +++ b/src/test/wix/TestData/WixprojPackageCsprojWpfNetCore/Package.wxs @@ -7,9 +7,9 @@ - - - + + + diff --git a/src/test/wix/TestData/WixprojPackageCsprojWpfNetCore/WixprojPackageCsprojWebApplicationNetCore.wixproj b/src/test/wix/TestData/WixprojPackageCsprojWpfNetCore/WixprojPackageCsprojWpfNetCore.wixproj similarity index 52% rename from src/test/wix/TestData/WixprojPackageCsprojWpfNetCore/WixprojPackageCsprojWebApplicationNetCore.wixproj rename to src/test/wix/TestData/WixprojPackageCsprojWpfNetCore/WixprojPackageCsprojWpfNetCore.wixproj index 686092b6f..df3332420 100644 --- a/src/test/wix/TestData/WixprojPackageCsprojWpfNetCore/WixprojPackageCsprojWebApplicationNetCore.wixproj +++ b/src/test/wix/TestData/WixprojPackageCsprojWpfNetCore/WixprojPackageCsprojWpfNetCore.wixproj @@ -1,17 +1,13 @@ - - - - - - + + + - diff --git a/src/wix/WixToolset.BuildTasks/ILogger.cs b/src/wix/WixToolset.BuildTasks/ILogger.cs new file mode 100644 index 000000000..16db691ea --- /dev/null +++ b/src/wix/WixToolset.BuildTasks/ILogger.cs @@ -0,0 +1,13 @@ +// 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 +{ + public interface ILogger + { + bool HasLoggedErrors { get; } + + void LogError(string message); + + void LogWarning(string message); + } +} diff --git a/src/wix/WixToolset.BuildTasks/MSBuildLoggerAdapter.cs b/src/wix/WixToolset.BuildTasks/MSBuildLoggerAdapter.cs new file mode 100644 index 000000000..5382254c6 --- /dev/null +++ b/src/wix/WixToolset.BuildTasks/MSBuildLoggerAdapter.cs @@ -0,0 +1,28 @@ +// 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 Microsoft.Build.Utilities; + + internal class MSBuildLoggerAdapter : ILogger + { + private readonly TaskLoggingHelper log; + + public MSBuildLoggerAdapter(TaskLoggingHelper log) + { + this.log = log; + } + + public bool HasLoggedErrors => this.log.HasLoggedErrors; + + public void LogError(string message) + { + this.log.LogError(message); + } + + public void LogWarning(string message) + { + this.log.LogWarning(message); + } + } +} diff --git a/src/wix/WixToolset.BuildTasks/ResolveInstallerPlatform.cs b/src/wix/WixToolset.BuildTasks/ResolveInstallerPlatform.cs new file mode 100644 index 000000000..5e551d7b1 --- /dev/null +++ b/src/wix/WixToolset.BuildTasks/ResolveInstallerPlatform.cs @@ -0,0 +1,174 @@ +// 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.Linq; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + + /// + /// This task calculates the InstallerPlatform from the RuntimeIdentifier, + /// InitialInstallerPlatform and Platform properties. + /// + public class ResolveInstallerPlatform : Task + { + private readonly ILogger logger; + + /// + /// Default constructor. + /// + public ResolveInstallerPlatform() + { + this.logger = new MSBuildLoggerAdapter(this.Log); + } + + /// + /// Constructor for dependency injection of logger used by unit tests. + /// + public ResolveInstallerPlatform(ILogger logger) + { + this.logger = logger; + } + + /// + /// The optional RuntimeIdentifier property. + /// + public string RuntimeIdentifier { private get; set; } + + /// + /// The optional InitialInstallerPlatform property. + /// + public string InitialInstallerPlatform { private get; set; } + + /// + /// The InstallerPlatform property. + /// + public string InstallerPlatform { private get; set; } + + /// + /// The optional Platform property. + /// + public string Platform { private get; set; } + + /// + /// The resolved InstallerPlatform. + /// + [Output] + public string ResolvedInstallerPlatform { get; private set; } + + /// + /// The optionally resolved Platform. + /// + [Output] + public string ResolvedPlatform { get; private set; } + + /// + /// Convert the input properties into output items. + /// + /// True upon completion of the task execution. + public override bool Execute() + { + if (String.IsNullOrEmpty(this.RuntimeIdentifier)) + { + this.ResolvedInstallerPlatform = this.InstallerPlatform; + } + else if (this.ValidateWindowsRuntimeIdentifier(this.RuntimeIdentifier, out var platform)) + { + if (!String.IsNullOrEmpty(this.InitialInstallerPlatform) && !String.Equals(this.InitialInstallerPlatform, platform, StringComparison.OrdinalIgnoreCase)) + { + this.logger.LogError($"The RuntimeIdentifier '{this.RuntimeIdentifier}' resolves to platform '{platform}', which conflicts with the provided InstallerPlatform '{this.InitialInstallerPlatform}'."); + } + else + { + this.ResolvedInstallerPlatform = platform; + } + } + + // If Platform was a generic value, resolve it to the resolved installer platform. + if (String.IsNullOrEmpty(this.Platform) + || this.Platform.Equals("AnyCPU", StringComparison.OrdinalIgnoreCase) + || this.Platform.Equals("Any CPU", StringComparison.OrdinalIgnoreCase) + || this.Platform.Equals("Win32", StringComparison.OrdinalIgnoreCase)) + { + this.ResolvedPlatform = this.ResolvedInstallerPlatform; + } + else if (!this.Platform.Equals(this.ResolvedInstallerPlatform, StringComparison.OrdinalIgnoreCase)) + { + this.logger.LogWarning($"The provided Platform '{this.Platform}' does not match the resolved InstallerPlatform '{this.ResolvedInstallerPlatform}'. The output will be built using '{this.ResolvedInstallerPlatform}'."); + } + + return !this.logger.HasLoggedErrors; + } + + private bool ValidateWindowsRuntimeIdentifier(string runtimeIdentifier, out string platform) + { + platform = null; + + var ridParts = runtimeIdentifier.Split('-'); + if (ridParts.Length < 2) + { + this.logger.LogError($"The RuntimeIdentifier '{runtimeIdentifier}' is not valid."); + + return false; + } + + var os = ridParts[0]; + + if (!os.StartsWith("win", StringComparison.OrdinalIgnoreCase) || (os.Length > 3 && !Int32.TryParse(os.Substring(3), out var _))) + { + this.logger.LogError($"The RuntimeIdentifier '{runtimeIdentifier}' is not a valid Windows RuntimeIdentifier."); + + return false; + } + + // Ensure there is only one platform specified in the RID. + foreach (var part in ridParts.Skip(1)) + { + string platformPart; + switch (part.ToLowerInvariant()) + { + case "x86": + case "win32": + platformPart = "x86"; + break; + + case "x64": + case "amd64": + platformPart = "x64"; + break; + + case "arm": + case "arm32": + platformPart = "arm"; + break; + + case "arm64": + platformPart = "arm64"; + break; + + default: + continue; + } + + if (String.IsNullOrEmpty(platform)) + { + platform = platformPart; + } + else // there can be only one platform in the RID. + { + this.logger.LogError($"The RuntimeIdentifier '{runtimeIdentifier}' specifies multiple platforms which is not supported."); + } + } + + if (String.IsNullOrEmpty(platform)) + { + this.logger.LogError($"The RuntimeIdentifier '{runtimeIdentifier}' does not specify a valid platform."); + + return false; + } + + return true; + } + } +} diff --git a/src/wix/WixToolset.BuildTasks/UpdateProjectReferenceMetadata.cs b/src/wix/WixToolset.BuildTasks/UpdateProjectReferenceMetadata.cs index d391bdb99..238ab02d3 100644 --- a/src/wix/WixToolset.BuildTasks/UpdateProjectReferenceMetadata.cs +++ b/src/wix/WixToolset.BuildTasks/UpdateProjectReferenceMetadata.cs @@ -364,7 +364,26 @@ public static ProjectReferenceFacade CreateFacade(ITaskItem projectReference, Ta } } - return new ProjectReferenceFacade(projectReference, configurations, null, platforms, null, targetFrameworks, null, runtimeIdentifiersValue.Values, null, publishBaseDir); + // If the Properties metadata is specified MSBuild will not use TargetFramework inference and require explicit declaration of + // our expansions (Configurations, Platforms, TargetFrameworks, RuntimeIdentifiers). Rather that try to interoperate, we'll + // warn the user that we're disabling our expansion behavior. + var propertiesValue = projectReference.GetMetadata("Properties"); + + if (!String.IsNullOrWhiteSpace(propertiesValue) && (configurationsValue.HadValue || platformsValue.HadValue || targetFrameworksValue.HadValue || runtimeIdentifiersValue.HadValue)) + { + logger.LogWarning( + "ProjectReference '{0}' specifies 'Properties' metadata. " + + "That overrides ProjectReference expansion so the 'Configurations', 'Platforms', 'TargetFrameworks', and 'RuntimeIdentifiers' metadata was ignored. " + + "Instead, use the 'AdditionalProperties' metadata to pass properties to the referenced project without disabling ProjectReference expansion.", + projectReference.ItemSpec); + + // Return a facade that does not participate in expansion. + return new ProjectReferenceFacade(projectReference, Array.Empty(), null, Array.Empty(), null, Array.Empty(), null, Array.Empty(), null, publishBaseDir); + } + else + { + return new ProjectReferenceFacade(projectReference, configurations, null, platforms, null, targetFrameworks, null, runtimeIdentifiersValue.Values, null, publishBaseDir); + } } public string CalculatePublishDir() diff --git a/src/wix/WixToolset.Sdk/tools/wix.targets b/src/wix/WixToolset.Sdk/tools/wix.targets index 4de5835fe..ac95a8d9b 100644 --- a/src/wix/WixToolset.Sdk/tools/wix.targets +++ b/src/wix/WixToolset.Sdk/tools/wix.targets @@ -40,6 +40,7 @@ + @@ -165,6 +166,7 @@ + <_InitialInstallerPlatform>$(InstallerPlatform) x86 $(Platform) @@ -178,6 +180,60 @@ + + + + ResolveInstallerPlatform; + $(PrepareForBuildDependsOn) + + + + + + + + + + + + + $(_ResolvedInstallerPlatform) + $(_ResolvedPlatform) + + + + TargetFramework=net8.0;RuntimeIdentifier=win-x64 + + + + + diff --git a/src/wix/test/WixToolsetTest.Sdk/TestData/Wixlib/WithBadFiles/Library.wxs b/src/wix/test/WixToolsetTest.Sdk/TestData/Wixlib/WithBadFiles/Library.wxs index bcf79a1f0..f946fcc62 100644 --- a/src/wix/test/WixToolsetTest.Sdk/TestData/Wixlib/WithBadFiles/Library.wxs +++ b/src/wix/test/WixToolsetTest.Sdk/TestData/Wixlib/WithBadFiles/Library.wxs @@ -1,7 +1,7 @@ - - - + + +