Summary
We are seeing intermittent build failures in CI where SKIE-generated Objective‑C headers contain duplicate @interface definitions (e.g., __SkieLambdaErrorType and __SkieUnknownCInteropFrameworkErrorType). This appears when multiple linkFrameworkIos tasks run in parallel across multiple KMP modules/targets. The failure is resolved when those iOS link tasks are serialized.
Environment
• OS: macOS (CI agent)
• Xcode: 26.2.0
• Kotlin: 2.2.21
• Gradle: 8.12
• SKIE plugin: touchlab.skie
• Build context: multiple KMP modules (3+) with iOS targets (iosArm64, iosX64, iosSimulatorArm64) AND iOS native projects
• Gradle parallel: org.gradle.parallel=true
Observed Failure
CI fails during iOS framework linking with duplicate interface definitions in a SKIE-generated header:
/Users/jenkins/workspace/.../core-project/core/build/skie/binaries/debugFramework/DEBUG/iosArm64/cache/kotlin-framework/SDKCoreInternal.framework/Headers/SDKCoreInternal.h:40:1: error: duplicate interface definition for class '__SkieLambdaErrorType'
@interface __SkieLambdaErrorType : NSObject
...
/Users/jenkins/workspace/.../SDKCoreInternal.h:50:1: error: duplicate interface definition for class '__SkieUnknownCInteropFrameworkErrorType'
@interface __SkieUnknownCInteropFrameworkErrorType : NSObject
Suspected Root Cause SKIE cache generation appears to be non‑thread‑safe when multiple linkFrameworkIos tasks run concurrently. Multiple modules/targets are writing into the same SKIE cache folder under build/skie/.../cache/kotlin-framework/..., resulting in duplicated interface blocks inside the generated header.
Reproduction (approximate)
- Have a multi‑module KMP project where multiple modules apply the touchlab.skie plugin, and also a native iOS module directly integrated with a Xcode Build Script phase which calls
./gradlew :<Shared module name>:embedAndSignAppleFrameworkForXcode
- Enable Gradle parallel execution: org.gradle.parallel=true.
- Trigger iOS framework link tasks in parallel (e.g., linkDebugFrameworkIosArm64, linkDebugFrameworkIosX64, linkDebugFrameworkIosSimulatorArm64, embedAndSignAppleFrameworkForXcode) across modules. See https://kotlinlang.org/docs/multiplatform/multiplatform-direct-integration.html#connect-the-framework-to-your-project
- CI intermittently fails with duplicate interface definitions inside SKIE-generated headers.
Expected Behavior
SKIE‑generated headers are deterministic and free of duplicate @interface definitions, even when multiple iOS framework link tasks run in parallel across modules.
Actual Behavior
Intermittent failures with duplicate interface definitions (see log snippet above).
Workaround / Mitigation We avoided the failure by serializing only the leaf iOS framework link tasks (per target) across all modules, while keeping overall Gradle parallelism enabled. This suggests the issue is due to concurrent SKIE cache writes.
Questions for Maintainers
• Is SKIE’s cache generation for iOS framework linking intended to be safe under parallel execution across modules?
• Is there a recommended configuration to isolate SKIE caches per task/module/target to avoid concurrent writes?
• Would SKIE accept a PR to namespace cache output by module/target to prevent cross‑task collisions?
Additional Context We also noticed this happens only on CI with higher concurrency; local builds are less likely to fail, which further points to a race condition.
Summary
We are seeing intermittent build failures in CI where SKIE-generated Objective‑C headers contain duplicate @interface definitions (e.g., __SkieLambdaErrorType and __SkieUnknownCInteropFrameworkErrorType). This appears when multiple linkFrameworkIos tasks run in parallel across multiple KMP modules/targets. The failure is resolved when those iOS link tasks are serialized.
Environment
• OS: macOS (CI agent)
• Xcode: 26.2.0
• Kotlin: 2.2.21
• Gradle: 8.12
• SKIE plugin: touchlab.skie
• Build context: multiple KMP modules (3+) with iOS targets (iosArm64, iosX64, iosSimulatorArm64) AND iOS native projects
• Gradle parallel: org.gradle.parallel=true
Observed Failure
CI fails during iOS framework linking with duplicate interface definitions in a SKIE-generated header:
/Users/jenkins/workspace/.../core-project/core/build/skie/binaries/debugFramework/DEBUG/iosArm64/cache/kotlin-framework/SDKCoreInternal.framework/Headers/SDKCoreInternal.h:40:1: error: duplicate interface definition for class '__SkieLambdaErrorType'
@interface __SkieLambdaErrorType : NSObject
...
/Users/jenkins/workspace/.../SDKCoreInternal.h:50:1: error: duplicate interface definition for class '__SkieUnknownCInteropFrameworkErrorType'
@interface __SkieUnknownCInteropFrameworkErrorType : NSObject
Suspected Root Cause SKIE cache generation appears to be non‑thread‑safe when multiple linkFrameworkIos tasks run concurrently. Multiple modules/targets are writing into the same SKIE cache folder under build/skie/.../cache/kotlin-framework/..., resulting in duplicated interface blocks inside the generated header.
Reproduction (approximate)
./gradlew :<Shared module name>:embedAndSignAppleFrameworkForXcodeExpected Behavior
SKIE‑generated headers are deterministic and free of duplicate @interface definitions, even when multiple iOS framework link tasks run in parallel across modules.
Actual Behavior
Intermittent failures with duplicate interface definitions (see log snippet above).
Workaround / Mitigation We avoided the failure by serializing only the leaf iOS framework link tasks (per target) across all modules, while keeping overall Gradle parallelism enabled. This suggests the issue is due to concurrent SKIE cache writes.
Questions for Maintainers
• Is SKIE’s cache generation for iOS framework linking intended to be safe under parallel execution across modules?
• Is there a recommended configuration to isolate SKIE caches per task/module/target to avoid concurrent writes?
• Would SKIE accept a PR to namespace cache output by module/target to prevent cross‑task collisions?
Additional Context We also noticed this happens only on CI with higher concurrency; local builds are less likely to fail, which further points to a race condition.