Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect .NET SDK & VSLANG Custom Language Settings & Apply To MSBuild #8503

Merged
merged 33 commits into from
May 12, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0f58db2
Add logic to change the encoding and detect .NET SDK and VS language …
nagilson Feb 28, 2023
3a3db4d
Restore the original encoding so msbuild does not impact encoding of …
nagilson Feb 28, 2023
e4595a9
Add a unit test for external ui language overrides
nagilson Feb 28, 2023
dc17603
Merge remote-tracking branch 'upstream/main' into nagilson-cli-language
nagilson Feb 28, 2023
fa715d1
Fix the merge conflict that vscode did not fix (grumpiness)
nagilson Feb 28, 2023
cc30ce9
Use langword=null for Sandcastle and VsDocMan
nagilson Feb 28, 2023
1058b59
Respond to feedback. But it's not done yet.
nagilson Mar 6, 2023
01b381b
Limit the scope of the breaking change so windows who dont support ut…
nagilson Mar 8, 2023
8487af6
Remove comment that I didn't hit ctrl S on, automatic encoding restor…
nagilson Mar 8, 2023
747ff0b
Move encoding restorer to a non public API
nagilson Mar 9, 2023
3b3b23d
Use a name instead of _ for the discard variable bc the compiler gets…
nagilson Mar 9, 2023
d52714b
Dont use runtimeinfo as its not in net 472 full framework even tho it…
nagilson Mar 9, 2023
b20919f
Move the encoding restorer so it's not part of a public api and build…
nagilson Mar 9, 2023
8d0e14b
Consider that full framework must set all culture variables for corre…
nagilson Mar 9, 2023
d96551c
Try to make full framework correctly change the culture :(
nagilson Mar 13, 2023
9b1c074
Check to see if the windows version of build machines doesnt support …
nagilson Mar 13, 2023
5a06292
Dont look for vslang because vs can manage the lang settings itself a…
nagilson Mar 13, 2023
60ee6bc
Make the encoding change happen earlier than msbuild use server stuff
nagilson Mar 14, 2023
992fb7e
Code clean up for dotnet cli language feature
nagilson Mar 14, 2023
048bd20
Respond to PR feedback
nagilson Mar 15, 2023
18f34d5
Follow the Microsoft style of using the string alias over System.String
nagilson Mar 23, 2023
881305c
prepare to add encoding change to logs as well
nagilson May 4, 2023
cfe0c6c
Merge remote-tracking branch 'upstream/main' into nagilson-cli-language
nagilson May 4, 2023
45961ed
Get the encoding to change log files as well and move the encoding up…
nagilson May 4, 2023
3023308
Merge remote-tracking branch 'upstream/main' into nagilson-cli-language
nagilson May 9, 2023
41d9da6
Add environment variable to disable/opt-out of the new feature
nagilson May 9, 2023
aa5a55b
Move the utf8 encoding change feature flag to the change wave
nagilson May 9, 2023
5c2db93
Remove some no-longer-required usings
rainersigwald May 9, 2023
28b8bd0
Remove the english language and redirect in the help link
nagilson May 9, 2023
e61c559
Consider that a user may delete windows registry keys
nagilson May 9, 2023
16a0acd
Merge branch 'nagilson-cli-language' of https://github.com/nagilson/m…
nagilson May 10, 2023
f794639
update test to expect the fallback culture instead of the original cu…
nagilson May 10, 2023
86cf4e5
Merge remote-tracking branch 'upstream/main' into nagilson-cli-language
nagilson May 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/MSBuild.UnitTests/XMake_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,35 @@ public void SetConsoleUICulture()
thisThread.CurrentUICulture = originalUICulture;
}


[Fact]
public void ConsoleUIRespectsSDKLanguage()
{
const string DOTNET_CLI_UI_LANGUAGE = nameof(DOTNET_CLI_UI_LANGUAGE);

// Save the current environment info so it can be restored.
var originalUILanguage = Environment.GetEnvironmentVariable(DOTNET_CLI_UI_LANGUAGE);
nagilson marked this conversation as resolved.
Show resolved Hide resolved
Encoding originalOutputEncoding = Console.OutputEncoding;
Encoding originalInputEncoding = Console.InputEncoding;
Thread thisThread = Thread.CurrentThread;
CultureInfo originalUICulture = thisThread.CurrentUICulture;

// Set the UI language based on the SDK environment var.
Environment.SetEnvironmentVariable(DOTNET_CLI_UI_LANGUAGE, "ja"); // japanese chose arbitrarily.

MSBuildApp.SetConsoleUI();

Assert.Equal(thisThread.CurrentUICulture, new CultureInfo("ja"));
Assert.Equal(65001, System.Console.OutputEncoding.CodePage); // utf 8 enabled for correct rendering.

// Restore the current UI culture back to the way it was at the beginning of this unit test.
thisThread.CurrentUICulture = originalUICulture;
Environment.SetEnvironmentVariable(DOTNET_CLI_UI_LANGUAGE, originalUILanguage);
// MSbuild should also restore the encoding upon exit, but we don't create that context here.
Console.OutputEncoding = originalOutputEncoding;
Console.InputEncoding = originalInputEncoding;
nagilson marked this conversation as resolved.
Show resolved Hide resolved
}

#if FEATURE_SYSTEM_CONFIGURATION
/// <summary>
/// Invalid configuration file should not dump stack.
Expand Down
78 changes: 77 additions & 1 deletion src/MSBuild/XMake.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
using Microsoft.Build.Shared.Debugging;
using Microsoft.Build.Experimental;
using Microsoft.Build.Framework.Telemetry;
using System.Runtime.CompilerServices;
using Microsoft.Build.Internal;
using Microsoft.Build.Logging.LiveLogger;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -665,6 +666,9 @@ private static void DebuggerLaunchCheck()
VerifyThrowSupportedOS();

// Setup the console UI.
Encoding originalOutputEncoding = Console.OutputEncoding;
Encoding originalInputEncoding = Console.InputEncoding;
CultureInfo originalThreadCulture = Thread.CurrentThread.CurrentUICulture;
SetConsoleUI();

// reset the application state for this new build
Expand Down Expand Up @@ -832,6 +836,10 @@ private static void DebuggerLaunchCheck()
// if there was no need to start the build e.g. because /help was triggered
// do nothing
}

// The encoding may be changed to support non-en characters for environment variables set by external tools. We don't want to impact other programs on the console.
Console.OutputEncoding = originalOutputEncoding;
Console.InputEncoding = originalInputEncoding;
nagilson marked this conversation as resolved.
Show resolved Hide resolved
}
/**********************************************************************************************************************
* WARNING: Do NOT add any more catch blocks below! Exceptions should be caught as close to their point of origin as
Expand Down Expand Up @@ -1635,7 +1643,7 @@ internal static void SetConsoleUI()
Thread thisThread = Thread.CurrentThread;

// Eliminate the complex script cultures from the language selection.
thisThread.CurrentUICulture = CultureInfo.CurrentUICulture.GetConsoleFallbackUICulture();
thisThread.CurrentUICulture = GetExternalOverridenUILanguageIfSupportableWithEncoding() ?? CultureInfo.CurrentUICulture.GetConsoleFallbackUICulture();

// Determine if the language can be displayed in the current console codepage, otherwise set to US English
int codepage;
Expand Down Expand Up @@ -1669,6 +1677,74 @@ internal static void SetConsoleUI()
#endif
}

/// <summary>
/// The .NET SDK and Visual Studio both have environment variables that set a custom language. MSBuild should respect those variables.
/// To use the correspoding UI culture, in certain cases the console encoding must be changed. This function will change the encoding in these cases.
/// This code introduces a breaking change due to the encoding of the console being changed.
/// If the environment variables are undefined, this function should be a no-op.
/// </summary>
/// <returns>The custom language that was set by the user for an 'external' tool besides MSBuild.
/// DOTNET_CLI_UI_LANGUAGE > VSLANG. Returns null if none are set.</returns>
nagilson marked this conversation as resolved.
Show resolved Hide resolved
private static CultureInfo GetExternalOverridenUILanguageIfSupportableWithEncoding()
{
CultureInfo externalLanguageSetting = GetExternalOverriddenUILanguage();
if (externalLanguageSetting != null)
{
if (
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && // Encoding is only an issue on Windows
!externalLanguageSetting.TwoLetterISOLanguageName.Equals("en", StringComparison.InvariantCultureIgnoreCase) &&
Environment.OSVersion.Version.Major >= 10 // UTF-8 is only officially supported on 10+.
)
{
// Setting both encodings causes a change in the CHCP, making it so we dont need to P-Invoke ourselves.
Console.OutputEncoding = Encoding.UTF8;
// If the InputEncoding is not set, the encoding will work in CMD but not in Powershell, as the raw CHCP page won't be changed.
Console.InputEncoding = Encoding.UTF8;
return externalLanguageSetting;
}
else if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return externalLanguageSetting;
}
}
return null;
}

/// <summary>
/// Look at UI language overrides that can be set by known external invokers. (DOTNET_CLI_UI_LANGUAGE and VSLANG).
/// Does NOT check System Locale or OS Display Language.
/// Ported from the .NET SDK: https://github.com/dotnet/sdk/src/Cli/Microsoft.DotNet.Cli.Utils/UILanguageOverride.cs.
nagilson marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <returns>The custom language that was set by the user for an 'external' tool besides MSBuild.
/// DOTNET_CLI_UI_LANGUAGE > VSLANG. Returns null if none are set.</returns>
private static CultureInfo GetExternalOverriddenUILanguage()
{
// DOTNET_CLI_UI_LANGUAGE=<culture name> is the main way for users to customize the CLI's UI language via the .NET SDK.
string dotnetCliLanguage = Environment.GetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE");
nagilson marked this conversation as resolved.
Show resolved Hide resolved
if (dotnetCliLanguage != null)
{
try
{
return new CultureInfo(dotnetCliLanguage);
}
catch (CultureNotFoundException) { }
}

// VSLANG=<lcid> is set by Visual Studio.
string vsLang = Environment.GetEnvironmentVariable("VSLANG");
if (vsLang != null && int.TryParse(vsLang, out int vsLcid))
{
try
{
return new CultureInfo(vsLcid);
}
catch (ArgumentOutOfRangeException) { }
catch (CultureNotFoundException) { }
}

return null;
}

/// <summary>
/// Gets all specified switches, from the command line, as well as all
/// response files, including the auto-response file.
Expand Down