Skip to content

Commit 868349f

Browse files
author
onion
committed
feat: ios cmake support
1 parent 7f090a2 commit 868349f

File tree

2 files changed

+107
-1
lines changed

2 files changed

+107
-1
lines changed

composeApp/build.gradle.kts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,20 @@ kotlin {
4646
}
4747
iosTarget.binaries.framework {
4848
baseName = "ComposeApp"
49-
isStatic = true
49+
// isStatic = true
50+
// 优点:启动速度可能稍快,因为不需要在运行时加载动态库。
51+
// 缺点:如果多个扩展(如 WidgetKit)都使用了这个静态框架,每个扩展都会有一份代码拷贝,导致应用总体积增大。 可能会遇到符号冲突(duplicate symbols)问题,特别是当框架内部依赖的库与主应用或其他框架依赖的库版本不同时。
52+
// isStatic = false?
53+
// 正确链接原生依赖(C/C++ 库): 当你构建一个动态框架时,linkerOpts 中指定的 -l... 和 -framework... 选项会记录为框架的运行时依赖。当你的应用启动并加载这个动态框架时,iOS 的动态链接器(dyld)会根据这些记录去寻找并加载所需的系统框架(如 Metal, Accelerate)和你提供的 C++ 库。如果 isStatic = true,链接器会尝试在构建静态库时就将所有原生库的代码合并进来,这非常复杂且极易出错,常常导致链接失败或运行时找不到符号(undefined symbols)。
54+
// 避免重复符号错误 (Duplicate Symbols): 如果你的 C++ 库或者它依赖的任何系统库,与你的主 App Target 或其他依赖的框架有重叠,使用静态链接会把这些符号复制多份到最终的可执行文件中,从而导致“duplicate symbols”链接错误。而动态框架则可以确保这些共享库只被加载一次。
55+
// 模块化和代码隔离: 使用动态框架可以将你的 Kotlin 和 C++ 封装成一个独立的模块。这使得依赖管理更加清晰。主应用只需要知道它依赖于 ComposeApp.framework,而不需要关心其内部复杂的原生链接细节。
56+
isStatic = false
57+
}
58+
iosTarget.compilations["main"].cinterops {
59+
val sdloader by creating {
60+
defFile(project.file("src/nativeInterop/cinterop/sdloader.def"))
61+
includeDirs(project.file("${rootProject.projectDir}/cpp/stable-diffusion.cpp/include"))
62+
}
5063
}
5164
}
5265

patch/mac_cmake.patch

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
Subject: [PATCH] mac cmake
2+
---
3+
Index: composeApp/build.gradle.kts
4+
IDEA additional info:
5+
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
6+
<+>UTF-8
7+
===================================================================
8+
diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts
9+
--- a/composeApp/build.gradle.kts (revision dd6004d75563bc12b389f706ce3dad6275e41ce3)
10+
+++ b/composeApp/build.gradle.kts (date 1771140321233)
11+
@@ -22,7 +22,7 @@
12+
jvmTarget.set(JvmTarget.JVM_21)
13+
}
14+
}
15+
-
16+
+
17+
listOf(
18+
iosX64(),
19+
iosArm64(),
20+
@@ -33,12 +33,12 @@
21+
isStatic = true
22+
}
23+
}
24+
-
25+
+
26+
jvm("desktop")
27+
-
28+
+
29+
sourceSets {
30+
val desktopMain by getting
31+
-
32+
+
33+
androidMain.dependencies {
34+
implementation(compose.preview)
35+
implementation(libs.androidx.activity.compose)
36+
@@ -272,10 +272,17 @@
37+
val generator = cmakeGenerator.get()
38+
val options = cmakeOptions.get()
39+
40+
+ val cmakePath = findCMake()
41+
+ println("Using CMake: $cmakePath")
42+
+
43+
+ // Get the current JVM path used by Gradle
44+
+ val javaHome = System.getProperty("java.home")
45+
+
46+
// 1. Configure CMake
47+
execOps.exec {
48+
workingDir = workDir
49+
- commandLine("cmake", "-S", ".", "-B", "build-$platform", "-G", generator)
50+
+ environment("JAVA_HOME", javaHome)
51+
+ commandLine(cmakePath, "-S", ".", "-B", "build-$platform", "-G", generator)
52+
args(options)
53+
isIgnoreExitValue = false
54+
}
55+
@@ -283,10 +290,23 @@
56+
// 2. Build CMake
57+
execOps.exec {
58+
workingDir = workDir
59+
- commandLine("cmake", "--build", "build-$platform", "--config", "Release")
60+
+ environment("JAVA_HOME", javaHome)
61+
+ commandLine(cmakePath, "--build", "build-$platform", "--config", "Release")
62+
isIgnoreExitValue = false
63+
}
64+
}
65+
+
66+
+ private fun findCMake(): String {
67+
+ val candidates = listOf(
68+
+ "/opt/homebrew/bin/cmake", // macOS Apple Silicon
69+
+ "/usr/local/bin/cmake", // macOS Intel
70+
+ "/usr/bin/cmake" // Linux / macOS default
71+
+ )
72+
+ for (path in candidates) {
73+
+ if (File(path).exists()) return path
74+
+ }
75+
+ return "cmake" // Fallback to command from PATH
76+
+ }
77+
}
78+
// 捕获 Configuration Phase 的变量,供 Execution Phase 使用,避免 configuration cache 问题
79+
val rootDirVal = rootDir
80+
@@ -405,10 +425,10 @@
81+
82+
// 如果库不存在,则添加构建任务依赖
83+
if (!libFile.exists() && currentPlatform.isNotEmpty()) {
84+
- println("原生库不存在,配置构建任务依赖...")
85+
- dependsOn("buildNativeLibFor$currentPlatform")
86+
+ println("原生库不存在,配置构建任务依赖...")
87+
+ dependsOn("buildNativeLibFor$currentPlatform")
88+
} else {
89+
- println("原生库已存在 (配置阶段检查)")
90+
+ println("原生库已存在 (配置阶段检查)")
91+
}
92+
// 捕获需要的路径字符串,供 doLast 使用
93+
val cppLibsDirStr = cppLibsDirVal

0 commit comments

Comments
 (0)