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 @@
-
-
-
+
+
+