From e51b29f411eb0d132f23a1eff2d74f32d989d632 Mon Sep 17 00:00:00 2001 From: YuliiaKovalova Date: Mon, 17 Mar 2025 18:40:09 +0100 Subject: [PATCH 01/12] add feature flag for custom culture --- documentation/specs/custom-cultures.md | 36 +++++++++++++++++++ documentation/wiki/ChangeWaves.md | 1 - src/Framework/Traits.cs | 7 +++- .../AssemblyDependency/Miscellaneous.cs | 6 ++-- .../AssemblyDependency/ReferenceTable.cs | 24 +++++++++---- .../ResolveAssemblyReference.cs | 18 ++++++++-- src/Tasks/CreateCSharpManifestResourceName.cs | 5 ++- .../CreateVisualBasicManifestResourceName.cs | 5 ++- .../Microsoft.Common.CurrentVersion.targets | 12 +++++-- 9 files changed, 91 insertions(+), 23 deletions(-) create mode 100644 documentation/specs/custom-cultures.md diff --git a/documentation/specs/custom-cultures.md b/documentation/specs/custom-cultures.md new file mode 100644 index 00000000000..826b96d4cf5 --- /dev/null +++ b/documentation/specs/custom-cultures.md @@ -0,0 +1,36 @@ +# MSBuild Custom Cultures Support + +## Overview + +The `MSBUILDENABLECUSTOMCULTURES` feature flag provides an opt-in mechanism for handling custom culture-specific resources in MSBuild projects. This feature allows for greater control over which directories are treated as culture-specific resources during the build process. + +## Purpose + +In some projects, directory names that match culture name patterns might not actually be culture resources. This can cause issues with resource compilation and deployment. This feature flag enables: + +1. Control over whether custom culture detection is enabled +2. Fine-grained configuration of which directories should be excluded from culture-specific resource processing + +## Usage + +### Enabling the Feature + +To enable the custom cultures feature, set the `MSBUILDENABLECUSTOMCULTURES` environment variable to `1` in your project file: + +### Excluding Specific Directories + +When the feature is enabled, you can specify directories that should not be treated as culture-specific resources using the `NonCultureResourceDirectories` property: + +```xml + + long;hash;temp + +``` + +In this example, directories named "long", "hash", or "temp" will not be processed as culture-specific resources, even if their names match culture naming patterns. + +## Additional Notes + +- This feature does not affect the standard resource handling for well-known cultures. +- The feature is designed to be backward compatible - existing projects without the feature flag will behave the same as before. +- Performance impact is minimal, as the exclusion check happens only during the resource discovery phase of the build. \ No newline at end of file diff --git a/documentation/wiki/ChangeWaves.md b/documentation/wiki/ChangeWaves.md index 6b88ed66097..fa76c042f22 100644 --- a/documentation/wiki/ChangeWaves.md +++ b/documentation/wiki/ChangeWaves.md @@ -26,7 +26,6 @@ A wave of features is set to "rotate out" (i.e. become standard functionality) t ### 17.14 - ~[.SLNX support - use the new parser for .sln and .slnx](https://github.com/dotnet/msbuild/pull/10836)~ reverted after compat problems discovered -- [Support custom culture in RAR](https://github.com/dotnet/msbuild/pull/11000) - [VS Telemetry](https://github.com/dotnet/msbuild/pull/11255) ### 17.12 diff --git a/src/Framework/Traits.cs b/src/Framework/Traits.cs index bcdc4ac195c..1f89492c762 100644 --- a/src/Framework/Traits.cs +++ b/src/Framework/Traits.cs @@ -7,11 +7,12 @@ namespace Microsoft.Build.Framework { /// - /// Represents toggleable features of the MSBuild engine + /// Represents toggleable features of the MSBuild engine. /// internal class Traits { private static Traits _instance = new Traits(); + public static Traits Instance { get @@ -132,6 +133,10 @@ public Traits() public readonly bool InProcNodeDisabled = Environment.GetEnvironmentVariable("MSBUILDNOINPROCNODE") == "1"; + /// + /// Escape hatch to enable custom cultures recognition. + /// + public static readonly bool EnableCustomCultures = Environment.GetEnvironmentVariable("MSBUILDENABLECUSTOMCULTURES") == "1"; /// /// Variables controlling opt out at the level of not initializing telemetry infrastructure. Set to "1" or "true" to opt out. diff --git a/src/Tasks.UnitTests/AssemblyDependency/Miscellaneous.cs b/src/Tasks.UnitTests/AssemblyDependency/Miscellaneous.cs index 164f91774e0..c77c32089ac 100644 --- a/src/Tasks.UnitTests/AssemblyDependency/Miscellaneous.cs +++ b/src/Tasks.UnitTests/AssemblyDependency/Miscellaneous.cs @@ -3279,7 +3279,7 @@ private ReferenceTable GenerateTableWithAssemblyFromTheGlobalLocation(string loc #if FEATURE_WIN32_REGISTRY null, null, null, #endif - null, null, new Version("4.0"), null, null, null, true, false, null, null, false, null, WarnOrErrorOnTargetArchitectureMismatchBehavior.None, false, false, null); + null, null, new Version("4.0"), null, null, null, true, false, null, null, false, null, WarnOrErrorOnTargetArchitectureMismatchBehavior.None, false, false, null, Array.Empty()); AssemblyNameExtension assemblyNameExtension = new AssemblyNameExtension(new AssemblyName("Microsoft.VisualStudio.Interopt, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")); TaskItem taskItem = new TaskItem("Microsoft.VisualStudio.Interopt, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); @@ -6739,7 +6739,7 @@ public void ReferenceTableDependentItemsInDenyList4() #if FEATURE_WIN32_REGISTRY null, null, null, #endif - null, null, null, new Version("4.0"), null, null, null, true, false, null, null, false, null, WarnOrErrorOnTargetArchitectureMismatchBehavior.None, false, false, null); + null, null, null, new Version("4.0"), null, null, null, true, false, null, null, false, null, WarnOrErrorOnTargetArchitectureMismatchBehavior.None, false, false, null, Array.Empty()); MockEngine mockEngine; ResolveAssemblyReference rar; Dictionary denyList; @@ -6917,7 +6917,7 @@ private static ReferenceTable MakeEmptyReferenceTable(TaskLoggingHelper log) #if FEATURE_WIN32_REGISTRY null, null, null, #endif - null, null, new Version("4.0"), null, log, null, true, false, null, null, false, null, WarnOrErrorOnTargetArchitectureMismatchBehavior.None, false, false, null); + null, null, new Version("4.0"), null, log, null, true, false, null, null, false, null, WarnOrErrorOnTargetArchitectureMismatchBehavior.None, false, false, null, Array.Empty()); return referenceTable; } diff --git a/src/Tasks/AssemblyDependency/ReferenceTable.cs b/src/Tasks/AssemblyDependency/ReferenceTable.cs index de1a4b26b20..93872d5bdc3 100644 --- a/src/Tasks/AssemblyDependency/ReferenceTable.cs +++ b/src/Tasks/AssemblyDependency/ReferenceTable.cs @@ -135,7 +135,7 @@ internal sealed class ReferenceTable private readonly bool _doNotCopyLocalIfInGac; /// - /// Shoould the framework attribute version mismatch be ignored. + /// Should the framework attribute version mismatch be ignored. /// private readonly bool _ignoreFrameworkAttributeVersionMismatch; @@ -145,7 +145,12 @@ internal sealed class ReferenceTable private readonly GetAssemblyPathInGac _getAssemblyPathInGac; /// - /// Should a warning or error be emitted on architecture mismatch + /// Contains the list of directories that should NOT be considered as custom culture directories. + /// + private readonly string[] _nonCultureResourceDirectories = []; + + /// + /// Should a warning or error be emitted on architecture mismatch. /// private readonly WarnOrErrorOnTargetArchitectureMismatchBehavior _warnOrErrorOnTargetArchitectureMismatch = WarnOrErrorOnTargetArchitectureMismatchBehavior.Warning; @@ -206,6 +211,7 @@ internal sealed class ReferenceTable /// /// /// + /// #else /// /// Construct. @@ -221,7 +227,7 @@ internal sealed class ReferenceTable /// Resolved sdk items /// Path to the FX. /// Installed assembly XML tables. - /// Like x86 or IA64\AMD64, the processor architecture being targetted. + /// Like x86 or IA64\AMD64, the processor architecture being targeted. /// Delegate used for checking for the existence of a file. /// Delegate used for files. /// Delegate used for getting directories. @@ -234,7 +240,7 @@ internal sealed class ReferenceTable /// Version of the runtime to target. /// Version of the framework targeted by the project. /// Target framework moniker we are targeting. - /// Logging helper to allow the logging of meessages from the Reference Table. + /// Logging helper to allow the logging of messages from the Reference Table. /// /// /// @@ -244,6 +250,7 @@ internal sealed class ReferenceTable /// /// /// + /// #endif internal ReferenceTable( IBuildEngine buildEngine, @@ -284,7 +291,8 @@ internal ReferenceTable( WarnOrErrorOnTargetArchitectureMismatchBehavior warnOrErrorOnTargetArchitectureMismatch, bool ignoreFrameworkAttributeVersionMismatch, bool unresolveFrameworkAssembliesFromHigherFrameworks, - ConcurrentDictionary assemblyMetadataCache) + ConcurrentDictionary assemblyMetadataCache, + string[] nonCultureResourceDirectories) { _log = log; _findDependencies = findDependencies; @@ -317,6 +325,7 @@ internal ReferenceTable( _warnOrErrorOnTargetArchitectureMismatch = warnOrErrorOnTargetArchitectureMismatch; _ignoreFrameworkAttributeVersionMismatch = ignoreFrameworkAttributeVersionMismatch; _assemblyMetadataCache = assemblyMetadataCache; + _nonCultureResourceDirectories = nonCultureResourceDirectories; // Set condition for when to check assembly version against the target framework version _checkAssemblyVersionAgainstTargetFrameworkVersion = unresolveFrameworkAssembliesFromHigherFrameworks || ((_projectTargetFramework ?? ReferenceTable.s_targetFrameworkVersion_40) <= ReferenceTable.s_targetFrameworkVersion_40); @@ -971,8 +980,9 @@ private void FindSatellites( // Is there a candidate satellite in that folder? string cultureName = Path.GetFileName(subDirectory); - // Custom or unknown cultures can be met as well - if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_14) || CultureInfoCache.IsValidCultureString(cultureName)) + // Custom or unknown cultures can be met only if the feature is enabled and the directory was not added to the exclusion list. + if ((Traits.EnableCustomCultures && !_nonCultureResourceDirectories.Contains(cultureName)) + || CultureInfoCache.IsValidCultureString(cultureName)) { string satelliteAssembly = Path.Combine(subDirectory, satelliteFilename); if (_fileExists(satelliteAssembly)) diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index b322052e386..d1d36ed4c33 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -177,6 +177,7 @@ internal static void Initialize(TaskLoggingHelper log) private bool _ignoreDefaultInstalledAssemblySubsetTables = false; private string[] _candidateAssemblyFiles = []; private string[] _targetFrameworkDirectories = []; + private string[] _nonCultureResourceDirectories = []; private string[] _searchPaths = []; private string[] _allowedAssemblyExtensions = [".winmd", ".dll", ".exe"]; private string[] _relatedFileExtensions = [".pdb", ".xml", ".pri"]; @@ -420,6 +421,15 @@ public string[] TargetFrameworkDirectories set { _targetFrameworkDirectories = value; } } + /// + /// Contains list of directories that point to custom culture resources that has to be ignored by MSBuild. + /// + public string[] NonCultureResourceDirectories + { + get { return _nonCultureResourceDirectories; } + set { _nonCultureResourceDirectories = value; } + } + /// /// A list of XML files that contain assemblies that are expected to be installed on the target machine. /// @@ -1505,7 +1515,10 @@ private void LogInputs() } Log.LogMessage(importance, property, "TargetFrameworkDirectories"); - Log.LogMessage(importance, indent + String.Join(",", TargetFrameworkDirectories)); + Log.LogMessage(importance, indent + string.Join(",", TargetFrameworkDirectories)); + + Log.LogMessage(importance, property, "NonCultureResourceDirectories"); + Log.LogMessage(importance, indent + string.Join(",", NonCultureResourceDirectories)); Log.LogMessage(importance, property, "InstalledAssemblyTables"); foreach (ITaskItem installedAssemblyTable in InstalledAssemblyTables) @@ -2413,7 +2426,8 @@ internal bool Execute( _warnOrErrorOnTargetArchitectureMismatch, _ignoreTargetFrameworkAttributeVersionMismatch, _unresolveFrameworkAssembliesFromHigherFrameworks, - assemblyMetadataCache); + assemblyMetadataCache, + _nonCultureResourceDirectories); dependencyTable.FindDependenciesOfExternallyResolvedReferences = FindDependenciesOfExternallyResolvedReferences; diff --git a/src/Tasks/CreateCSharpManifestResourceName.cs b/src/Tasks/CreateCSharpManifestResourceName.cs index 6f3b6b5a06d..2b13627a33b 100644 --- a/src/Tasks/CreateCSharpManifestResourceName.cs +++ b/src/Tasks/CreateCSharpManifestResourceName.cs @@ -103,13 +103,12 @@ internal static string CreateManifestNameImpl( dependentUponFileName = FileUtilities.FixFilePath(dependentUponFileName); Culture.ItemCultureInfo info; - if (!string.IsNullOrEmpty(culture) && ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_14)) + if (!string.IsNullOrEmpty(culture) && Traits.EnableCustomCultures) { info = new Culture.ItemCultureInfo() { culture = culture, - cultureNeutralFilename = - embeddedFileName.RemoveLastInstanceOf("." + culture, StringComparison.OrdinalIgnoreCase), + cultureNeutralFilename = embeddedFileName.RemoveLastInstanceOf("." + culture, StringComparison.OrdinalIgnoreCase), }; } else diff --git a/src/Tasks/CreateVisualBasicManifestResourceName.cs b/src/Tasks/CreateVisualBasicManifestResourceName.cs index 0115685336f..c23fa65b16a 100644 --- a/src/Tasks/CreateVisualBasicManifestResourceName.cs +++ b/src/Tasks/CreateVisualBasicManifestResourceName.cs @@ -102,13 +102,12 @@ internal static string CreateManifestNameImpl( dependentUponFileName = FileUtilities.FixFilePath(dependentUponFileName); Culture.ItemCultureInfo info; - if (!string.IsNullOrEmpty(culture) && ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_14)) + if (!string.IsNullOrEmpty(culture) && Traits.EnableCustomCultures) { info = new Culture.ItemCultureInfo() { culture = culture, - cultureNeutralFilename = - embeddedFileName.RemoveLastInstanceOf("." + culture, StringComparison.OrdinalIgnoreCase) + cultureNeutralFilename = embeddedFileName.RemoveLastInstanceOf("." + culture, StringComparison.OrdinalIgnoreCase), }; } else diff --git a/src/Tasks/Microsoft.Common.CurrentVersion.targets b/src/Tasks/Microsoft.Common.CurrentVersion.targets index 48b9de51827..cf7732cd5da 100644 --- a/src/Tasks/Microsoft.Common.CurrentVersion.targets +++ b/src/Tasks/Microsoft.Common.CurrentVersion.targets @@ -2412,6 +2412,11 @@ Copyright (C) Microsoft Corporation. All rights reserved. + + + + + - + false - + - + + + + + + + + + + $(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Bin\SdkResolvers\Microsoft.DotNet.MSBuildSdkResolver + + + + + - + + + + + diff --git a/src/MSBuild.Bootstrap.Utils/Tasks/LocateVisualStudioTask.cs b/src/MSBuild.Bootstrap.Utils/Tasks/LocateVisualStudioTask.cs new file mode 100644 index 00000000000..9d6354c81e0 --- /dev/null +++ b/src/MSBuild.Bootstrap.Utils/Tasks/LocateVisualStudioTask.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace MSBuild.Bootstrap.Utils.Tasks +{ + public class LocateVisualStudioTask : ToolTask + { + private readonly StringBuilder _standardOutput = new(); + + [Output] + public string VsInstallPath { get; set; } + + protected override string ToolName => "powershell.exe"; + + protected override string GenerateFullPathToTool() => ToolName; + + // vswhere.exe is a tool that allows to detect the installed VS on the machine. + // Local VS bits is a source for MSBuild-dependencies for full framework bootstrap. + protected override string GenerateCommandLineCommands() + { + string script = @" + $vsWherePath = ""${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"" + if (Test-Path $vsWherePath) { + try { + $vsPath = & $vsWherePath -latest -property installationPath + if ($vsPath -and (Test-Path $vsPath)) { + Write-Output $vsPath + exit 0 + } + } catch { + Write-Warning ""VSWhere failed: $_"" + } + } + # No installation found + exit 1 + "; + + script = script.Replace("\"", "\\\""); + + return $"-NoProfile -ExecutionPolicy Bypass -Command \"{script}\""; + } + + public override bool Execute() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Log.LogMessage(MessageImportance.High, "Not running on Windows. Skipping Visual Studio detection."); + return true; + } + + _ = ExecuteTool(ToolName, string.Empty, GenerateCommandLineCommands()); + + if (!Log.HasLoggedErrors) + { + VsInstallPath = _standardOutput.ToString().Trim(); + } + + return true; + } + + // Override to capture standard output + protected override void LogEventsFromTextOutput(string singleLine, MessageImportance messageImportance) + { + if (!string.IsNullOrWhiteSpace(singleLine)) + { + _ = _standardOutput.AppendLine(singleLine); + } + + base.LogEventsFromTextOutput(singleLine, messageImportance); + } + } +} diff --git a/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs b/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs index 18cf39a3eef..878b710456f 100644 --- a/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs +++ b/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs @@ -7,7 +7,6 @@ using Microsoft.Build.UnitTests.Shared; using Shouldly; using Xunit; -using Xunit.Abstractions; namespace Microsoft.Build.Tasks.UnitTests { @@ -21,7 +20,7 @@ public class ResolveAssemblyReference_CustomCultureTests "TestResources", "CustomCulture"); - [WindowsFullFrameworkOnlyTheory] + [WindowsOnlyTheory] [InlineData(true, "", true, true)] [InlineData(false)] [InlineData(true, "yue", false, true)] From 863babecade756fa15067f7e0c61233f121e041e Mon Sep 17 00:00:00 2001 From: YuliiaKovalova Date: Tue, 25 Mar 2025 13:06:59 +0100 Subject: [PATCH 08/12] fix review comments --- documentation/specs/custom-cultures.md | 4 +-- eng/BootStrapMsBuild.targets | 12 ++----- src/BuildCheck.UnitTests/EndToEndTests.cs | 3 -- src/Framework/Traits.cs | 2 +- .../Tasks/LocateVisualStudioTask.cs | 35 ++++++------------- ...lveAssemblyReference_CustomCultureTests.cs | 2 -- .../AssemblyDependency/ReferenceTable.cs | 2 +- src/Tasks/CreateCSharpManifestResourceName.cs | 2 +- .../CreateVisualBasicManifestResourceName.cs | 2 +- 9 files changed, 18 insertions(+), 46 deletions(-) diff --git a/documentation/specs/custom-cultures.md b/documentation/specs/custom-cultures.md index 826b96d4cf5..361d4a7944c 100644 --- a/documentation/specs/custom-cultures.md +++ b/documentation/specs/custom-cultures.md @@ -2,7 +2,7 @@ ## Overview -The `MSBUILDENABLECUSTOMCULTURES` feature flag provides an opt-in mechanism for handling custom culture-specific resources in MSBuild projects. This feature allows for greater control over which directories are treated as culture-specific resources during the build process. +The `MSBUILDENABLECUSTOMCULTURES` feature flag provides an opt-in mechanism for handling custom culture-specific resources in MSBuild projects. This feature allows for greater control over which directories are treated as culture-specific resources during the build process. ## Purpose @@ -27,7 +27,7 @@ When the feature is enabled, you can specify directories that should not be trea ``` -In this example, directories named "long", "hash", or "temp" will not be processed as culture-specific resources, even if their names match culture naming patterns. +In this example, directories named "long", "hash", or "temp" will not be processed as culture-specific resources and the assemblied inside of them will be skipped, even if their names match culture naming patterns. Globbing is not supported. ## Additional Notes diff --git a/eng/BootStrapMsBuild.targets b/eng/BootStrapMsBuild.targets index e58940cde64..4789ffcec85 100644 --- a/eng/BootStrapMsBuild.targets +++ b/eng/BootStrapMsBuild.targets @@ -73,11 +73,7 @@ - - - $(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Bin\SdkResolvers\Microsoft.DotNet.MSBuildSdkResolver - - + @@ -87,11 +83,7 @@ - - - - - + diff --git a/src/BuildCheck.UnitTests/EndToEndTests.cs b/src/BuildCheck.UnitTests/EndToEndTests.cs index 0d9f03d72fb..20fe9dca5cc 100644 --- a/src/BuildCheck.UnitTests/EndToEndTests.cs +++ b/src/BuildCheck.UnitTests/EndToEndTests.cs @@ -12,7 +12,6 @@ using Microsoft.Build.Shared; using Microsoft.Build.UnitTests; using Microsoft.Build.UnitTests.Shared; -using Microsoft.VisualStudio.TestPlatform.Utilities; using Shouldly; using Xunit; using Xunit.Abstractions; @@ -149,8 +148,6 @@ void AssertHasResourceForCulture(string culture, bool isResourceExpected) $"Unexpected resource for culture {culture} was found in deps.json:{Environment.NewLine}{output.DepsJsonResources.ToString()}"); } } - - _env.SetEnvironmentVariable("MSBUILDENABLECUSTOMCULTURES", ""); } private readonly record struct EmbedResourceTestOutput(String LogOutput, JsonObject DepsJsonResources); diff --git a/src/Framework/Traits.cs b/src/Framework/Traits.cs index 1f89492c762..a85d9bf44c4 100644 --- a/src/Framework/Traits.cs +++ b/src/Framework/Traits.cs @@ -136,7 +136,7 @@ public Traits() /// /// Escape hatch to enable custom cultures recognition. /// - public static readonly bool EnableCustomCultures = Environment.GetEnvironmentVariable("MSBUILDENABLECUSTOMCULTURES") == "1"; + public readonly bool EnableCustomCultures = Environment.GetEnvironmentVariable("MSBUILDENABLECUSTOMCULTURES") == "1"; /// /// Variables controlling opt out at the level of not initializing telemetry infrastructure. Set to "1" or "true" to opt out. diff --git a/src/MSBuild.Bootstrap.Utils/Tasks/LocateVisualStudioTask.cs b/src/MSBuild.Bootstrap.Utils/Tasks/LocateVisualStudioTask.cs index 9d6354c81e0..91cf7608e36 100644 --- a/src/MSBuild.Bootstrap.Utils/Tasks/LocateVisualStudioTask.cs +++ b/src/MSBuild.Bootstrap.Utils/Tasks/LocateVisualStudioTask.cs @@ -1,6 +1,8 @@ // 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 System.IO; using System.Runtime.InteropServices; using System.Text; using Microsoft.Build.Framework; @@ -15,36 +17,19 @@ public class LocateVisualStudioTask : ToolTask [Output] public string VsInstallPath { get; set; } - protected override string ToolName => "powershell.exe"; + protected override string ToolName => "vswhere.exe"; - protected override string GenerateFullPathToTool() => ToolName; - - // vswhere.exe is a tool that allows to detect the installed VS on the machine. - // Local VS bits is a source for MSBuild-dependencies for full framework bootstrap. - protected override string GenerateCommandLineCommands() + protected override string GenerateFullPathToTool() { - string script = @" - $vsWherePath = ""${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"" - if (Test-Path $vsWherePath) { - try { - $vsPath = & $vsWherePath -latest -property installationPath - if ($vsPath -and (Test-Path $vsPath)) { - Write-Output $vsPath - exit 0 - } - } catch { - Write-Warning ""VSWhere failed: $_"" - } - } - # No installation found - exit 1 - "; + string programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); + string vsWherePath = Path.Combine(programFilesX86, "Microsoft Visual Studio", "Installer", ToolName); - script = script.Replace("\"", "\\\""); - return $"-NoProfile -ExecutionPolicy Bypass -Command \"{script}\""; + return vsWherePath; } + protected override string GenerateCommandLineCommands() => "-latest -prerelease -property installationPath"; + public override bool Execute() { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -53,7 +38,7 @@ public override bool Execute() return true; } - _ = ExecuteTool(ToolName, string.Empty, GenerateCommandLineCommands()); + _ = ExecuteTool(GenerateFullPathToTool(), string.Empty, GenerateCommandLineCommands()); if (!Log.HasLoggedErrors) { diff --git a/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs b/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs index 878b710456f..2f6842785da 100644 --- a/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs +++ b/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs @@ -65,8 +65,6 @@ public void E2EScenarioTests(bool enableCustomCulture, string customCultureExclu var euyCultureResourceDll = Path.Combine(projBOutputPath, "euy", "ProjectA.resources.dll"); AssertCustomCulture(isEuyCultureExpected, "euy", euyCultureResourceDll); - - env.SetEnvironmentVariable("MSBUILDENABLECUSTOMCULTURES", ""); } void AssertCustomCulture(bool isCultureExpectedToExist, string customCultureName, string cultureResourcePath) diff --git a/src/Tasks/AssemblyDependency/ReferenceTable.cs b/src/Tasks/AssemblyDependency/ReferenceTable.cs index 93872d5bdc3..714ecadff88 100644 --- a/src/Tasks/AssemblyDependency/ReferenceTable.cs +++ b/src/Tasks/AssemblyDependency/ReferenceTable.cs @@ -981,7 +981,7 @@ private void FindSatellites( string cultureName = Path.GetFileName(subDirectory); // Custom or unknown cultures can be met only if the feature is enabled and the directory was not added to the exclusion list. - if ((Traits.EnableCustomCultures && !_nonCultureResourceDirectories.Contains(cultureName)) + if ((Traits.Instance.EnableCustomCultures && !_nonCultureResourceDirectories.Contains(cultureName)) || CultureInfoCache.IsValidCultureString(cultureName)) { string satelliteAssembly = Path.Combine(subDirectory, satelliteFilename); diff --git a/src/Tasks/CreateCSharpManifestResourceName.cs b/src/Tasks/CreateCSharpManifestResourceName.cs index 2b13627a33b..d257b91f260 100644 --- a/src/Tasks/CreateCSharpManifestResourceName.cs +++ b/src/Tasks/CreateCSharpManifestResourceName.cs @@ -103,7 +103,7 @@ internal static string CreateManifestNameImpl( dependentUponFileName = FileUtilities.FixFilePath(dependentUponFileName); Culture.ItemCultureInfo info; - if (!string.IsNullOrEmpty(culture) && Traits.EnableCustomCultures) + if (!string.IsNullOrEmpty(culture) && Traits.Instance.EnableCustomCultures) { info = new Culture.ItemCultureInfo() { diff --git a/src/Tasks/CreateVisualBasicManifestResourceName.cs b/src/Tasks/CreateVisualBasicManifestResourceName.cs index c23fa65b16a..7ca5e38443d 100644 --- a/src/Tasks/CreateVisualBasicManifestResourceName.cs +++ b/src/Tasks/CreateVisualBasicManifestResourceName.cs @@ -102,7 +102,7 @@ internal static string CreateManifestNameImpl( dependentUponFileName = FileUtilities.FixFilePath(dependentUponFileName); Culture.ItemCultureInfo info; - if (!string.IsNullOrEmpty(culture) && Traits.EnableCustomCultures) + if (!string.IsNullOrEmpty(culture) && Traits.Instance.EnableCustomCultures) { info = new Culture.ItemCultureInfo() { From 68101aebb52f9317e8e4c511ab9aa82999afe7d3 Mon Sep 17 00:00:00 2001 From: YuliiaKovalova Date: Tue, 25 Mar 2025 13:08:11 +0100 Subject: [PATCH 09/12] cleanup the description --- documentation/specs/custom-cultures.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/specs/custom-cultures.md b/documentation/specs/custom-cultures.md index 361d4a7944c..bc6d1f30ec4 100644 --- a/documentation/specs/custom-cultures.md +++ b/documentation/specs/custom-cultures.md @@ -2,7 +2,7 @@ ## Overview -The `MSBUILDENABLECUSTOMCULTURES` feature flag provides an opt-in mechanism for handling custom culture-specific resources in MSBuild projects. This feature allows for greater control over which directories are treated as culture-specific resources during the build process. +The `MSBUILDENABLECUSTOMCULTURES` feature flag provides an opt-in mechanism for handling custom culture-specific resources in MSBuild projects. This feature allows for greater control over which directories are treated as culture-specific resources during the build process. ## Purpose @@ -15,7 +15,7 @@ In some projects, directory names that match culture name patterns might not act ### Enabling the Feature -To enable the custom cultures feature, set the `MSBUILDENABLECUSTOMCULTURES` environment variable to `1` in your project file: +To enable the custom cultures feature, set the `MSBUILDENABLECUSTOMCULTURES` environment variable to `1`. ### Excluding Specific Directories From a50210ff0c7bc9e188aa4ff66ebcf114c5d22935 Mon Sep 17 00:00:00 2001 From: YuliiaKovalova Date: Tue, 25 Mar 2025 16:51:59 +0100 Subject: [PATCH 10/12] substitute MSBUILDENABLECUSTOMCULTURES env var -> EnableCustomCulture project property --- documentation/specs/custom-cultures.md | 10 ++++++++-- src/BuildCheck.UnitTests/EndToEndTests.cs | 1 - .../EntryProject/EntryProject.csproj | 1 + .../ReferencedProject/ReferencedProject.csproj | 1 + src/Framework/Traits.cs | 5 ----- .../MSBuild/Microsoft.Build.CommonTypes.xsd | 1 + .../AssemblyDependency/Miscellaneous.cs | 4 ++-- .../ResolveAssemblyReference_CustomCultureTests.cs | 5 ++--- .../TestResources/CustomCulture/ProjectB.csproj | 1 + src/Tasks/AssemblyDependency/ReferenceTable.cs | 11 ++++++++++- .../AssemblyDependency/ResolveAssemblyReference.cs | 14 ++++++++++++++ src/Tasks/CreateCSharpManifestResourceName.cs | 11 +++++++---- src/Tasks/CreateManifestResourceName.cs | 11 +++++++++++ src/Tasks/CreateVisualBasicManifestResourceName.cs | 11 +++++++---- src/Tasks/Microsoft.CSharp.CurrentVersion.targets | 1 + src/Tasks/Microsoft.Common.CurrentVersion.targets | 5 +++++ .../Microsoft.VisualBasic.CurrentVersion.targets | 1 + 17 files changed, 72 insertions(+), 22 deletions(-) diff --git a/documentation/specs/custom-cultures.md b/documentation/specs/custom-cultures.md index bc6d1f30ec4..a3e683c25e4 100644 --- a/documentation/specs/custom-cultures.md +++ b/documentation/specs/custom-cultures.md @@ -2,7 +2,7 @@ ## Overview -The `MSBUILDENABLECUSTOMCULTURES` feature flag provides an opt-in mechanism for handling custom culture-specific resources in MSBuild projects. This feature allows for greater control over which directories are treated as culture-specific resources during the build process. +The `EnableCustomCulture` property provides an opt-in mechanism for handling custom culture-specific resources in MSBuild projects. This feature allows for greater control over which directories are treated as culture-specific resources during the build process. ## Purpose @@ -15,7 +15,13 @@ In some projects, directory names that match culture name patterns might not act ### Enabling the Feature -To enable the custom cultures feature, set the `MSBUILDENABLECUSTOMCULTURES` environment variable to `1`. +To enable the custom cultures feature, set the `EnableCustomCulture` property `true`. + +```xml + + true + +``` ### Excluding Specific Directories diff --git a/src/BuildCheck.UnitTests/EndToEndTests.cs b/src/BuildCheck.UnitTests/EndToEndTests.cs index 20fe9dca5cc..08e3fccb43f 100644 --- a/src/BuildCheck.UnitTests/EndToEndTests.cs +++ b/src/BuildCheck.UnitTests/EndToEndTests.cs @@ -118,7 +118,6 @@ public void EmbeddedResourceCheckTest( string expectedDiagnostic, bool resourceExpectedToBeRecognizedAsSatelite) { - _env.SetEnvironmentVariable("MSBUILDENABLECUSTOMCULTURES", "1"); EmbedResourceTestOutput output = RunEmbeddedResourceTest(resourceElement, resourceExtension, respectAssignedCulturePropSet); int expectedWarningsCount = 0; diff --git a/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/EntryProject/EntryProject.csproj b/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/EntryProject/EntryProject.csproj index 1ac36d043de..228409c378c 100644 --- a/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/EntryProject/EntryProject.csproj +++ b/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/EntryProject/EntryProject.csproj @@ -4,6 +4,7 @@ net9.0 enable enable + true diff --git a/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/ReferencedProject.csproj b/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/ReferencedProject.csproj index 4208181be80..c03ac65c696 100644 --- a/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/ReferencedProject.csproj +++ b/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/ReferencedProject.csproj @@ -5,6 +5,7 @@ net8.0 enable enable + true diff --git a/src/Framework/Traits.cs b/src/Framework/Traits.cs index a85d9bf44c4..a70ed22074f 100644 --- a/src/Framework/Traits.cs +++ b/src/Framework/Traits.cs @@ -133,11 +133,6 @@ public Traits() public readonly bool InProcNodeDisabled = Environment.GetEnvironmentVariable("MSBUILDNOINPROCNODE") == "1"; - /// - /// Escape hatch to enable custom cultures recognition. - /// - public readonly bool EnableCustomCultures = Environment.GetEnvironmentVariable("MSBUILDENABLECUSTOMCULTURES") == "1"; - /// /// Variables controlling opt out at the level of not initializing telemetry infrastructure. Set to "1" or "true" to opt out. /// mirroring diff --git a/src/MSBuild/MSBuild/Microsoft.Build.CommonTypes.xsd b/src/MSBuild/MSBuild/Microsoft.Build.CommonTypes.xsd index 05365f4f62a..56c53a3af4d 100644 --- a/src/MSBuild/MSBuild/Microsoft.Build.CommonTypes.xsd +++ b/src/MSBuild/MSBuild/Microsoft.Build.CommonTypes.xsd @@ -1893,6 +1893,7 @@ elementFormDefault="qualified"> + boolean diff --git a/src/Tasks.UnitTests/AssemblyDependency/Miscellaneous.cs b/src/Tasks.UnitTests/AssemblyDependency/Miscellaneous.cs index 91d1617534d..76123167442 100644 --- a/src/Tasks.UnitTests/AssemblyDependency/Miscellaneous.cs +++ b/src/Tasks.UnitTests/AssemblyDependency/Miscellaneous.cs @@ -6700,7 +6700,7 @@ public void ReferenceTableDependentItemsInDenyList3() [Fact] public void ReferenceTableDependentItemsInDenyList4() { - ReferenceTable referenceTable = new ReferenceTable(null, false, false, false, false, Array.Empty(), null, null, null, null, null, null, SystemProcessorArchitecture.None, fileExists, null, null, null, + ReferenceTable referenceTable = new ReferenceTable(null, false, false, false, false, false, Array.Empty(), null, null, null, null, null, null, SystemProcessorArchitecture.None, fileExists, null, null, null, #if FEATURE_WIN32_REGISTRY null, null, null, #endif @@ -6878,7 +6878,7 @@ public void ReferenceTableDependentItemsInDenyListPrimaryWithSpecificVersion() private static ReferenceTable MakeEmptyReferenceTable(TaskLoggingHelper log) { - ReferenceTable referenceTable = new ReferenceTable(null, false, false, false, false, Array.Empty(), null, null, null, null, null, null, SystemProcessorArchitecture.None, fileExists, null, null, null, null, + ReferenceTable referenceTable = new ReferenceTable(null, false, false, false, false, false, Array.Empty(), null, null, null, null, null, null, SystemProcessorArchitecture.None, fileExists, null, null, null, null, #if FEATURE_WIN32_REGISTRY null, null, null, #endif diff --git a/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs b/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs index 2f6842785da..01ebc327698 100644 --- a/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs +++ b/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs @@ -32,8 +32,6 @@ public void E2EScenarioTests(bool enableCustomCulture, string customCultureExclu { using (TestEnvironment env = TestEnvironment.Create()) { - env.SetEnvironmentVariable("MSBUILDENABLECUSTOMCULTURES", enableCustomCulture ? "1" : ""); - // Set up project paths var testAssetsPath = TestAssetsRootPath; var solutionFolder = env.CreateFolder(); @@ -46,7 +44,8 @@ public void E2EScenarioTests(bool enableCustomCulture, string customCultureExclu Directory.CreateDirectory(projectBFolder); var projBContent = File.ReadAllText(Path.Combine(testAssetsPath, projectBName)) .Replace("OutputPathPlaceholder", projBOutputPath) - .Replace("NonCultureResourceDirectoriesPlaceholder", customCultureExclusions); + .Replace("NonCultureResourceDirectoriesPlaceholder", customCultureExclusions) + .Replace("EnableCustomCulturePlaceholder", enableCustomCulture.ToString()); env.CreateFile(Path.Combine(projectBFolder, projectBName), projBContent); // Copy ProjectA files to test solution folder diff --git a/src/Tasks.UnitTests/TestResources/CustomCulture/ProjectB.csproj b/src/Tasks.UnitTests/TestResources/CustomCulture/ProjectB.csproj index 0ec036aaa93..1daa05a8bc7 100644 --- a/src/Tasks.UnitTests/TestResources/CustomCulture/ProjectB.csproj +++ b/src/Tasks.UnitTests/TestResources/CustomCulture/ProjectB.csproj @@ -5,6 +5,7 @@ false Library OutputPathPlaceholder + EnableCustomCulturePlaceholder diff --git a/src/Tasks/AssemblyDependency/ReferenceTable.cs b/src/Tasks/AssemblyDependency/ReferenceTable.cs index 714ecadff88..f82b1b43eb0 100644 --- a/src/Tasks/AssemblyDependency/ReferenceTable.cs +++ b/src/Tasks/AssemblyDependency/ReferenceTable.cs @@ -149,6 +149,11 @@ internal sealed class ReferenceTable /// private readonly string[] _nonCultureResourceDirectories = []; + /// + /// Is true, custom culture processing is enabled. + /// + private readonly bool _enableCustomCulture = false; + /// /// Should a warning or error be emitted on architecture mismatch. /// @@ -179,6 +184,7 @@ internal sealed class ReferenceTable /// If true, then search for satellite files. /// If true, then search for serialization assembly files. /// If true, then search for related files. + /// If true, custom culture processing is enabled. /// Paths to search for dependent assemblies on. /// /// List of literal assembly file names to be considered when SearchPaths has {CandidateAssemblyFiles}. @@ -221,6 +227,7 @@ internal sealed class ReferenceTable /// If true, then search for satellite files. /// If true, then search for serialization assembly files. /// If true, then search for related files. + /// If true, custom culture processing is enabled. /// Paths to search for dependent assemblies on. /// /// List of literal assembly file names to be considered when SearchPaths has {CandidateAssemblyFiles}. @@ -258,6 +265,7 @@ internal ReferenceTable( bool findSatellites, bool findSerializationAssemblies, bool findRelatedFiles, + bool enableCustomCulture, string[] searchPaths, string[] allowedAssemblyExtensions, string[] relatedFileExtensions, @@ -326,6 +334,7 @@ internal ReferenceTable( _ignoreFrameworkAttributeVersionMismatch = ignoreFrameworkAttributeVersionMismatch; _assemblyMetadataCache = assemblyMetadataCache; _nonCultureResourceDirectories = nonCultureResourceDirectories; + _enableCustomCulture = enableCustomCulture; // Set condition for when to check assembly version against the target framework version _checkAssemblyVersionAgainstTargetFrameworkVersion = unresolveFrameworkAssembliesFromHigherFrameworks || ((_projectTargetFramework ?? ReferenceTable.s_targetFrameworkVersion_40) <= ReferenceTable.s_targetFrameworkVersion_40); @@ -981,7 +990,7 @@ private void FindSatellites( string cultureName = Path.GetFileName(subDirectory); // Custom or unknown cultures can be met only if the feature is enabled and the directory was not added to the exclusion list. - if ((Traits.Instance.EnableCustomCultures && !_nonCultureResourceDirectories.Contains(cultureName)) + if ((_enableCustomCulture && !_nonCultureResourceDirectories.Contains(cultureName)) || CultureInfoCache.IsValidCultureString(cultureName)) { string satelliteAssembly = Path.Combine(subDirectory, satelliteFilename); diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index d1d36ed4c33..8dd35fcdcbc 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -175,6 +175,7 @@ internal static void Initialize(TaskLoggingHelper log) private ITaskItem[] _resolvedSDKReferences = Array.Empty(); private bool _ignoreDefaultInstalledAssemblyTables = false; private bool _ignoreDefaultInstalledAssemblySubsetTables = false; + private bool _enableCustomCulture = false; private string[] _candidateAssemblyFiles = []; private string[] _targetFrameworkDirectories = []; private string[] _nonCultureResourceDirectories = []; @@ -430,6 +431,15 @@ public string[] NonCultureResourceDirectories set { _nonCultureResourceDirectories = value; } } + /// + /// Contains the information if custom culture is enabled. + /// + public bool EnableCustomCulture + { + get { return _enableCustomCulture; } + set { _enableCustomCulture = value; } + } + /// /// A list of XML files that contain assemblies that are expected to be installed on the target machine. /// @@ -1554,6 +1564,9 @@ private void LogInputs() Log.LogMessage(importance, property, "AutoUnify"); Log.LogMessage(importance, indent + AutoUnify.ToString()); + Log.LogMessage(importance, property, "EnableCustomCulture"); + Log.LogMessage(importance, $"{indent}{EnableCustomCulture}"); + Log.LogMessage(importance, property, "CopyLocalDependenciesWhenParentReferenceInGac"); Log.LogMessage(importance, indent + _copyLocalDependenciesWhenParentReferenceInGac); @@ -2393,6 +2406,7 @@ internal bool Execute( _findSatellites, _findSerializationAssemblies, _findRelatedFiles, + _enableCustomCulture, _searchPaths, _allowedAssemblyExtensions, _relatedFileExtensions, diff --git a/src/Tasks/CreateCSharpManifestResourceName.cs b/src/Tasks/CreateCSharpManifestResourceName.cs index d257b91f260..c7f838b16ef 100644 --- a/src/Tasks/CreateCSharpManifestResourceName.cs +++ b/src/Tasks/CreateCSharpManifestResourceName.cs @@ -51,7 +51,7 @@ protected override string CreateManifestName( Actual implementation is in a static method called CreateManifestNameImpl. The reason is that CreateManifestName can't be static because it is an override of a method declared in the base class, but its convenient - to expose a static version anyway for unittesting purposes. + to expose a static version anyway for unit testing purposes. */ return CreateManifestNameImpl( fileName, @@ -62,7 +62,8 @@ The reason is that CreateManifestName can't be static because it is an culture, binaryStream, Log, - treatAsCultureNeutral); + treatAsCultureNeutral, + EnableCustomCulture); } /// @@ -81,6 +82,7 @@ The reason is that CreateManifestName can't be static because it is an /// File contents binary stream, may be null /// Task's TaskLoggingHelper, for logging warnings or errors /// Whether to treat the current file as 'culture-neutral' and retain the culture in the name. + /// Whether custom culture handling is expected. /// Returns the manifest name internal static string CreateManifestNameImpl( string fileName, @@ -91,7 +93,8 @@ internal static string CreateManifestNameImpl( string culture, // may be null Stream binaryStream, // File contents binary stream, may be null TaskLoggingHelper log, - bool treatAsCultureNeutral = false) + bool treatAsCultureNeutral = false, + bool enableCustomCulture = false) { // Use the link file name if there is one, otherwise, fall back to file name. string embeddedFileName = FileUtilities.FixFilePath(linkFileName); @@ -103,7 +106,7 @@ internal static string CreateManifestNameImpl( dependentUponFileName = FileUtilities.FixFilePath(dependentUponFileName); Culture.ItemCultureInfo info; - if (!string.IsNullOrEmpty(culture) && Traits.Instance.EnableCustomCultures) + if (!string.IsNullOrEmpty(culture) && enableCustomCulture) { info = new Culture.ItemCultureInfo() { diff --git a/src/Tasks/CreateManifestResourceName.cs b/src/Tasks/CreateManifestResourceName.cs index 934d67c6a68..d974b1a8d1c 100644 --- a/src/Tasks/CreateManifestResourceName.cs +++ b/src/Tasks/CreateManifestResourceName.cs @@ -28,6 +28,8 @@ public abstract class CreateManifestResourceName : TaskExtension private ITaskItem[] _resourceFiles; + private bool _enableCustomCulture; + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Shipped this way in Dev11 Beta (go-live)")] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Taskitem", Justification = "Shipped this way in Dev11 Beta (go-live)")] protected Dictionary itemSpecToTaskitem = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -56,6 +58,15 @@ public ITaskItem[] ResourceFiles set => _resourceFiles = value; } + /// + /// Contains the information if custom culture is enabled. + /// + public bool EnableCustomCulture + { + get { return _enableCustomCulture; } + set { _enableCustomCulture = value; } + } + /// /// Rootnamespace to use for naming. /// diff --git a/src/Tasks/CreateVisualBasicManifestResourceName.cs b/src/Tasks/CreateVisualBasicManifestResourceName.cs index 7ca5e38443d..d2cf7f405ef 100644 --- a/src/Tasks/CreateVisualBasicManifestResourceName.cs +++ b/src/Tasks/CreateVisualBasicManifestResourceName.cs @@ -50,7 +50,7 @@ protected override string CreateManifestName( Actual implementation is in a static method called CreateManifestNameImpl. The reason is that CreateManifestName can't be static because it is an override of a method declared in the base class, but its convenient - to expose a static version anyway for unittesting purposes. + to expose a static version anyway for unit testing purposes. */ return CreateManifestNameImpl( fileName, @@ -61,7 +61,8 @@ The reason is that CreateManifestName can't be static because it is an culture, binaryStream, Log, - treatAsCultureNeutral); + treatAsCultureNeutral, + EnableCustomCulture); } /// @@ -80,6 +81,7 @@ The reason is that CreateManifestName can't be static because it is an /// File contents binary stream, may be null /// Task's TaskLoggingHelper, for logging warnings or errors /// Whether to treat the current file as 'culture-neutral' and retain the culture in the name. + /// Whether custom culture handling is expected. /// Returns the manifest name internal static string CreateManifestNameImpl( string fileName, @@ -90,7 +92,8 @@ internal static string CreateManifestNameImpl( string culture, Stream binaryStream, // File contents binary stream, may be null TaskLoggingHelper log, - bool treatAsCultureNeutral = false) + bool treatAsCultureNeutral = false, + bool enableCustomCulture = false) { // Use the link file name if there is one, otherwise, fall back to file name. string embeddedFileName = linkFileName; @@ -102,7 +105,7 @@ internal static string CreateManifestNameImpl( dependentUponFileName = FileUtilities.FixFilePath(dependentUponFileName); Culture.ItemCultureInfo info; - if (!string.IsNullOrEmpty(culture) && Traits.Instance.EnableCustomCultures) + if (!string.IsNullOrEmpty(culture) && enableCustomCulture) { info = new Culture.ItemCultureInfo() { diff --git a/src/Tasks/Microsoft.CSharp.CurrentVersion.targets b/src/Tasks/Microsoft.CSharp.CurrentVersion.targets index 60045885791..0280966ef15 100644 --- a/src/Tasks/Microsoft.CSharp.CurrentVersion.targets +++ b/src/Tasks/Microsoft.CSharp.CurrentVersion.targets @@ -100,6 +100,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. diff --git a/src/Tasks/Microsoft.Common.CurrentVersion.targets b/src/Tasks/Microsoft.Common.CurrentVersion.targets index cf7732cd5da..0c4ce55ad13 100644 --- a/src/Tasks/Microsoft.Common.CurrentVersion.targets +++ b/src/Tasks/Microsoft.Common.CurrentVersion.targets @@ -2417,6 +2417,10 @@ Copyright (C) Microsoft Corporation. All rights reserved. + + false + + - 17.14.1release + 17.14.2release 17.13.9 15.1.0.0 preview From b94e2fb390abdaa90f4c722e5516c4c1fddb9f9c Mon Sep 17 00:00:00 2001 From: YuliiaKovalova Date: Wed, 2 Apr 2025 17:58:07 +0200 Subject: [PATCH 12/12] final tweaks --- documentation/wiki/ChangeWaves.md | 1 + src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj | 1 - .../ResolveAssemblyReference_CustomCultureTests.cs | 5 ----- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/documentation/wiki/ChangeWaves.md b/documentation/wiki/ChangeWaves.md index fa76c042f22..833299ed296 100644 --- a/documentation/wiki/ChangeWaves.md +++ b/documentation/wiki/ChangeWaves.md @@ -26,6 +26,7 @@ A wave of features is set to "rotate out" (i.e. become standard functionality) t ### 17.14 - ~[.SLNX support - use the new parser for .sln and .slnx](https://github.com/dotnet/msbuild/pull/10836)~ reverted after compat problems discovered +- ~~[Support custom culture in RAR](https://github.com/dotnet/msbuild/pull/11000)~~ - see [11607](https://github.com/dotnet/msbuild/pull/11607) for details - [VS Telemetry](https://github.com/dotnet/msbuild/pull/11255) ### 17.12 diff --git a/src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj b/src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj index 9c1615e7aaf..2ff059b2833 100644 --- a/src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj +++ b/src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj @@ -138,7 +138,6 @@ - diff --git a/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs b/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs index 01ebc327698..1a7d7ed0562 100644 --- a/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs +++ b/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs @@ -83,11 +83,6 @@ private void CopyTestAsset(string sourceFolder, string fileName, string destinat { var sourcePath = Path.Combine(sourceFolder, fileName); - if (!File.Exists(sourcePath)) - { - throw new FileNotFoundException($"Test asset not found: {sourcePath}"); - } - File.Copy(sourcePath, Path.Combine(destinationFolder, fileName)); } }