Skip to content

Commit 1d9c402

Browse files
authored
Support loading ICU data from managed Interop (#49406)
In iOS we support loading a custom dat file when working with ICU. The way this worked originally was the mono runtime exported a function that native code would call into (similar to wasm). After thinking about it a bit, it makes more sense to load this the same way we do on desktop, but with the ability to provide the path to an ICU dat file via an AppContext key `ICU_DAT_FILE_PATH`. This can be provided before Xamarin iOS calls `monovm_initialize` and they won't have to worry about calling some special function.
1 parent 6791d05 commit 1d9c402

File tree

12 files changed

+122
-50
lines changed

12 files changed

+122
-50
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Runtime.InteropServices;
6+
7+
internal static partial class Interop
8+
{
9+
internal static partial class Globalization
10+
{
11+
[DllImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_LoadICUData")]
12+
internal static extern int LoadICUData(string path);
13+
}
14+
}

src/libraries/Native/Unix/System.Globalization.Native/entrypoints.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ static const Entry s_globalizationNative[] =
5050
DllImportEntry(GlobalizationNative_IsPredefinedLocale)
5151
DllImportEntry(GlobalizationNative_LastIndexOf)
5252
DllImportEntry(GlobalizationNative_LoadICU)
53+
#if defined(STATIC_ICU)
54+
DllImportEntry(GlobalizationNative_LoadICUData)
55+
#endif
5356
DllImportEntry(GlobalizationNative_NormalizeString)
5457
DllImportEntry(GlobalizationNative_StartsWith)
5558
DllImportEntry(GlobalizationNative_ToAscii)

src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ PALEXPORT int32_t GlobalizationNative_GetICUVersion(void);
1111

1212
#if defined(STATIC_ICU)
1313

14-
PALEXPORT int32_t GlobalizationNative_LoadICUData(char* path);
14+
PALEXPORT int32_t GlobalizationNative_LoadICUData(const char* path);
1515

1616
PALEXPORT const char* GlobalizationNative_GetICUDTName(const char* culture);
1717

src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_static.c

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,19 @@
2222
static int32_t isLoaded = 0;
2323
static int32_t isDataSet = 0;
2424

25+
static void log_shim_error(const char* format, ...)
26+
{
27+
va_list args;
28+
29+
va_start(args, format);
30+
vfprintf(stderr, format, args);
31+
va_end(args);
32+
}
33+
2534
static void log_icu_error(const char* name, UErrorCode status)
2635
{
2736
const char * statusText = u_errorName(status);
28-
fprintf(stderr, "ICU call %s failed with error #%d '%s'.\n", name, status, statusText);
37+
log_shim_error("ICU call %s failed with error #%d '%s'.\n", name, status, statusText);
2938
}
3039

3140
static void U_CALLCONV icu_trace_data(const void* context, int32_t fnNumber, int32_t level, const char* fmt, va_list args)
@@ -89,45 +98,60 @@ static int32_t load_icu_data(void* pData)
8998
}
9099
}
91100

92-
int32_t GlobalizationNative_LoadICUData(char* path)
101+
int32_t GlobalizationNative_LoadICUData(const char* path)
93102
{
94103
int32_t ret = -1;
95104
char* icu_data;
96105

97-
FILE *fp = fopen (path, "rb");
106+
FILE *fp = fopen(path, "rb");
98107
if (fp == NULL) {
99-
fprintf (stderr, "Unable to load ICU dat file '%s'.", path);
108+
log_shim_error("Unable to load ICU dat file '%s'.", path);
100109
return ret;
101110
}
102111

103-
if (fseek (fp, 0L, SEEK_END) != 0) {
104-
fprintf (stderr, "Unable to determine size of the dat file");
112+
if (fseek(fp, 0L, SEEK_END) != 0) {
113+
fclose(fp);
114+
log_shim_error("Unable to determine size of the dat file");
105115
return ret;
106116
}
107117

108-
long bufsize = ftell (fp);
118+
long bufsize = ftell(fp);
109119

110120
if (bufsize == -1) {
111-
fprintf (stderr, "Unable to determine size of the ICU dat file.");
121+
fclose(fp);
122+
log_shim_error("Unable to determine size of the ICU dat file.");
112123
return ret;
113124
}
114125

115-
icu_data = malloc (sizeof (char) * (bufsize + 1));
126+
icu_data = malloc(sizeof(char) * (bufsize + 1));
127+
128+
if (icu_data == NULL) {
129+
fclose(fp);
130+
log_shim_error("Unable to allocate enough to read the ICU dat file");
131+
return ret;
132+
}
116133

117-
if (fseek (fp, 0L, SEEK_SET) != 0) {
118-
fprintf (stderr, "Unable to seek ICU dat file.");
134+
if (fseek(fp, 0L, SEEK_SET) != 0) {
135+
fclose(fp);
136+
log_shim_error("Unable to seek ICU dat file.");
119137
return ret;
120138
}
121139

122-
fread (icu_data, sizeof (char), bufsize, fp);
123-
if (ferror ( fp ) != 0 ) {
124-
fprintf (stderr, "Unable to read ICU dat file");
140+
fread(icu_data, sizeof(char), bufsize, fp);
141+
if (ferror( fp ) != 0 ) {
142+
fclose(fp);
143+
log_shim_error("Unable to read ICU dat file");
125144
return ret;
126145
}
127146

128-
fclose (fp);
147+
fclose(fp);
148+
149+
if (load_icu_data(icu_data) == 0) {
150+
log_shim_error("ICU BAD EXIT %d.", ret);
151+
return ret;
152+
}
129153

130-
return load_icu_data (icu_data);
154+
return GlobalizationNative_LoadICU();
131155
}
132156

133157
const char* GlobalizationNative_GetICUDTName(const char* culture)

src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<PropertyGroup>
1212
<Nullable>enable</Nullable>
1313
<IsOSXLike Condition="'$(TargetsOSX)' == 'true' or '$(TargetsMacCatalyst)' == 'true' or '$(TargetsiOS)' == 'true' or '$(TargetstvOS)' == 'true'">true</IsOSXLike>
14+
<IsiOSLike Condition="'$(TargetsMacCatalyst)' == 'true' or '$(TargetsiOS)' == 'true' or '$(TargetstvOS)' == 'true'">true</IsiOSLike>
1415
<SupportsArmIntrinsics Condition="'$(Platform)' == 'arm64'">true</SupportsArmIntrinsics>
1516
<SupportsX86Intrinsics Condition="'$(Platform)' == 'x64' or ('$(Platform)' == 'x86' and '$(TargetsUnix)' != 'true')">true</SupportsX86Intrinsics>
1617
<ILLinkSharedDirectory>$(MSBuildThisFileDirectory)ILLink\</ILLinkSharedDirectory>
@@ -1082,6 +1083,9 @@
10821083
<Compile Include="$(CommonPath)Interop\Interop.ICU.cs">
10831084
<Link>Common\Interop\Interop.ICU.cs</Link>
10841085
</Compile>
1086+
<Compile Include="$(CommonPath)Interop\Interop.ICU.iOS.cs" Condition="'$(IsiOSLike)' == 'true'">
1087+
<Link>Common\Interop\Interop.ICU.iOS.cs</Link>
1088+
</Compile>
10851089
<Compile Include="$(CommonPath)Interop\Interop.Idna.cs">
10861090
<Link>Common\Interop\Interop.Idna.cs</Link>
10871091
</Compile>
@@ -1831,11 +1835,13 @@
18311835
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.NoRegistry.cs" />
18321836
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.UnixOrBrowser.cs" />
18331837
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.OSVersion.OSX.cs" Condition="'$(IsOSXLike)' == 'true'" />
1834-
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.GetFolderPathCore.Unix.cs" Condition="'$(TargetsMacCatalyst)' != 'true' and '$(TargetsiOS)' != 'true' and '$(TargetstvOS)' != 'true'" />
1838+
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.GetFolderPathCore.Unix.cs" Condition="'$(IsiOSLike)' != 'true'" />
18351839
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarData.Unix.cs" />
18361840
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.Unix.cs" />
18371841
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureInfo.Unix.cs" />
18381842
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\GlobalizationMode.Unix.cs" />
1843+
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\GlobalizationMode.LoadICU.Unix.cs" Condition="'$(IsiOSLike)' != 'true'" />
1844+
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\GlobalizationMode.LoadICU.iOS.cs" Condition="'$(IsiOSLike)' == 'true'" />
18391845
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.Unix.cs" />
18401846
<Compile Include="$(MSBuildThisFileDirectory)System\Guid.Unix.cs" />
18411847
<Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStreamHelpers.Unix.cs" />
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace System.Globalization
5+
{
6+
internal static partial class GlobalizationMode
7+
{
8+
private static int LoadICU() => Interop.Globalization.LoadICU();
9+
}
10+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace System.Globalization
5+
{
6+
internal static partial class GlobalizationMode
7+
{
8+
private static int LoadICU()
9+
{
10+
object? datPath = AppContext.GetData("ICU_DAT_FILE_PATH");
11+
return (datPath != null) ? Interop.Globalization.LoadICUData(datPath!.ToString()!) : Interop.Globalization.LoadICU();
12+
}
13+
}
14+
}

src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ private static bool GetGlobalizationInvariantMode()
2222
}
2323
else
2424
{
25-
int loaded = Interop.Globalization.LoadICU();
25+
int loaded = LoadICU();
2626
if (loaded == 0 && !OperatingSystem.IsBrowser())
2727
{
2828
// This can't go into resources, because a resource lookup requires globalization, which requires ICU

src/mono/mono/mini/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ if(HAVE_SYS_ICU)
5050
if(STATIC_ICU)
5151
set(pal_icushim_sources_base
5252
pal_icushim_static.c)
53+
add_definitions(-DSTATIC_ICU=1)
5354
else()
5455
set(pal_icushim_sources_base
5556
pal_icushim.c)

src/tasks/AppleAppBuilder/AppleAppBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ public override bool Execute()
182182

183183
if (GenerateXcodeProject)
184184
{
185-
Xcode generator = new Xcode(TargetOS);
185+
Xcode generator = new Xcode(TargetOS, Arch);
186186
generator.EnableRuntimeLogging = EnableRuntimeLogging;
187187

188188
XcodeProjectPath = generator.GenerateXCode(ProjectName, MainLibraryFileName, assemblerFiles,

0 commit comments

Comments
 (0)