mirror of
https://github.com/Tencent/MMKV.git
synced 2026-10-17 04:30:56 +08:00
375 lines
13 KiB
Kotlin
375 lines
13 KiB
Kotlin
/*
|
|
* Tencent is pleased to support the open source community by making
|
|
* MMKV available.
|
|
*
|
|
* Copyright (C) 2026 THL A29 Limited, a Tencent company.
|
|
* All rights reserved.
|
|
*
|
|
* Licensed under the BSD 3-Clause License (the "License"); you may not use
|
|
* this file except in compliance with the License. You may obtain a copy of
|
|
* the License at
|
|
*
|
|
* https://opensource.org/licenses/BSD-3-Clause
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* 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
|
|
|
|
plugins {
|
|
kotlin("multiplatform")
|
|
id("com.android.kotlin.multiplatform.library")
|
|
id("maven-publish")
|
|
id("signing")
|
|
}
|
|
|
|
val mmkvVersion = (findProperty("MMKV_VERSION") as? String) ?: "2.4.3"
|
|
val publishVersion = (findProperty("VERSION_NAME") as? String) ?: mmkvVersion
|
|
val baseArtifactId = (findProperty("POM_ARTIFACT_ID") as? String) ?: "mmkv-kmp"
|
|
val publishedGroup = (findProperty("GROUP") as? String) ?: "com.tencent"
|
|
val isSnapshot = publishVersion.endsWith("-SNAPSHOT")
|
|
val sonatypeUsername =
|
|
(
|
|
findProperty("SONATYPE_NEXUS_USERNAME")
|
|
?: findProperty("mavenCentralUsername")
|
|
?: findProperty("REPOSITORY_USERNAME")
|
|
) as String?
|
|
val sonatypePassword =
|
|
(
|
|
findProperty("SONATYPE_NEXUS_PASSWORD")
|
|
?: findProperty("mavenCentralPassword")
|
|
?: findProperty("REPOSITORY_PASSWORD")
|
|
) as String?
|
|
val signingKey =
|
|
(findProperty("SIGNING_KEY") ?: findProperty("signingInMemoryKey")) as String?
|
|
val signingPassword =
|
|
(findProperty("SIGNING_PASSWORD") ?: findProperty("signingInMemoryKeyPassword")) as String?
|
|
val hasFileBasedSigning =
|
|
!findProperty("signing.keyId")?.toString().isNullOrBlank() &&
|
|
!findProperty("signing.secretKeyRingFile")?.toString().isNullOrBlank()
|
|
val mmkvGitRepository = findProperty("MMKV_GIT_REPOSITORY") as? String
|
|
val mmkvGitTag = findProperty("MMKV_GIT_TAG") as? String
|
|
val mmkvGitBranch = findProperty("MMKV_GIT_BRANCH") as? String
|
|
val mmkvGitCommit = findProperty("MMKV_GIT_COMMIT") as? String
|
|
val mmkvForceFetch = (findProperty("MMKV_FORCE_FETCH") as? String)?.toBooleanStrictOrNull() == true
|
|
val mmkvGitRef = mmkvGitCommit?.takeIf { it.isNotBlank() }
|
|
?: mmkvGitBranch?.takeIf { it.isNotBlank() }
|
|
?: mmkvGitTag?.takeIf { it.isNotBlank() }
|
|
|
|
// Keep this project's internal coordinates distinct from the published Android
|
|
// dependency (`com.tencent:mmkv`) so same-build resolution never substitutes the
|
|
// native Android AAR with this KMP wrapper project.
|
|
group = "$publishedGroup.kmpbuild"
|
|
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() }
|
|
.joinToString("") { token -> token.replaceFirstChar { it.uppercase() } }
|
|
|
|
fun cmakeBuildDirFor(label: String) = nativeBuildRoot.resolve(label)
|
|
|
|
fun registerCMakeBuildTask(
|
|
label: String,
|
|
cmakeTarget: String = "mmkv-kmp",
|
|
extraConfigureArgs: List<String> = emptyList(),
|
|
): TaskProvider<Exec> {
|
|
val buildDir = cmakeBuildDirFor(label)
|
|
val configureTask = tasks.register<Exec>("cmakeConfigure${taskSuffix(label)}") {
|
|
group = "build"
|
|
description = "Configure CMake for $label"
|
|
|
|
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"))
|
|
|
|
doFirst { buildDir.mkdirs() }
|
|
|
|
workingDir = nativeInteropDir
|
|
commandLine(
|
|
buildList {
|
|
add(cmakeExecutable)
|
|
add("-S")
|
|
add(".")
|
|
add("-B")
|
|
add(buildDir.absolutePath)
|
|
add("-DCMAKE_BUILD_TYPE=Release")
|
|
add("-DMMKV_VERSION=v$mmkvVersion")
|
|
if (!mmkvGitRepository.isNullOrBlank()) {
|
|
add("-DMMKV_GIT_REPOSITORY=$mmkvGitRepository")
|
|
}
|
|
if (!mmkvGitRef.isNullOrBlank()) {
|
|
add("-DMMKV_GIT_TAG=$mmkvGitRef")
|
|
}
|
|
if (mmkvForceFetch) {
|
|
add("-DMMKV_FORCE_FETCH=ON")
|
|
}
|
|
addAll(extraConfigureArgs)
|
|
}
|
|
)
|
|
}
|
|
|
|
return tasks.register<Exec>("cmakeBuild${taskSuffix(label)}") {
|
|
group = "build"
|
|
description = "Build native MMKV artifact for $label via CMake"
|
|
dependsOn(configureTask)
|
|
|
|
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.
|
|
outputs.upToDateWhen { false }
|
|
|
|
workingDir = nativeInteropDir
|
|
commandLine(
|
|
buildList {
|
|
add(cmakeExecutable)
|
|
add("--build")
|
|
add(buildDir.absolutePath)
|
|
add("--config")
|
|
add("Release")
|
|
add("--target")
|
|
add(cmakeTarget)
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
fun publicationArtifactId(publicationName: String): String = when (publicationName) {
|
|
"kotlinMultiplatform" -> baseArtifactId
|
|
"android", "androidRelease" -> "$baseArtifactId-android"
|
|
else -> "$baseArtifactId-${publicationName.lowercase()}"
|
|
}
|
|
|
|
fun pomName(publicationName: String): String = when (publicationName) {
|
|
"kotlinMultiplatform" -> "MMKV Kotlin Multiplatform"
|
|
else -> "MMKV Kotlin Multiplatform ($publicationName)"
|
|
}
|
|
|
|
fun MavenPublication.configurePom(publicationName: String) {
|
|
pom {
|
|
name.set(pomName(publicationName))
|
|
description.set((findProperty("POM_DESCRIPTION") as? String) ?: "Experimental Kotlin Multiplatform wrapper for MMKV")
|
|
url.set((findProperty("POM_URL") as? String) ?: "https://github.com/Tencent/MMKV")
|
|
licenses {
|
|
license {
|
|
name.set((findProperty("POM_LICENCE_NAME") as? String) ?: "BSD 3-Clause License")
|
|
url.set((findProperty("POM_LICENCE_URL") as? String) ?: "https://opensource.org/licenses/BSD-3-Clause")
|
|
}
|
|
}
|
|
developers {
|
|
developer {
|
|
id.set((findProperty("POM_DEVELOPER_ID") as? String) ?: "tencent")
|
|
name.set((findProperty("POM_DEVELOPER_NAME") as? String) ?: "Tencent")
|
|
}
|
|
}
|
|
scm {
|
|
url.set((findProperty("POM_SCM_URL") as? String) ?: "https://github.com/Tencent/MMKV")
|
|
connection.set((findProperty("POM_SCM_CONNECTION") as? String) ?: "scm:git:git://github.com/Tencent/MMKV.git")
|
|
developerConnection.set((findProperty("POM_SCM_DEV_CONNECTION") as? String) ?: "scm:git:ssh://git@github.com/Tencent/MMKV.git")
|
|
}
|
|
}
|
|
}
|
|
|
|
val javadocJars = mutableMapOf<String, TaskProvider<Jar>>()
|
|
|
|
fun javadocJarFor(publicationName: String): TaskProvider<Jar> =
|
|
javadocJars.getOrPut(publicationName) {
|
|
tasks.register<Jar>("${taskSuffix(publicationName)}JavadocJar") {
|
|
// Each publication needs a distinct physical file so Gradle's
|
|
// signing tasks don't race on a shared *.asc output.
|
|
archiveBaseName.set("$baseArtifactId-$publicationName")
|
|
archiveClassifier.set("javadoc")
|
|
from(rootProject.file("README.md"))
|
|
}
|
|
}
|
|
|
|
val verifySonatypePublication = tasks.register("verifySonatypePublication") {
|
|
group = "publishing"
|
|
description = "Fail early when Maven Central credentials or release signing are missing."
|
|
doLast {
|
|
check(!sonatypeUsername.isNullOrBlank()) {
|
|
"Missing SONATYPE_NEXUS_USERNAME (or mavenCentralUsername)."
|
|
}
|
|
check(!sonatypePassword.isNullOrBlank()) {
|
|
"Missing SONATYPE_NEXUS_PASSWORD (or mavenCentralPassword)."
|
|
}
|
|
if (!isSnapshot) {
|
|
check(!signingKey.isNullOrBlank() || hasFileBasedSigning) {
|
|
"Missing an in-memory signing key or Gradle signing.keyId/signing.secretKeyRingFile."
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
kotlin {
|
|
withSourcesJar()
|
|
|
|
android {
|
|
namespace = "com.tencent.mmkv.kmp"
|
|
compileSdk = 35
|
|
minSdk = 23
|
|
compilerOptions {
|
|
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
|
|
}
|
|
withDeviceTestBuilder {
|
|
sourceSetTreeName = "test"
|
|
}.configure {
|
|
instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
|
}
|
|
}
|
|
|
|
// Experimental KMP support currently targets Android + iOS only.
|
|
val darwinTargets = listOf(
|
|
iosArm64(),
|
|
iosSimulatorArm64(),
|
|
iosX64(),
|
|
)
|
|
|
|
applyDefaultHierarchyTemplate {
|
|
common {
|
|
// Keep Darwin source in one shared source set while publishing only iOS targets.
|
|
group("darwin") {
|
|
group("ios")
|
|
}
|
|
}
|
|
}
|
|
|
|
val darwinBuildSettings = mapOf(
|
|
"iosArm64" to listOf("iOS", "iphoneos", "arm64", "13.0"),
|
|
"iosSimulatorArm64" to listOf("iOS", "iphonesimulator", "arm64", "13.0"),
|
|
"iosX64" to listOf("iOS", "iphonesimulator", "x86_64", "13.0"),
|
|
)
|
|
darwinTargets.forEach { target ->
|
|
val label = target.targetName
|
|
val settings = darwinBuildSettings.getValue(label)
|
|
val cmakeTask = registerCMakeBuildTask(
|
|
label = label,
|
|
extraConfigureArgs = listOf(
|
|
"-DCMAKE_SYSTEM_NAME=${settings[0]}",
|
|
"-DCMAKE_OSX_SYSROOT=${settings[1]}",
|
|
"-DCMAKE_OSX_ARCHITECTURES=${settings[2]}",
|
|
"-DCMAKE_OSX_DEPLOYMENT_TARGET=${settings[3]}",
|
|
"-DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY",
|
|
),
|
|
)
|
|
val buildDir = cmakeBuildDirFor(label)
|
|
val generatedIncludeDir = buildDir.resolve("include")
|
|
|
|
target.compilations.getByName("main") {
|
|
cinterops {
|
|
val mmkv by creating {
|
|
defFile("nativeInterop/cinterop/mmkv.def")
|
|
compilerOpts("-I${generatedIncludeDir.absolutePath}")
|
|
includeDirs(generatedIncludeDir)
|
|
extraOpts("-libraryPath", buildDir.absolutePath)
|
|
}
|
|
}
|
|
}
|
|
|
|
tasks.matching { task ->
|
|
task.name.contains(target.targetName, ignoreCase = true) &&
|
|
task.name.startsWith("cinterop")
|
|
}.configureEach {
|
|
dependsOn(cmakeTask)
|
|
}
|
|
}
|
|
|
|
sourceSets {
|
|
commonTest {
|
|
dependencies {
|
|
implementation(kotlin("test"))
|
|
}
|
|
}
|
|
|
|
androidMain {
|
|
dependencies {
|
|
implementation("com.tencent:mmkv:$mmkvVersion")
|
|
implementation("androidx.startup:startup-runtime:1.2.0")
|
|
}
|
|
}
|
|
|
|
val androidDeviceTest by getting {
|
|
dependencies {
|
|
implementation("androidx.test:runner:1.7.0")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
publishing {
|
|
publications.withType<MavenPublication>().configureEach {
|
|
groupId = publishedGroup
|
|
artifactId = publicationArtifactId(name)
|
|
artifact(javadocJarFor(name))
|
|
configurePom(name)
|
|
}
|
|
|
|
repositories {
|
|
maven {
|
|
name = "localTest"
|
|
url = (findProperty("MMKV_LOCAL_REPOSITORY") as? String)
|
|
?.takeIf { it.isNotBlank() }
|
|
?.let(::uri)
|
|
?: uri(layout.buildDirectory.dir("local-maven"))
|
|
}
|
|
|
|
val releaseRepo = findProperty("RELEASE_REPOSITORY_URL") as? String
|
|
val snapshotRepo = findProperty("SNAPSHOT_REPOSITORY_URL") as? String
|
|
val repoUrl = if (isSnapshot) snapshotRepo else releaseRepo
|
|
if (!repoUrl.isNullOrBlank()) {
|
|
maven {
|
|
name = "sonatype"
|
|
url = uri(repoUrl)
|
|
credentials {
|
|
username = sonatypeUsername
|
|
password = sonatypePassword
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tasks.matching {
|
|
it.name.startsWith("publish") && it.name.endsWith("ToSonatypeRepository")
|
|
}.configureEach {
|
|
dependsOn(verifySonatypePublication)
|
|
}
|
|
|
|
signing {
|
|
if (!signingKey.isNullOrBlank()) {
|
|
useInMemoryPgpKeys(signingKey, signingPassword)
|
|
}
|
|
sign(publishing.publications)
|
|
}
|
|
|
|
apply(from = rootProject.file("gradle/central-portal.gradle.kts"))
|