Merge pull request #1696 from Tencent/dev

Fix KMP IDE imports and sample builds
This commit is contained in:
Guo Ling
2026-10-16 19:09:26 +08:00
committed by GitHub
6 changed files with 76 additions and 8 deletions
+42 -1
View File
@@ -150,7 +150,30 @@ duplicate native symbols.
## Build from source
Building the KMP project and its iOS targets requires macOS, Xcode command-line
tools, CMake, and JDK 11 or newer:
tools, CMake, and JDK 21. The checked-in Gradle daemon JVM criteria select JDK 21
for both command-line builds and Android Studio imports; install it before syncing.
If Android Studio cannot find `cmake` on its PATH, set `cmake.dir` to the CMake
installation directory containing `bin/cmake` in the untracked
`KMP/local.properties`. For example, with Homebrew on Apple Silicon:
```properties
cmake.dir=/opt/homebrew
```
Both native configure and build tasks use this path. When `cmake.dir` is unset,
they use `cmake` from PATH.
Open the `KMP` directory in Android Studio. The sample is enabled by
`MMKV_INCLUDE_SAMPLE=true` in `gradle.properties`. After syncing, select the
Android App run configuration for `:sample:composeApp` to run the Android sample.
Use `-PMMKV_INCLUDE_SAMPLE=false` for a library-only build.
The sample enables Android and iOS by default. Its desktop configuration is
disabled because the MMKV wrapper does not yet provide a JVM target. The existing
desktop scaffold is retained behind `-PMMKV_ENABLE_DESKTOP=true` for development;
it still requires JVM support in the wrapper before it can build. Published-MMKV
mode always disables desktop, even when this flag is set.
```bash
cd KMP
@@ -161,4 +184,22 @@ cd KMP
`com.tencent:mmkv:2.4.3` Android artifact has been published only to Maven
Local.
To prepare the iOS sample, install CocoaPods and run the following from `KMP`:
```bash
./gradlew :sample:composeApp:podInstall -PMMKV_INCLUDE_SAMPLE=true
open sample/iosApp/iosApp.xcworkspace
```
This generates the Pods support files, including the `.xcconfig` files referenced
by the Xcode project. Use the generated workspace for Xcode builds. The sample is
included automatically when Xcode invokes Gradle through the CocoaPods build script.
When running the iOS sample from Android Studio, its linked Xcode project must
use `sample/iosApp/iosApp.xcworkspace` as well. If the IDE linked the `.xcodeproj`
before `podInstall` created the workspace, invoke **Unlink Xcode project** via
**Find Action**, then **Sync Project with Gradle Files** to detect the workspace.
Building the standalone project omits the Pods target and can fail with
`Framework 'Pods_iosApp' not found`.
Release maintainers should also follow [PUBLISHING.md](./PUBLISHING.md).
+3
View File
@@ -1,6 +1,9 @@
# Project-wide Gradle settings.
org.gradle.jvmargs=-Xmx2048m
# Include the sample application in Android Studio imports.
MMKV_INCLUDE_SAMPLE=true
# Android
android.useAndroidX=true
android.nonTransitiveRClass=false
+2
View File
@@ -0,0 +1,2 @@
# Keep the build JVM compatible with Gradle 8.14, including Android Studio imports.
toolchainVersion=21
+18 -2
View File
@@ -18,6 +18,7 @@
* limitations under the License.
*/
import java.util.Properties
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.tasks.Exec
import org.gradle.api.tasks.bundling.Jar
@@ -71,6 +72,19 @@ version = publishVersion
val nativeInteropDir = project.file("nativeInterop")
val nativeBuildRoot = nativeInteropDir.resolve("build")
// These custom Exec tasks do not use AGP's CMake discovery. Honor cmake.dir so
// IDE imports do not depend on the GUI process inheriting the shell's PATH.
val localProperties = Properties().apply {
val propertiesFile = rootProject.file("local.properties")
if (propertiesFile.isFile) {
propertiesFile.inputStream().use { load(it) }
}
}
val cmakeExecutable = localProperties.getProperty("cmake.dir")
?.trim()?.takeIf { it.isNotEmpty() }
?.let { rootProject.file(it).resolve("bin/cmake").absolutePath }
?: "cmake"
fun taskSuffix(label: String): String =
label.split('-', '_')
.filter { it.isNotBlank() }
@@ -90,6 +104,7 @@ fun registerCMakeBuildTask(
inputs.file(nativeInteropDir.resolve("CMakeLists.txt"))
inputs.file(nativeInteropDir.resolve("cinterop/mmkv.def"))
inputs.property("cmakeExecutable", cmakeExecutable)
outputs.file(buildDir.resolve("CMakeCache.txt"))
outputs.file(buildDir.resolve("include/MMKVBridge.h"))
@@ -98,7 +113,7 @@ fun registerCMakeBuildTask(
workingDir = nativeInteropDir
commandLine(
buildList {
add("cmake")
add(cmakeExecutable)
add("-S")
add(".")
add("-B")
@@ -126,6 +141,7 @@ fun registerCMakeBuildTask(
inputs.file(nativeInteropDir.resolve("CMakeLists.txt"))
inputs.file(nativeInteropDir.resolve("cinterop/mmkv.def"))
inputs.property("cmakeExecutable", cmakeExecutable)
outputs.file(buildDir.resolve("libmmkv-kmp.a"))
// Let CMake perform its own incremental source check. Gradle cannot
// reliably model FetchContent/local Core source inputs here.
@@ -134,7 +150,7 @@ fun registerCMakeBuildTask(
workingDir = nativeInteropDir
commandLine(
buildList {
add("cmake")
add(cmakeExecutable)
add("--build")
add(buildDir.absolutePath)
add("--config")
+7 -4
View File
@@ -7,6 +7,10 @@ plugins {
}
val usePublishedMMKV = providers.gradleProperty("MMKV_USE_PUBLISHED").orNull == "true"
// The MMKV wrapper currently has no JVM target. Keep the desktop scaffold
// opt-in so normal imports and aggregate builds only use supported targets.
val enableDesktop = !usePublishedMMKV &&
providers.gradleProperty("MMKV_ENABLE_DESKTOP").orNull == "true"
val mmkvVersion = providers.gradleProperty("VERSION_NAME").getOrElse("2.4.3")
kotlin {
@@ -16,8 +20,7 @@ kotlin {
}
}
if (!usePublishedMMKV) {
// Desktop is available only when the sample consumes the in-tree project.
if (enableDesktop) {
jvm("desktop") {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
@@ -61,7 +64,7 @@ kotlin {
implementation("androidx.activity:activity-compose:1.10.1")
}
}
if (!usePublishedMMKV) {
if (enableDesktop) {
val desktopMain by getting {
dependencies {
implementation(compose.desktop.currentOs)
@@ -71,7 +74,7 @@ kotlin {
}
}
if (!usePublishedMMKV) {
if (enableDesktop) {
compose.desktop {
application {
mainClass = "com.tencent.mmkv.sample.MainKt"
+4 -1
View File
@@ -26,6 +26,9 @@ dependencyResolutionManagement {
}
include(":mmkv")
if (providers.gradleProperty("MMKV_INCLUDE_SAMPLE").orNull == "true") {
// Xcode's generated CocoaPods script passes platform/arch/configuration, but
// does not forward the MMKV_INCLUDE_SAMPLE flag used during pod installation.
val isCocoaPodsBuild = providers.gradleProperty("kotlin.native.cocoapods.platform").isPresent
if (providers.gradleProperty("MMKV_INCLUDE_SAMPLE").orNull == "true" || isCocoaPodsBuild) {
include(":sample:composeApp")
}