libSystem.Globalization.Native!GlobalizationNative_GetTimeZoneDisplayName
libSystem.Globalization.Native!GlobalizationNative_IndexOf
libSystem.Globalization.Native!GlobalizationNative_IndexOfOrdinalIgnoreCase
+libSystem.Globalization.Native!GlobalizationNative_InitICUFunctions
libSystem.Globalization.Native!GlobalizationNative_IsNormalized
libSystem.Globalization.Native!GlobalizationNative_IsPredefinedLocale
libSystem.Globalization.Native!GlobalizationNative_LastIndexOf
bool invariantEnabled = GetInvariantSwitchValue();
if (!invariantEnabled)
{
- if (Interop.Globalization.LoadICU() == 0)
+ if (TryGetAppLocalIcuSwitchValue(out string? icuSuffixAndVersion))
+ {
+ LoadAppLocalIcu(icuSuffixAndVersion, suffixWithSeparator: true);
+ }
+ else if (Interop.Globalization.LoadICU() == 0)
{
string message = "Couldn't find a valid ICU package installed on the system. " +
"Set the configuration flag System.Globalization.Invariant to true if you want to run with no globalization support.";
}
return invariantEnabled;
}
+
+ private static void LoadAppLocalIcuCore(ReadOnlySpan<char> version, ReadOnlySpan<char> suffix)
+ {
+
+#if TARGET_OSX
+ const string extension = ".dylib";
+ bool versionAtEnd = false;
+#else
+ string extension = version.Length > 0 ? "so." : "so";
+ bool versionAtEnd = true;
+#endif
+
+#if !TARGET_OSX
+ // In Linux we need to load libicudata first because libicuuc and libicui18n depend on it. In order for the loader to find
+ // it on the same path, we load it before loading the other two libraries.
+ LoadLibrary(CreateLibraryName("libicudata", suffix, extension, version, versionAtEnd), failOnLoadFailure: true);
+#endif
+
+ IntPtr icuucLib = LoadLibrary(CreateLibraryName("libicuuc", suffix, extension, version, versionAtEnd), failOnLoadFailure: true);
+ IntPtr icuinLib = LoadLibrary(CreateLibraryName("libicui18n", suffix, extension, version, versionAtEnd), failOnLoadFailure: true);
+
+ Interop.Globalization.InitICUFunctions(icuucLib, icuinLib, version, suffix);
+ }
}
}
internal static bool UseNls { get; } = !Invariant &&
(GetSwitchValue("System.Globalization.UseNls", "DOTNET_SYSTEM_GLOBALIZATION_USENLS") ||
- Interop.Globalization.LoadICU() == 0);
+ !LoadIcu());
+
+ private static bool LoadIcu()
+ {
+ if (!TryGetAppLocalIcuSwitchValue(out string? icuSuffixAndVersion))
+ {
+ return Interop.Globalization.LoadICU() != 0;
+ }
+
+ LoadAppLocalIcu(icuSuffixAndVersion);
+ return true;
+ }
+
+ private static void LoadAppLocalIcuCore(ReadOnlySpan<char> version, ReadOnlySpan<char> suffix)
+ {
+ const string extension = ".dll";
+ const string icuucBase = "icuuc";
+ const string icuinBase = "icuin";
+ IntPtr icuucLib = IntPtr.Zero;
+ IntPtr icuinLib = IntPtr.Zero;
+
+ int index = version.IndexOf('.');
+ if (index > 0)
+ {
+ ReadOnlySpan<char> truncatedVersion = version.Slice(0, index);
+ icuucLib = LoadLibrary(CreateLibraryName(icuucBase, suffix, extension, truncatedVersion), failOnLoadFailure: false);
+
+ if (icuucLib != IntPtr.Zero)
+ {
+ icuinLib = LoadLibrary(CreateLibraryName(icuinBase, suffix, extension, truncatedVersion), failOnLoadFailure: false);
+ }
+ }
+
+ if (icuucLib == IntPtr.Zero)
+ {
+ icuucLib = LoadLibrary(CreateLibraryName(icuucBase, suffix, extension, version), failOnLoadFailure: true);
+ }
+
+ if (icuinLib == IntPtr.Zero)
+ {
+ icuinLib = LoadLibrary(CreateLibraryName(icuinBase, suffix, extension, version), failOnLoadFailure: true);
+ }
+
+ Interop.Globalization.InitICUFunctions(icuucLib, icuinLib, version, suffix);
+ }
}
}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
namespace System.Globalization
{
internal static partial class GlobalizationMode
private static bool GetInvariantSwitchValue() =>
GetSwitchValue("System.Globalization.Invariant", "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT");
+ private static bool TryGetAppLocalIcuSwitchValue([NotNullWhen(true)] out string? value) =>
+ TryGetStringValue("System.Globalization.AppLocalIcu", "DOTNET_SYSTEM_GLOBALIZATION_APPLOCALICU", out value);
+
// GetSwitchValue calls CLRConfig first to detect if the switch is defined in the config file.
// if the switch is defined we just use the value of this switch. otherwise, we'll try to get the switch
// value from the environment variable if it is defined.
private static bool GetSwitchValue(string switchName, string envVariable)
{
- bool ret = CLRConfig.GetBoolValue(switchName, out bool exist);
- if (!exist)
+ if (!AppContext.TryGetSwitch(switchName, out bool ret))
{
string? switchValue = Environment.GetEnvironmentVariable(envVariable);
if (switchValue != null)
return ret;
}
+
+ 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, bool suffixWithSeparator = false)
+ {
+ ReadOnlySpan<char> icuSuffix = default;
+ ReadOnlySpan<char> version = 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().Slice(0, indexOfSeparator);
+
+ if (icuSuffix.Length > 35)
+ {
+ Environment.FailFast($"The resolved \"{icuSuffix.ToString()}\" suffix from System.Globalization.AppLocalIcu switch has to be < 20 chars long.");
+ }
+
+ version = icuSuffixAndVersion.AsSpan().Slice(icuSuffix.Length + 1);
+ }
+ else
+ {
+ version = icuSuffixAndVersion;
+ }
+
+ if (version.Length > 33)
+ {
+ Environment.FailFast($"The resolved version \"{version.ToString()}\" from System.Globalization.AppLocalIcu switch has to be < 33 chars long.");
+ }
+
+ if (suffixWithSeparator)
+ {
+ icuSuffix = string.Concat(icuSuffix, ".");
+ }
+
+ 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, out IntPtr lib) && failOnLoadFailure)
+ {
+ Environment.FailFast($"Failed to load app-local ICU: {library}");
+ }
+
+ return lib;
+ }
}
}
QCFuncElement("GetTimeZoneDisplayName", GlobalizationNative_GetTimeZoneDisplayName)
QCFuncElement("IndexOf", GlobalizationNative_IndexOf)
QCFuncElement("IndexOfOrdinalIgnoreCase", GlobalizationNative_IndexOfOrdinalIgnoreCase)
+ QCFuncElement("InitICUFunctions", GlobalizationNative_InitICUFunctions)
QCFuncElement("IsNormalized", GlobalizationNative_IsNormalized)
QCFuncElement("IsPredefinedLocale", GlobalizationNative_IsPredefinedLocale)
QCFuncElement("LastIndexOf", GlobalizationNative_LastIndexOf)
[DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IndexOfOrdinalIgnoreCase")]
internal static extern unsafe int IndexOfOrdinalIgnoreCase(string target, int cwTargetLength, char* pSource, int cwSourceLength, bool findLast);
+
[DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IndexOfOrdinalIgnoreCase")]
internal static extern unsafe int IndexOfOrdinalIgnoreCase(char* target, int cwTargetLength, char* pSource, int cwSourceLength, bool findLast);
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System;
+using System.Diagnostics;
using System.Runtime.InteropServices;
internal static partial class Interop
[DllImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_LoadICU")]
internal static extern int LoadICU();
+ internal static void InitICUFunctions(IntPtr icuuc, IntPtr icuin, ReadOnlySpan<char> version, ReadOnlySpan<char> suffix)
+ {
+ Debug.Assert(icuuc != IntPtr.Zero);
+ Debug.Assert(icuin != IntPtr.Zero);
+
+ InitICUFunctions(icuuc, icuin, version.ToString(), suffix.Length > 0 ? suffix.ToString() : null);
+ }
+
+ [DllImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_InitICUFunctions")]
+ internal static extern void InitICUFunctions(IntPtr icuuc, IntPtr icuin, string version, string? suffix);
+
[DllImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_GetICUVersion")]
internal static extern int GetICUVersion();
}
FOR_ALL_ICU_FUNCTIONS
#undef PER_FUNCTION_BLOCK
+// 35 for the actual suffix, 1 for _ and 1 for '\0'
+#define SYMBOL_CUSTOM_SUFFIX_SIZE 37
+#define SYMBOL_NAME_SIZE (128 + SYMBOL_CUSTOM_SUFFIX_SIZE)
+#define MaxICUVersionStringWithSuffixLength (MaxICUVersionStringLength + SYMBOL_CUSTOM_SUFFIX_SIZE)
+
+
+#if defined(TARGET_WINDOWS) || defined(TARGET_OSX) || defined(TARGET_ANDROID)
+
+#define MaxICUVersionStringLength 33
+
+#endif
+
static void* libicuuc = NULL;
static void* libicui18n = NULL;
+#if defined (TARGET_UNIX)
+
+#define PER_FUNCTION_BLOCK(fn, lib) \
+ c_static_assert_msg((sizeof(#fn) + MaxICUVersionStringWithSuffixLength + 1) <= sizeof(symbolName), "The symbolName is too small for symbol " #fn); \
+ sprintf(symbolName, #fn "%s", symbolVersion); \
+ fn##_ptr = (__typeof(fn)*)dlsym(lib, symbolName); \
+ if (fn##_ptr == NULL) { fprintf(stderr, "Cannot get symbol %s from " #lib "\nError: %s\n", symbolName, dlerror()); abort(); }
+
+static int FindSymbolVersion(int majorVer, int minorVer, int subVer, char* symbolName, char* symbolVersion, char* suffix)
+{
+ // Find out the format of the version string added to each symbol
+ // First try just the unversioned symbol
+ if (dlsym(libicuuc, "u_strlen") == NULL)
+ {
+ // Now try just the _majorVer added
+ sprintf(symbolVersion, "_%d%s", majorVer, suffix);
+ sprintf(symbolName, "u_strlen%s", symbolVersion);
+ if (dlsym(libicuuc, symbolName) == NULL)
+ {
+ if (minorVer == -1)
+ return FALSE;
+
+ // Now try the _majorVer_minorVer added
+ sprintf(symbolVersion, "_%d_%d%s", majorVer, minorVer, suffix);
+ sprintf(symbolName, "u_strlen%s", symbolVersion);
+ if (dlsym(libicuuc, symbolName) == NULL)
+ {
+ if (subVer == -1)
+ return FALSE;
+
+ // Finally, try the _majorVer_minorVer_subVer added
+ sprintf(symbolVersion, "_%d_%d_%d%s", majorVer, minorVer, subVer, suffix);
+ sprintf(symbolName, "u_strlen%s", symbolVersion);
+ if (dlsym(libicuuc, symbolName) == NULL)
+ {
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+#endif // TARGET_UNIX
+
#if defined(TARGET_WINDOWS)
+#define sscanf sscanf_s
+
+#define PER_FUNCTION_BLOCK(fn, lib) \
+ sprintf_s(symbolName, SYMBOL_NAME_SIZE, #fn "%s", symbolVersion); \
+ fn##_ptr = (__typeof(fn)*)GetProcAddress((HMODULE)lib, symbolName); \
+ if (fn##_ptr == NULL) { fprintf(stderr, "Cannot get symbol %s from " #lib "\nError: %u\n", symbolName, GetLastError()); abort(); }
+
static int FindICULibs()
{
libicuuc = LoadLibraryExW(L"icu.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
return TRUE;
}
+static int FindSymbolVersion(int majorVer, int minorVer, int subVer, char* symbolName, char* symbolVersion, char* suffix)
+{
+ HMODULE lib = (HMODULE)libicuuc;
+ // Find out the format of the version string added to each symbol
+ // First try just the unversioned symbol
+ if (GetProcAddress(lib, "u_strlen") == NULL)
+ {
+ // Now try just the _majorVer added
+ sprintf_s(symbolVersion, MaxICUVersionStringWithSuffixLength,"_%d%s", majorVer, suffix);
+ sprintf_s(symbolName, SYMBOL_NAME_SIZE, "u_strlen%s", symbolVersion);
+ if (GetProcAddress(lib, symbolName) == NULL)
+ {
+ if (minorVer == -1)
+ return FALSE;
+
+ // Now try the _majorVer_minorVer added
+ sprintf_s(symbolVersion, MaxICUVersionStringWithSuffixLength, "_%d_%d%s", majorVer, minorVer, suffix);
+ sprintf_s(symbolName, SYMBOL_NAME_SIZE, "u_strlen%s", symbolVersion);
+ if (GetProcAddress(lib, symbolName) == NULL)
+ {
+ if (subVer == -1)
+ return FALSE;
+ // Finally, try the _majorVer_minorVer_subVer added
+ sprintf_s(symbolVersion, MaxICUVersionStringWithSuffixLength, "_%d_%d_%d%s", majorVer, minorVer, subVer, suffix);
+ sprintf_s(symbolName, SYMBOL_NAME_SIZE, "u_strlen%s", symbolVersion);
+ if (GetProcAddress(lib, symbolName) == NULL)
+ {
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
#elif defined(TARGET_OSX)
static int FindICULibs()
// support ICU versions from 50-255
#define MinICUVersion 50
#define MaxICUVersion 255
-#define MaxICUVersionStringLength 4
-
-static int FindSymbolVersion(char* symbolName, char* symbolVersion)
-{
- for (int i = MinICUVersion; i <= MaxICUVersion; i++)
- {
- sprintf(symbolVersion, "_%d", i);
- sprintf(symbolName, "u_strlen%s", symbolVersion);
- if (dlsym(libicuuc, symbolName) != NULL)
- {
- return TRUE;
- }
- }
-
- return FALSE;
-}
static int FindICULibs(char* symbolName, char* symbolVersion)
{
return FALSE;
}
- if (!FindSymbolVersion(symbolName, symbolVersion))
+ char symbolSuffix[SYMBOL_CUSTOM_SUFFIX_SIZE]="";
+ for (int i = MinICUVersion; i <= MaxICUVersion; i++)
{
- fprintf(stderr, "Cannot determine ICU version.");
- return FALSE;
+ if (FindSymbolVersion(i, -1, -1, symbolName, symbolVersion, symbolSuffix))
+ {
+ return TRUE;
+ }
}
- return TRUE;
+ fprintf(stderr, "Cannot determine ICU version.");
+ return FALSE;
}
#else // !TARGET_WINDOWS && !TARGET_OSX && !TARGET_ANDROID
}
}
-static int FindSymbolVersion(int majorVer, int minorVer, int subVer, char* symbolName, char* symbolVersion)
-{
- // Find out the format of the version string added to each symbol
- // First try just the unversioned symbol
- if (dlsym(libicuuc, "u_strlen") == NULL)
- {
- // Now try just the _majorVer added
- sprintf(symbolVersion, "_%d", majorVer);
- sprintf(symbolName, "u_strlen%s", symbolVersion);
- if ((dlsym(libicuuc, symbolName) == NULL) && (minorVer != -1))
- {
- // Now try the _majorVer_minorVer added
- sprintf(symbolVersion, "_%d_%d", majorVer, minorVer);
- sprintf(symbolName, "u_strlen%s", symbolVersion);
- if ((dlsym(libicuuc, symbolName) == NULL) && (subVer != -1))
- {
- // Finally, try the _majorVer_minorVer_subVer added
- sprintf(symbolVersion, "_%d_%d_%d", majorVer, minorVer, subVer);
- sprintf(symbolName, "u_strlen%s", symbolVersion);
- if (dlsym(libicuuc, symbolName) == NULL)
- {
- return FALSE;
- }
- }
- }
- }
-
- return TRUE;
-}
-
// Try to open the necessary ICU libraries
static int OpenICULibraries(int majorVer, int minorVer, int subVer, const char* versionPrefix, char* symbolName, char* symbolVersion)
{
libicuuc = dlopen(libicuucName, RTLD_LAZY);
if (libicuuc != NULL)
{
- if (FindSymbolVersion(majorVer, minorVer, subVer, symbolName, symbolVersion))
+ char symbolSuffix[SYMBOL_CUSTOM_SUFFIX_SIZE]="";
+ if (FindSymbolVersion(majorVer, minorVer, subVer, symbolName, symbolVersion, symbolSuffix))
{
libicui18n = dlopen(libicui18nName, RTLD_LAZY);
}
#endif
+static void ValidateICUDataCanLoad()
+{
+ UVersionInfo version;
+ UErrorCode err = U_ZERO_ERROR;
+ ulocdata_getCLDRVersion(version, &err);
+
+ if (U_FAILURE(err))
+ {
+ fprintf(stderr, "Could not load ICU data. UErrorCode: %d\n", err);
+ abort();
+ }
+}
+
// GlobalizationNative_LoadICU
// This method get called from the managed side during the globalization initialization.
// This method shouldn't get called at all if we are running in globalization invariant mode
// return 0 if failed to load ICU and 1 otherwise
int32_t GlobalizationNative_LoadICU()
{
-#if defined(TARGET_WINDOWS)
-
- if (!FindICULibs())
- {
- return FALSE;
- }
-
-#define PER_FUNCTION_BLOCK(fn, lib) \
- fn##_ptr = (__typeof(fn)*)GetProcAddress((HMODULE)lib, #fn); \
- if (fn##_ptr == NULL) { fprintf(stderr, "Cannot get symbol %s from " #lib "\nError: %u\n", #fn, GetLastError()); abort(); }
+ char symbolName[SYMBOL_NAME_SIZE];
+ char symbolVersion[MaxICUVersionStringLength + 1]="";
-#elif defined(TARGET_OSX)
+#if defined(TARGET_WINDOWS) || defined(TARGET_OSX)
if (!FindICULibs())
{
return FALSE;
}
- // Get pointers to all the ICU functions that are needed
-#define PER_FUNCTION_BLOCK(fn, lib) \
- fn##_ptr = (__typeof(fn)*)dlsym(lib, #fn); \
- if (fn##_ptr == NULL) { fprintf(stderr, "Cannot get symbol %s from " #lib "\nError: %s\n", #fn, dlerror()); abort(); }
-
-#else // !TARGET_WINDOWS && !TARGET_OSX
-
- char symbolName[128];
- char symbolVersion[MaxICUVersionStringLength + 1] = "";
-
-#if defined(TARGET_ANDROID)
+#elif defined(TARGET_ANDROID)
if (!FindICULibs(symbolName, symbolVersion))
{
return FALSE;
return FALSE;
}
}
-#endif
+#endif // TARGET_WINDOWS || TARGET_OSX
- // Get pointers to all the ICU functions that are needed
-#define PER_FUNCTION_BLOCK(fn, lib) \
- c_static_assert_msg((sizeof(#fn) + MaxICUVersionStringLength + 1) <= sizeof(symbolName), "The symbolName is too small for symbol " #fn); \
- sprintf(symbolName, #fn "%s", symbolVersion); \
- fn##_ptr = (__typeof(fn)*)dlsym(lib, symbolName); \
- if (fn##_ptr == NULL) { fprintf(stderr, "Cannot get symbol %s from " #lib "\nError: %s\n", symbolName, dlerror()); abort(); }
+ FOR_ALL_ICU_FUNCTIONS
+ ValidateICUDataCanLoad();
+ return TRUE;
+}
+
+void GlobalizationNative_InitICUFunctions(void* icuuc, void* icuin, const char* version, const char* suffix)
+{
+ assert(icuuc != NULL);
+ assert(icuin != NULL);
+ assert(version != NULL);
+
+ libicuuc = icuuc;
+ libicui18n = icuin;
+ int major = -1;
+ int minor = -1;
+ int build = -1;
+
+ char symbolName[SYMBOL_NAME_SIZE];
+ char symbolVersion[MaxICUVersionStringWithSuffixLength + 1]="";
+ char symbolSuffix[SYMBOL_CUSTOM_SUFFIX_SIZE]="";
+ sscanf(version, "%d.%d.%d", &major, &minor, &build);
+
+ if (suffix != NULL)
+ {
+ assert(strlen(suffix) + 1 <= SYMBOL_CUSTOM_SUFFIX_SIZE);
+
+#if defined(TARGET_WINDOWS)
+ sprintf_s(symbolSuffix, SYMBOL_CUSTOM_SUFFIX_SIZE, "_%s", suffix);
+#else
+ sprintf(symbolSuffix, "_%s", suffix);
#endif
+ }
- FOR_ALL_ICU_FUNCTIONS
-#undef PER_FUNCTION_BLOCK
+ if(!FindSymbolVersion(major, minor, build, symbolName, symbolVersion, symbolSuffix))
+ {
+ fprintf(stderr, "Could not find symbol: %s from libicuuc\n", symbolName);
+ abort();
+ }
- return TRUE;
+ FOR_ALL_ICU_FUNCTIONS
+ ValidateICUDataCanLoad();
}
+#undef PER_FUNCTION_BLOCK
+
// GlobalizationNative_GetICUVersion
// return the current loaded ICU version
int32_t GlobalizationNative_GetICUVersion()
PALEXPORT int32_t GlobalizationNative_LoadICU(void);
+PALEXPORT void GlobalizationNative_InitICUFunctions(void* icuuc, void* icuin, const char* version, const char* suffix);
+
PALEXPORT int32_t GlobalizationNative_GetICUVersion(void);
PER_FUNCTION_BLOCK(ucol_safeClone, libicui18n) \
PER_FUNCTION_BLOCK(ucol_setAttribute, libicui18n) \
PER_FUNCTION_BLOCK(ucol_strcoll, libicui18n) \
- PER_FUNCTION_BLOCK(ucurr_forLocale, libicui18n) \
- PER_FUNCTION_BLOCK(ucurr_getName, libicui18n) \
PER_FUNCTION_BLOCK(udat_close, libicui18n) \
PER_FUNCTION_BLOCK(udat_countSymbols, libicui18n) \
PER_FUNCTION_BLOCK(udat_getSymbols, libicui18n) \
PER_FUNCTION_BLOCK(uidna_nameToASCII, libicuuc) \
PER_FUNCTION_BLOCK(uidna_nameToUnicode, libicuuc) \
PER_FUNCTION_BLOCK(uidna_openUTS46, libicuuc) \
- PER_FUNCTION_BLOCK(uldn_close, libicui18n) \
- PER_FUNCTION_BLOCK(uldn_keyValueDisplayName, libicui18n) \
- PER_FUNCTION_BLOCK(uldn_open, libicui18n) \
PER_FUNCTION_BLOCK(uloc_canonicalize, libicuuc) \
PER_FUNCTION_BLOCK(uloc_countAvailable, libicuuc) \
PER_FUNCTION_BLOCK(uloc_getAvailable, libicuuc) \
PER_FUNCTION_BLOCK(uloc_getName, libicuuc) \
PER_FUNCTION_BLOCK(uloc_getParent, libicuuc) \
PER_FUNCTION_BLOCK(uloc_setKeywordValue, libicuuc) \
+ PER_FUNCTION_BLOCK(ulocdata_getCLDRVersion, libicui18n) \
PER_FUNCTION_BLOCK(ulocdata_getMeasurementSystem, libicui18n) \
PER_FUNCTION_BLOCK(unorm2_getNFCInstance, libicuuc) \
PER_FUNCTION_BLOCK(unorm2_getNFDInstance, libicuuc) \
PER_FUNCTION_BLOCK(usearch_openFromCollator, libicui18n)
#if HAVE_SET_MAX_VARIABLE
-#define FOR_ALL_ICU_FUNCTIONS \
- FOR_ALL_UNCONDITIONAL_ICU_FUNCTIONS \
+#define FOR_ALL_SET_VARIABLE_ICU_FUNCTIONS \
PER_FUNCTION_BLOCK(ucol_setMaxVariable, libicui18n)
#else
-#define FOR_ALL_ICU_FUNCTIONS \
- FOR_ALL_UNCONDITIONAL_ICU_FUNCTIONS \
+#define FOR_ALL_SET_VARIABLE_ICU_FUNCTIONS \
PER_FUNCTION_BLOCK(ucol_setVariableTop, libicui18n)
#endif
+#if defined(TARGET_WINDOWS)
+#define FOR_ALL_OS_CONDITIONAL_ICU_FUNCTIONS \
+ PER_FUNCTION_BLOCK(ucurr_forLocale, libicuuc) \
+ PER_FUNCTION_BLOCK(ucurr_getName, libicuuc) \
+ PER_FUNCTION_BLOCK(uldn_close, libicuuc) \
+ PER_FUNCTION_BLOCK(uldn_keyValueDisplayName, libicuuc) \
+ PER_FUNCTION_BLOCK(uldn_open, libicuuc)
+#else
+ // Unix ICU is dynamically resolved at runtime and these APIs in old versions
+ // of ICU were in libicui18n
+#define FOR_ALL_OS_CONDITIONAL_ICU_FUNCTIONS \
+ PER_FUNCTION_BLOCK(ucurr_forLocale, libicui18n) \
+ PER_FUNCTION_BLOCK(ucurr_getName, libicui18n) \
+ PER_FUNCTION_BLOCK(uldn_close, libicui18n) \
+ PER_FUNCTION_BLOCK(uldn_keyValueDisplayName, libicui18n) \
+ PER_FUNCTION_BLOCK(uldn_open, libicui18n)
+#endif
+
+#define FOR_ALL_ICU_FUNCTIONS \
+ FOR_ALL_UNCONDITIONAL_ICU_FUNCTIONS \
+ FOR_ALL_SET_VARIABLE_ICU_FUNCTIONS \
+ FOR_ALL_OS_CONDITIONAL_ICU_FUNCTIONS
+
// Declare pointers to all the used ICU functions
#define PER_FUNCTION_BLOCK(fn, lib) EXTERN_C __typeof(fn)* fn##_ptr;
FOR_ALL_ICU_FUNCTIONS
#define uloc_getName(...) uloc_getName_ptr(__VA_ARGS__)
#define uloc_getParent(...) uloc_getParent_ptr(__VA_ARGS__)
#define uloc_setKeywordValue(...) uloc_setKeywordValue_ptr(__VA_ARGS__)
+#define ulocdata_getCLDRVersion(...) ulocdata_getCLDRVersion_ptr(__VA_ARGS__)
#define ulocdata_getMeasurementSystem(...) ulocdata_getMeasurementSystem_ptr(__VA_ARGS__)
#define unorm2_getNFCInstance(...) unorm2_getNFCInstance_ptr(__VA_ARGS__)
#define unorm2_getNFDInstance(...) unorm2_getNFDInstance_ptr(__VA_ARGS__)
int32_t uloc_getName(const char * localeID, char * name, int32_t nameCapacity, UErrorCode * err);
int32_t uloc_getParent(const char * localeID, char * parent, int32_t parentCapacity, UErrorCode * err);
int32_t uloc_setKeywordValue(const char * keywordName, const char * keywordValue, char * buffer, int32_t bufferCapacity, UErrorCode * status);
+void ulocdata_getCLDRVersion(UVersionInfo versionArray, UErrorCode * status);
UMeasurementSystem ulocdata_getMeasurementSystem(const char * localeID, UErrorCode * status);
const UNormalizer2 * unorm2_getNFCInstance(UErrorCode * pErrorCode);
const UNormalizer2 * unorm2_getNFDInstance(UErrorCode * pErrorCode);
[Fact]
public static void IcuShouldNotBeLoaded()
{
- Assert.False(PlatformDetection.IsIcuGlobalization);
+ Assert.False(PlatformDetection.IsIcuGlobalization, $"Found ICU: {PlatformDetection.ICUVersion}");
}
}
}