Skip to content

Commit 4e2d017

Browse files
authored
Introduce ICUExport: a command utility that upgrades the ICU version for the swift-foundation-icu, based on ICU releases from apple-oss-distribution (#81)
1 parent 6452e94 commit 4e2d017

File tree

5 files changed

+811
-0
lines changed

5 files changed

+811
-0
lines changed

ICUExport/Package.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// swift-tools-version: 6.0
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "ICUExport",
8+
platforms: [.macOS(.v15)],
9+
dependencies: [
10+
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.4.0"),
11+
.package(url: "https://github.com/swiftlang/swift-subprocess", from: "0.1.0"),
12+
],
13+
targets: [
14+
// Targets are the basic building blocks of a package, defining a module or a test suite.
15+
// Targets can depend on other targets in this package and products from dependencies.
16+
.executableTarget(
17+
name: "icu-export",
18+
dependencies: [
19+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
20+
.product(name: "Subprocess", package: "swift-subprocess")
21+
],
22+
resources: [
23+
.process("Patches/putil.patch"),
24+
.process("Patches/namespace.patch"),
25+
]
26+
),
27+
]
28+
)

ICUExport/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# ICUExport
2+
3+
A utility that upgrades the ICU version for the [swift-foundation-icu](https://github.com/swiftlang/swift-foundation-icu) repository.
4+
5+
## Usage
6+
7+
`icu-export` performs a merge between the existing `swift-foundation-icu` and the upgraded `ICU` repository to extract relevant files from each. To start, first clone the two repositories:
8+
9+
```bash
10+
git clone https://github.com/apple-oss-distributions/ICU.git /path/to/ICU
11+
git clone https://github.com/swiftlang/swift-foundation-icu.git /path/to/swift-foundation-icu
12+
```
13+
14+
Run the utility:
15+
16+
```bash
17+
swift run icu-export -i /path/to/ICU -s /path/to/swift-foundation-icu -o /output/path
18+
```
19+
20+
`icu-export` will create a directory `SwiftFoundationICU` under `/output/path` that contains the newly updated ICU.
21+
22+
23+
### Known Limitations
24+
25+
Depending on the changes made to each ICU upgrade, `icu-export` may fail to apply the patches in `Sources/Patches`. In such cases, please manually apply the changes.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
From ff7d419b581bff5cbdd1a1b483d9389339558a00 Mon Sep 17 00:00:00 2001
2+
From: Charles Hu <[email protected]>
3+
Date: Tue, 23 Jul 2024 18:32:04 -0700
4+
Subject: [PATCH 1/2] Hide all ICU public C++ Symbols
5+
6+
**Rationale:** When FoundationInternationalization tests are executed, we effectively load two ICU instances into memory: 1) The system ICU loaded by XCTest via system Foundation; 2) The package ICU SwiftFoundation utilizes.
7+
8+
These two ICUs cause symbol collisions for dyld due to the fact that all public C++ symbols share a global namespace and coalesce across all loaded dylibs. Consequently, we encounter sporadic test failures in SwiftFoundation as dyld arbitrarily selects ICU symbols and occasionally chooses the system one.
9+
10+
To address this issue, we resolved to hide all C++ APIs, ensuring they are not weakly referenced and potentially bound to the system ICU implementation. This solution proves effective for SwiftFoundation, as it does not actually utilize the C++ APIs.
11+
---
12+
.../include/_foundation_unicode/utypes.h | 32 +++++++++++++++----
13+
1 file changed, 25 insertions(+), 7 deletions(-)
14+
15+
diff --git a/icuSources/include/_foundation_unicode/utypes.h b/icuSources/include/_foundation_unicode/utypes.h
16+
index e707465..10f7189 100644
17+
--- a/icuSources/include/_foundation_unicode/utypes.h
18+
+++ b/icuSources/include/_foundation_unicode/utypes.h
19+
@@ -354,13 +354,31 @@ typedef double UDate;
20+
#endif
21+
22+
#if defined(U_COMBINED_IMPLEMENTATION)
23+
-#define U_DATA_API U_EXPORT
24+
-#define U_COMMON_API U_EXPORT
25+
-#define U_I18N_API U_EXPORT
26+
-#define U_LAYOUT_API U_EXPORT
27+
-#define U_LAYOUTEX_API U_EXPORT
28+
-#define U_IO_API U_EXPORT
29+
-#define U_TOOLUTIL_API U_EXPORT
30+
+// SwiftFoundationICU Changes: hide all C++ public symbols
31+
+// Rationale: When FoundationInternationalization tests are executed,
32+
+// we effectively load two ICU instances into memory:
33+
+//
34+
+// 1) The system ICU loaded by XCTest via system Foundation;
35+
+// 2) The package ICU SwiftFoundation utilizes.
36+
+//
37+
+// These two ICUs cause symbol collisions for dyld due to the fact that
38+
+// all public C++ symbols share a global namespace and coalesce across all loaded dylibs.
39+
+// Consequently, we encounter sporadic test failures in SwiftFoundation as dyld
40+
+// arbitrarily selects ICU symbols and occasionally chooses the system one.
41+
+//
42+
+// To address this issue, we resolved to hide all C++ APIs,
43+
+// ensuring they are not weakly referenced and potentially bound to
44+
+// the system ICU implementation.
45+
+//
46+
+// This solution proves effective for SwiftFoundation,
47+
+// as it does not actually utilize the C++ APIs.
48+
+#define U_DATA_API __attribute__((visibility("hidden")))
49+
+#define U_COMMON_API __attribute__((visibility("hidden")))
50+
+#define U_I18N_API __attribute__((visibility("hidden")))
51+
+#define U_LAYOUT_API __attribute__((visibility("hidden")))
52+
+#define U_LAYOUTEX_API __attribute__((visibility("hidden")))
53+
+#define U_IO_API __attribute__((visibility("hidden")))
54+
+#define U_TOOLUTIL_API __attribute__((visibility("hidden")))
55+
#elif defined(U_STATIC_IMPLEMENTATION)
56+
#define U_DATA_API
57+
#define U_COMMON_API
58+
59+
From b671477b99b6d4b4a32b3426a0fc42ec242d7d2a Mon Sep 17 00:00:00 2001
60+
From: Charles Hu <[email protected]>
61+
Date: Fri, 26 Jul 2024 14:02:53 -0700
62+
Subject: [PATCH 2/2] Use U_CAPI for uspoof_getInclusionUnicodeSet and
63+
uspoof_getRecommendedUnicodeSet
64+
65+
---
66+
icuSources/i18n/uspoof.cpp | 4 ++--
67+
1 file changed, 2 insertions(+), 2 deletions(-)
68+
69+
diff --git a/icuSources/i18n/uspoof.cpp b/icuSources/i18n/uspoof.cpp
70+
index 206e0b2..d1ec69d 100644
71+
--- a/icuSources/i18n/uspoof.cpp
72+
+++ b/icuSources/i18n/uspoof.cpp
73+
@@ -781,13 +781,13 @@ uspoof_getRecommendedSet(UErrorCode *status) {
74+
return gRecommendedSet->toUSet();
75+
}
76+
77+
-U_I18N_API const UnicodeSet * U_EXPORT2
78+
+U_CAPI const UnicodeSet * U_EXPORT2
79+
uspoof_getInclusionUnicodeSet(UErrorCode *status) {
80+
umtx_initOnce(gSpoofInitStaticsOnce, &initializeStatics, *status);
81+
return gInclusionSet;
82+
}
83+
84+
-U_I18N_API const UnicodeSet * U_EXPORT2
85+
+U_CAPI const UnicodeSet * U_EXPORT2
86+
uspoof_getRecommendedUnicodeSet(UErrorCode *status) {
87+
umtx_initOnce(gSpoofInitStaticsOnce, &initializeStatics, *status);
88+
return gRecommendedSet;
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
From db45750963292c30f0eb84833b522a42fd9a2fe4 Mon Sep 17 00:00:00 2001
2+
From: Charles Hu <[email protected]>
3+
Date: Thu, 25 Jul 2024 14:52:24 -0700
4+
Subject: [PATCH] putil.cpp SwiftFoundationICU changes
5+
6+
---
7+
icuSources/common/putil.cpp | 40 -------------------------------------
8+
1 file changed, 40 deletions(-)
9+
10+
diff --git a/icuSources/common/putil.cpp b/icuSources/common/putil.cpp
11+
index 89216be..f318f56 100644
12+
--- a/icuSources/common/putil.cpp
13+
+++ b/icuSources/common/putil.cpp
14+
@@ -1426,31 +1426,6 @@ static BOOL U_CALLCONV getIcuDataDirectoryUnderWindowsDirectory(char* directoryB
15+
}
16+
#endif
17+
18+
-#if APPLE_ICU_CHANGES
19+
-// rdar://102831360 (Add Swift Package Manager support to ICU)
20+
-#if defined(USE_PACKAGE_DATA)
21+
-const char* getPackageICUDataPath() {
22+
- Dl_info dl_info;
23+
- dladdr(reinterpret_cast<const void*>(getPackageICUDataPath), &dl_info);
24+
- const char* libraryFilename = dl_info.dli_fname;
25+
- if (libraryFilename != NULL && libraryFilename[0] != 0) {
26+
- // Remove the executable name
27+
- char path[PATH_MAX + 1];
28+
- strncpy(path, libraryFilename, PATH_MAX);
29+
- char *lastSlash = strrchr(path, '/');
30+
- if (lastSlash) {
31+
- // Terminate the string at /
32+
- *lastSlash = 0;
33+
- }
34+
- // Append the resource bundle path // rdar://104488214
35+
- strcat(path, "/FoundationICU_FoundationICU.resources");
36+
- return strdup(path);
37+
- }
38+
- return "";
39+
-}
40+
-#endif
41+
-#endif // APPLE_ICU_CHANGES
42+
-
43+
static void U_CALLCONV dataDirectoryInitFn() {
44+
/* If we already have the directory, then return immediately. Will happen if user called
45+
* u_setDataDirectory().
46+
@@ -1459,14 +1434,6 @@ static void U_CALLCONV dataDirectoryInitFn() {
47+
return;
48+
}
49+
50+
-#if APPLE_ICU_CHANGES
51+
-// rdar://102831360 (Add Swift Package Manager support to ICU)
52+
-#if defined(USE_PACKAGE_DATA)
53+
- u_setDataDirectory(getPackageICUDataPath());
54+
- return;
55+
-#endif
56+
-#endif // APPLE_ICU_CHANGES
57+
-
58+
const char *path = nullptr;
59+
#if defined(ICU_DATA_DIR_PREFIX_ENV_VAR)
60+
char datadir_path_buffer[PATH_MAX];
61+
@@ -1603,13 +1570,6 @@ static void U_CALLCONV TimeZoneDataDirInitFn(UErrorCode &status) {
62+
dir = getenv("ICU_TIMEZONE_FILES_DIR");
63+
#endif // U_PLATFORM_HAS_WINUWP_API
64+
65+
-#if APPLE_ICU_CHANGES
66+
-// rdar://102831360 (Add Swift Package Manager support to ICU)
67+
-#if defined(USE_PACKAGE_DATA)
68+
- dir = getPackageICUDataPath();
69+
-#endif
70+
-#endif // APPLE_ICU_CHANGES
71+
-
72+
#if defined(U_TIMEZONE_FILES_DIR)
73+
if (dir == nullptr) {
74+
// Build time configuration setting.
75+
--
76+
2.39.5 (Apple Git-154)
77+

0 commit comments

Comments
 (0)