From 1acb28c02f29c35c3b39b04ba4c8c74ed61213d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Fri, 6 Mar 2026 07:53:22 +0100 Subject: [PATCH 1/4] fix: use custom modulemap to prevent Xcode 26 ODR errors with CocoaPods The auto-generated modulemap includes a .Swift Clang submodule referencing RiveRuntime-Swift.h, which bakes in Swift stdlib C++ interop definitions for the exact Swift version used to build. When CocoaPods consumers use a different Xcode/Swift version, the differing type definitions cause hard ODR errors. --- Config/Base.xcconfig | 5 +++++ Source/RiveRuntime.modulemap | 6 ++++++ 2 files changed, 11 insertions(+) create mode 100644 Source/RiveRuntime.modulemap diff --git a/Config/Base.xcconfig b/Config/Base.xcconfig index 20378c3a..4330831b 100644 --- a/Config/Base.xcconfig +++ b/Config/Base.xcconfig @@ -11,3 +11,8 @@ GCC_PREPROCESSOR_DEFINITIONS = $(inherited) WITH_RIVE_AUDIO WITH_RIVE_TEXT WITH_RIVE_SCRIPTING SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) WITH_RIVE_AUDIO WITH_RIVE_TEXT WITH_RIVE_SCRIPTING + +// Custom modulemap omitting the .Swift Clang submodule to prevent ODR errors +// when CocoaPods consumers use a different Xcode/Swift version than the one +// used to build this XCFramework. +MODULEMAP_FILE = $(SRCROOT)/Source/RiveRuntime.modulemap diff --git a/Source/RiveRuntime.modulemap b/Source/RiveRuntime.modulemap new file mode 100644 index 00000000..015544bc --- /dev/null +++ b/Source/RiveRuntime.modulemap @@ -0,0 +1,6 @@ +framework module RiveRuntime { + umbrella header "RiveRuntime.h" + export * + + module * { export * } +} From 96ebd32357e658cec2382d81c887b2e3eb918810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Wed, 11 Mar 2026 14:31:53 +0100 Subject: [PATCH 2/4] fix: post-process XCFramework to strip RiveRuntime.Swift modulemap submodule The Swift build phase unconditionally appends a `module RiveRuntime.Swift` block to the framework's module.modulemap, referencing RiveRuntime-Swift.h. This header bakes in Swift stdlib C++ interop type definitions (e.g. swift::Optional::init parameter names) for the exact Swift version used to build the XCFramework. When consumers use a different Xcode/Swift version, these definitions conflict with the consumer's own Swift module definitions, causing hard ODR errors that break the build. The MODULEMAP_FILE build setting cannot prevent this: Xcode appends the Swift submodule unconditionally after applying any custom modulemap. Fix: post-process every module.modulemap in the assembled XCFramework to strip the `module RiveRuntime.Swift { ... }` block. Without this entry, Clang never loads the stale RiveRuntime-Swift.h into the module graph, so the conflicting C++ type definitions are never seen. This makes the shipped XCFramework compatible with any Xcode/Swift version for both CocoaPods and SPM consumers. Co-Authored-By: Claude Sonnet 4.6 --- Config/Base.xcconfig | 5 ----- scripts/build_framework.sh | 17 ++++++++++++++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Config/Base.xcconfig b/Config/Base.xcconfig index 4330831b..20378c3a 100644 --- a/Config/Base.xcconfig +++ b/Config/Base.xcconfig @@ -11,8 +11,3 @@ GCC_PREPROCESSOR_DEFINITIONS = $(inherited) WITH_RIVE_AUDIO WITH_RIVE_TEXT WITH_RIVE_SCRIPTING SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) WITH_RIVE_AUDIO WITH_RIVE_TEXT WITH_RIVE_SCRIPTING - -// Custom modulemap omitting the .Swift Clang submodule to prevent ODR errors -// when CocoaPods consumers use a different Xcode/Swift version than the one -// used to build this XCFramework. -MODULEMAP_FILE = $(SRCROOT)/Source/RiveRuntime.modulemap diff --git a/scripts/build_framework.sh b/scripts/build_framework.sh index fb6f267d..1834edb6 100755 --- a/scripts/build_framework.sh +++ b/scripts/build_framework.sh @@ -33,7 +33,7 @@ xcodebuild archive \ -destination generic/platform=iOS \ -archivePath ".build/archives/RiveRuntime_iOS" \ SKIP_INSTALL=NO \ - BUILD_LIBRARY_FOR_DISTRIBUTION=YES + BUILD_LIBRARY_FOR_DISTRIBUTION=YES xcodebuild archive \ -configuration ${CONFIGURATION} \ @@ -123,3 +123,18 @@ xcodebuild \ -framework RiveRuntime.framework \ -output archive/RiveRuntime.xcframework +# Post-process: strip the auto-appended `module RiveRuntime.Swift { ... }` block +# from every modulemap in the XCFramework. The Swift build phase unconditionally +# appends this block even when MODULEMAP_FILE is set, re-introducing the stale +# Swift C++ interop header that causes ODR violations in consumers using a different +# Xcode/Swift version. Stripping it here prevents Clang from ever loading the header. +echo "Stripping RiveRuntime.Swift submodule from XCFramework modulemaps..." +find archive/RiveRuntime.xcframework -name "module.modulemap" | while read -r map; do + # The Swift build phase unconditionally appends `module RiveRuntime.Swift { ... }` + # to the modulemap even when MODULEMAP_FILE is set. Strip it so Clang never loads + # the stale C++ interop header (which causes ODR errors across Swift versions). + perl -0777 -i -pe 's/\n*^module RiveRuntime\.Swift \{[^}]*\}\n?//mg' "$map" + echo " Patched: $map" +done +echo "Done." + From 91858081c9f1675d14922e136d153152c57b0eef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Wed, 11 Mar 2026 14:33:58 +0100 Subject: [PATCH 3/4] chore: remove unused RiveRuntime.modulemap The custom modulemap file is no longer needed. The Xcode 26 ODR fix is handled entirely by the post-process step in build_framework.sh which strips the `module RiveRuntime.Swift` block from the assembled XCFramework after build. Co-Authored-By: Claude Sonnet 4.6 --- Source/RiveRuntime.modulemap | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 Source/RiveRuntime.modulemap diff --git a/Source/RiveRuntime.modulemap b/Source/RiveRuntime.modulemap deleted file mode 100644 index 015544bc..00000000 --- a/Source/RiveRuntime.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module RiveRuntime { - umbrella header "RiveRuntime.h" - export * - - module * { export * } -} From 3cc72fc6c39b16f325ef90fd150e39abd0470050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Wed, 11 Mar 2026 14:41:45 +0100 Subject: [PATCH 4/4] chore: deduplicate comment in build_framework.sh post-process block Co-Authored-By: Claude Sonnet 4.6 --- scripts/build_framework.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/scripts/build_framework.sh b/scripts/build_framework.sh index 1834edb6..43f9ef0a 100755 --- a/scripts/build_framework.sh +++ b/scripts/build_framework.sh @@ -123,16 +123,14 @@ xcodebuild \ -framework RiveRuntime.framework \ -output archive/RiveRuntime.xcframework -# Post-process: strip the auto-appended `module RiveRuntime.Swift { ... }` block -# from every modulemap in the XCFramework. The Swift build phase unconditionally -# appends this block even when MODULEMAP_FILE is set, re-introducing the stale -# Swift C++ interop header that causes ODR violations in consumers using a different -# Xcode/Swift version. Stripping it here prevents Clang from ever loading the header. +# Strip the `module RiveRuntime.Swift { ... }` block from every modulemap in the +# XCFramework. The Swift build phase unconditionally appends this block, which +# bakes in C++ interop type definitions for the Swift version used to build the +# XCFramework. Consumers using a different Xcode/Swift version see conflicting +# definitions and get ODR errors. Removing the block prevents Clang from ever +# loading the stale header. echo "Stripping RiveRuntime.Swift submodule from XCFramework modulemaps..." find archive/RiveRuntime.xcframework -name "module.modulemap" | while read -r map; do - # The Swift build phase unconditionally appends `module RiveRuntime.Swift { ... }` - # to the modulemap even when MODULEMAP_FILE is set. Strip it so Clang never loads - # the stale C++ interop header (which causes ODR errors across Swift versions). perl -0777 -i -pe 's/\n*^module RiveRuntime\.Swift \{[^}]*\}\n?//mg' "$map" echo " Patched: $map" done