From d9ad3577ad4b351778de25a370630258d8fc4cbc Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Wed, 24 Apr 2024 07:50:09 +0000 Subject: [PATCH 1/4] Fix "Unable to translate Unicode character" --- src/Tasks/Microsoft.Common.CurrentVersion.targets | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Tasks/Microsoft.Common.CurrentVersion.targets b/src/Tasks/Microsoft.Common.CurrentVersion.targets index 1c931d712d0..60bc769d75f 100644 --- a/src/Tasks/Microsoft.Common.CurrentVersion.targets +++ b/src/Tasks/Microsoft.Common.CurrentVersion.targets @@ -5579,6 +5579,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. File="$(IntermediateOutputPath)$(CleanFile)" Lines="@(_CleanUniqueRemainingFileWritesAfterIncrementalClean)" Condition="'@(_CleanUnfilteredPriorFileWrites)'!='@(_CleanUniqueRemainingFileWritesAfterIncrementalClean)'" + Encoding="Unicode" Overwrite="true"/> From e0fea3a8c5a3da6d6105dc9d7de7f2f79c114fce Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Wed, 24 Apr 2024 13:35:41 +0000 Subject: [PATCH 2/4] Half-fix --- .../Microsoft.Common.CurrentVersion.targets | 5 +-- src/Tasks/ShortenAndHashProjectName.cs | 35 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 src/Tasks/ShortenAndHashProjectName.cs diff --git a/src/Tasks/Microsoft.Common.CurrentVersion.targets b/src/Tasks/Microsoft.Common.CurrentVersion.targets index 60bc769d75f..2c6592add66 100644 --- a/src/Tasks/Microsoft.Common.CurrentVersion.targets +++ b/src/Tasks/Microsoft.Common.CurrentVersion.targets @@ -389,7 +389,9 @@ Copyright (C) Microsoft Corporation. All rights reserved. $(MSBuildProjectFile) - $(MSBuildProjectFile.Substring(0,8)).$([MSBuild]::StableStringHash($(MSBuildProjectFile)).ToString("X8")) + + + $(MSBuildCopyMarkerName).Up2Date @@ -5579,7 +5581,6 @@ Copyright (C) Microsoft Corporation. All rights reserved. File="$(IntermediateOutputPath)$(CleanFile)" Lines="@(_CleanUniqueRemainingFileWritesAfterIncrementalClean)" Condition="'@(_CleanUnfilteredPriorFileWrites)'!='@(_CleanUniqueRemainingFileWritesAfterIncrementalClean)'" - Encoding="Unicode" Overwrite="true"/> diff --git a/src/Tasks/ShortenAndHashProjectName.cs b/src/Tasks/ShortenAndHashProjectName.cs new file mode 100644 index 00000000000..e28573a9e70 --- /dev/null +++ b/src/Tasks/ShortenAndHashProjectName.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Microsoft.Build.Utilities; + +#nullable disable + +namespace Microsoft.Build.Tasks +{ + // For a long MSBuildProjectFile let's shorten this to 17 chars - using the first 8 chars of the filename and a filename hash. + public sealed class ShortenAndHashProjectName : Task + { + [Required] + public string ProjectName { get; set; } + + [Output] + public string ShortProjectName { get; set; } + + public override bool Execute() + { + if (ProjectName.Length <= 17) + { + ShortProjectName = ProjectName; + return true; + } + + // if the last char of string is a surrogate, cutting it in half would confuse encoder + int length = char.IsHighSurrogate(ProjectName[7]) ? 9 : 8; + string truncatedProjectName = ProjectName.Substring(0, length); + string originalProjectNameHash = StableStringHash(ProjectName); + ShortProjectName = $"{truncatedProjectName}.{originalProjectNameHash}".ToString("X8"); + return true; + } +} \ No newline at end of file From 8fc3eede0ee5579bbe3f59317e99e0602783b6aa Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Wed, 24 Apr 2024 15:19:20 +0000 Subject: [PATCH 3/4] Expose SubstringByTextElements in IntrinsicFunctions + test. --- .../Evaluation/Expander_Tests.cs | 12 +++++++ src/Build/Evaluation/IntrinsicFunctions.cs | 7 ++++ .../Microsoft.Common.CurrentVersion.targets | 6 ++-- src/Tasks/ShortenAndHashProjectName.cs | 35 ------------------- 4 files changed, 21 insertions(+), 39 deletions(-) delete mode 100644 src/Tasks/ShortenAndHashProjectName.cs diff --git a/src/Build.UnitTests/Evaluation/Expander_Tests.cs b/src/Build.UnitTests/Evaluation/Expander_Tests.cs index 4af483bd8e8..ccc15f1a584 100644 --- a/src/Build.UnitTests/Evaluation/Expander_Tests.cs +++ b/src/Build.UnitTests/Evaluation/Expander_Tests.cs @@ -4380,6 +4380,18 @@ public void PropertyFunctionCheckFeatureAvailability(string featureName, string Assert.Equal(availability, result); } + [Theory] + [InlineData("\u3407\ud840\udc60\ud86a\ude30\ud86e\udc0a\ud86e\udda0\ud879\udeae\u2fd5\u0023", 0, 3, "\u3407\ud840\udc60\ud86a\ude30")] + [InlineData("\u3407\ud840\udc60\ud86a\ude30\ud86e\udc0a\ud86e\udda0\ud879\udeae\u2fd5\u0023", 2, 5, "\ud86a\ude30\ud86e\udc0a\ud86e\udda0\ud879\udeae\u2fd5")] + public void SubstringByTextElements(string featureName, int start, int length, string expected) + { + var expander = new Expander(new PropertyDictionary(), FileSystems.Default); + + var result = expander.ExpandIntoStringLeaveEscaped($"$([MSBuild]::SubstringByTextElements({featureName}, {start}, {length}))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); + + Assert.Equal(expected, result); + } + [Fact] public void PropertyFunctionIntrinsicFunctionGetCurrentToolsDirectory() { diff --git a/src/Build/Evaluation/IntrinsicFunctions.cs b/src/Build/Evaluation/IntrinsicFunctions.cs index 92c09ec6c01..64245d70a8b 100644 --- a/src/Build/Evaluation/IntrinsicFunctions.cs +++ b/src/Build/Evaluation/IntrinsicFunctions.cs @@ -9,6 +9,7 @@ using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; +using System.Globalization; using Microsoft.Build.Framework; using Microsoft.Build.Internal; @@ -627,6 +628,12 @@ internal static bool AreFeaturesEnabled(Version wave) return ChangeWaves.AreFeaturesEnabled(wave); } + internal static string SubstringByTextElements(string input, int start, int length) + { + StringInfo stringInfo = new StringInfo(input); + return stringInfo.SubstringByTextElements(start, length); + } + internal static string CheckFeatureAvailability(string featureName) { return Features.CheckFeatureAvailability(featureName).ToString(); diff --git a/src/Tasks/Microsoft.Common.CurrentVersion.targets b/src/Tasks/Microsoft.Common.CurrentVersion.targets index 2c6592add66..3fc9a9c4d00 100644 --- a/src/Tasks/Microsoft.Common.CurrentVersion.targets +++ b/src/Tasks/Microsoft.Common.CurrentVersion.targets @@ -388,10 +388,8 @@ Copyright (C) Microsoft Corporation. All rights reserved. $(MSBuildProjectFile) - - - - + + $([MSBuild]::SubstringByTextElements($(MSBuildProjectFile), 0, 8)).$([MSBuild]::StableStringHash($(MSBuildProjectFile)).ToString("X8")) $(MSBuildCopyMarkerName).Up2Date diff --git a/src/Tasks/ShortenAndHashProjectName.cs b/src/Tasks/ShortenAndHashProjectName.cs deleted file mode 100644 index e28573a9e70..00000000000 --- a/src/Tasks/ShortenAndHashProjectName.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using Microsoft.Build.Utilities; - -#nullable disable - -namespace Microsoft.Build.Tasks -{ - // For a long MSBuildProjectFile let's shorten this to 17 chars - using the first 8 chars of the filename and a filename hash. - public sealed class ShortenAndHashProjectName : Task - { - [Required] - public string ProjectName { get; set; } - - [Output] - public string ShortProjectName { get; set; } - - public override bool Execute() - { - if (ProjectName.Length <= 17) - { - ShortProjectName = ProjectName; - return true; - } - - // if the last char of string is a surrogate, cutting it in half would confuse encoder - int length = char.IsHighSurrogate(ProjectName[7]) ? 9 : 8; - string truncatedProjectName = ProjectName.Substring(0, length); - string originalProjectNameHash = StableStringHash(ProjectName); - ShortProjectName = $"{truncatedProjectName}.{originalProjectNameHash}".ToString("X8"); - return true; - } -} \ No newline at end of file From 259728cf6522ec844498802d948feaa40c16b19a Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com> Date: Thu, 25 Apr 2024 08:05:27 +0000 Subject: [PATCH 4/4] Feedback. --- src/Build/Evaluation/Expander.cs | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/Build/Evaluation/Expander.cs b/src/Build/Evaluation/Expander.cs index c8c3d32f072..53aa7ba7b8a 100644 --- a/src/Build/Evaluation/Expander.cs +++ b/src/Build/Evaluation/Expander.cs @@ -4187,6 +4187,14 @@ private bool TryExecuteWellKnownFunction(out object returnVal, object objectInst return true; } } + else if (string.Equals(_methodMethodName, nameof(IntrinsicFunctions.SubstringByTextElements), StringComparison.OrdinalIgnoreCase)) + { + if (TryGetArgs(args, out string arg0, out int arg1, out int arg2)) + { + returnVal = IntrinsicFunctions.SubstringByTextElements(arg0, arg1, arg2); + return true; + } + } else if (string.Equals(_methodMethodName, nameof(IntrinsicFunctions.CheckFeatureAvailability), StringComparison.OrdinalIgnoreCase)) { if (TryGetArg(args, out string arg0)) @@ -4518,6 +4526,32 @@ private bool TryGetArgs(object[] args, out string arg0, out string arg1) return false; } + private static bool TryGetArgs(object[] args, out string arg0, out int arg1, out int arg2) + { + arg0 = null; + arg1 = 0; + arg2 = 0; + + if (args.Length != 3) + { + return false; + } + + var value1 = args[1] as string; + var value2 = args[2] as string; + arg0 = args[0] as string; + if (value1 != null && + value2 != null && + arg0 != null && + int.TryParse(value1, out arg1) && + int.TryParse(value2, out arg2)) + { + return true; + } + + return false; + } + private static bool TryGetArg(object[] args, out int arg0) { if (args.Length != 1)