diff --git a/src/Build.UnitTests/Evaluation/Expander_Tests.cs b/src/Build.UnitTests/Evaluation/Expander_Tests.cs index c39c1c358aa..095d7515200 100644 --- a/src/Build.UnitTests/Evaluation/Expander_Tests.cs +++ b/src/Build.UnitTests/Evaluation/Expander_Tests.cs @@ -5207,5 +5207,58 @@ public void PropertyFunctionRegisterBuildCheck() logger.AllBuildEvents.Count.ShouldBe(1); } } + + /// + /// Test ToolLocationHelper.GetPlatformSDKLocation fast path + /// + [Fact] + public void PropertyFunctionToolLocationHelperGetPlatformSDKLocation() + { + PropertyDictionary pg = new PropertyDictionary(); + + Expander expander = new Expander(pg, FileSystems.Default); + + // This should use the fast path and not throw even if the SDK doesn't exist + string result = expander.ExpandIntoStringLeaveEscaped("$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPlatformSDKLocation('Windows', '10.0'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); + + // The result could be empty if SDK is not installed, but it should not throw + result.ShouldNotBeNull(); + } + + /// + /// Test ToolLocationHelper.GetPlatformSDKDisplayName fast path + /// + [Fact] + public void PropertyFunctionToolLocationHelperGetPlatformSDKDisplayName() + { + PropertyDictionary pg = new PropertyDictionary(); + + Expander expander = new Expander(pg, FileSystems.Default); + + // This should use the fast path and not throw even if the SDK doesn't exist + string result = expander.ExpandIntoStringLeaveEscaped("$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPlatformSDKDisplayName('Windows', '10.0'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); + + // The result could be a generated default name if SDK is not installed, but it should not throw + result.ShouldNotBeNull(); + } + + /// + /// Test that both ToolLocationHelper methods work together in the same property expansion + /// + [Fact] + public void PropertyFunctionToolLocationHelperBothMethodsTogether() + { + PropertyDictionary pg = new PropertyDictionary(); + + Expander expander = new Expander(pg, FileSystems.Default); + + // Test both methods in sequence + string result1 = expander.ExpandIntoStringLeaveEscaped("$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPlatformSDKLocation('Windows', '10.0'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); + string result2 = expander.ExpandIntoStringLeaveEscaped("$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPlatformSDKDisplayName('Windows', '10.0'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); + + // Both should complete without throwing + result1.ShouldNotBeNull(); + result2.ShouldNotBeNull(); + } } } diff --git a/src/Build/Evaluation/Expander/WellKnownFunctions.cs b/src/Build/Evaluation/Expander/WellKnownFunctions.cs index e35c847123f..d0eecaf0a64 100644 --- a/src/Build/Evaluation/Expander/WellKnownFunctions.cs +++ b/src/Build/Evaluation/Expander/WellKnownFunctions.cs @@ -898,6 +898,35 @@ internal static bool TryExecuteWellKnownFunction(string methodName, Type receive } } } + else if (receiverType.FullName == "Microsoft.Build.Utilities.ToolLocationHelper") + { + if (string.Equals(methodName, "GetPlatformSDKLocation", StringComparison.OrdinalIgnoreCase)) + { + if (ParseArgs.TryGetArgs(args, out string? arg0, out string? arg1)) + { + // Use reflection to invoke the method, but avoid the expensive binding process + var method = receiverType.GetMethod("GetPlatformSDKLocation", new[] { typeof(string), typeof(string) }); + if (method != null) + { + returnVal = method.Invoke(null, new object?[] { arg0, arg1 }); + return true; + } + } + } + else if (string.Equals(methodName, "GetPlatformSDKDisplayName", StringComparison.OrdinalIgnoreCase)) + { + if (ParseArgs.TryGetArgs(args, out string? arg0, out string? arg1)) + { + // Use reflection to invoke the method, but avoid the expensive binding process + var method = receiverType.GetMethod("GetPlatformSDKDisplayName", new[] { typeof(string), typeof(string) }); + if (method != null) + { + returnVal = method.Invoke(null, new object?[] { arg0, arg1 }); + return true; + } + } + } + } else if (string.Equals(methodName, nameof(Regex.Replace), StringComparison.OrdinalIgnoreCase) && args.Length == 3) { if (ParseArgs.TryGetArgs(args, out string? arg1, out string? arg2, out string? arg3) && arg1 != null && arg2 != null && arg3 != null)