|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace System.Globalization
{
internal static partial class GlobalizationMode
{
// Split from GlobalizationMode so the whole class can be trimmed when Invariant=true. Trimming tests
// validate this implementation detail.
private static partial class Settings
{
internal static bool Invariant { get; } = AppContextConfigHelper.GetBooleanConfig("System.Globalization.Invariant", "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT");
#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
internal static bool Hybrid { get; } = true;
#endif
internal static bool PredefinedCulturesOnly { get; } = AppContextConfigHelper.GetBooleanConfig("System.Globalization.PredefinedCulturesOnly", "DOTNET_SYSTEM_GLOBALIZATION_PREDEFINED_CULTURES_ONLY", GlobalizationMode.Invariant);
}
// Note: Invariant=true and Invariant=false are substituted at different levels in the ILLink.Substitutions file.
// This allows for the whole Settings nested class to be trimmed when Invariant=true, and allows for the Settings
// static cctor (on Unix) to be preserved when Invariant=false.
internal static bool Invariant => Settings.Invariant;
// same as GlobalizationMode.Invariant but doesn't trigger ICU load in GlobalizationMode.Settings.cctor
// during runtime startup on Browser platform
internal static bool InvariantNoLoad
{
get
{
#if TARGET_BROWSER
return AppContextConfigHelper.GetBooleanConfig("System.Globalization.Invariant", "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT");
#else
return Settings.Invariant;
#endif
}
}
#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS
internal static bool Hybrid => Settings.Hybrid;
#endif
internal static bool PredefinedCulturesOnly => Settings.PredefinedCulturesOnly;
private static bool TryGetAppLocalIcuSwitchValue([NotNullWhen(true)] out string? value) =>
TryGetStringValue("System.Globalization.AppLocalIcu", "DOTNET_SYSTEM_GLOBALIZATION_APPLOCALICU", out value);
private static bool TryGetStringValue(string switchName, string envVariable, [NotNullWhen(true)] out string? value)
{
value = AppContext.GetData(switchName) as string;
if (string.IsNullOrEmpty(value))
{
value = Environment.GetEnvironmentVariable(envVariable);
if (string.IsNullOrEmpty(value))
{
return false;
}
}
return true;
}
private static void LoadAppLocalIcu(string icuSuffixAndVersion)
{
ReadOnlySpan<char> version;
ReadOnlySpan<char> icuSuffix = default;
// Custom built ICU can have a suffix on the name, i.e: libicuucmyapp.so.67.1
// So users would set the runtime switch as: myapp:67.1
int indexOfSeparator = icuSuffixAndVersion.IndexOf(':');
if (indexOfSeparator >= 0)
{
icuSuffix = icuSuffixAndVersion.AsSpan(0, indexOfSeparator);
version = icuSuffixAndVersion.AsSpan(icuSuffix.Length + 1);
}
else
{
version = icuSuffixAndVersion;
}
LoadAppLocalIcuCore(version, icuSuffix);
}
private static string CreateLibraryName(ReadOnlySpan<char> baseName, ReadOnlySpan<char> suffix, ReadOnlySpan<char> extension, ReadOnlySpan<char> version, bool versionAtEnd = false) =>
versionAtEnd ?
string.Concat(baseName, suffix, extension, version) :
string.Concat(baseName, suffix, version, extension);
private static IntPtr LoadLibrary(string library, bool failOnLoadFailure)
{
if (!NativeLibrary.TryLoad(library, typeof(object).Assembly, DllImportSearchPath.ApplicationDirectory | DllImportSearchPath.System32, out IntPtr lib) && failOnLoadFailure)
{
Environment.FailFast($"Failed to load app-local ICU: {library}");
}
return lib;
}
}
}
|