This commit is contained in:
Him188 2022-05-28 23:03:13 +01:00
parent 92942c1037
commit 7088835402
9 changed files with 323 additions and 124 deletions

View File

@ -37,6 +37,7 @@ buildscript {
plugins {
kotlin("jvm") // version Versions.kotlinCompiler
kotlin("plugin.serialization") version Versions.kotlinCompiler
id("com.google.osdetector")
id("org.jetbrains.dokka") version Versions.dokka
id("me.him188.kotlin-jvm-blocking-bridge") version Versions.blockingBridge
id("me.him188.kotlin-dynamic-delegation") version Versions.dynamicDelegation
@ -44,6 +45,8 @@ plugins {
id("org.jetbrains.kotlinx.binary-compatibility-validator") version Versions.binaryValidator apply false
}
osDetector = osdetector
GpgSigner.setup(project)
analyzes.CompiledCodeVerify.run { registerAllVerifyTasks() }

View File

@ -58,5 +58,7 @@ dependencies {
api(asm("util"))
api(asm("commons"))
api("gradle.plugin.com.google.gradle:osdetector-gradle-plugin:1.7.0")
api(gradleApi())
}

View File

@ -7,25 +7,84 @@
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
import com.google.gradle.osdetector.OsDetector
import org.apache.tools.ant.taskdefs.condition.Os
import org.gradle.api.Project
import org.gradle.api.attributes.Attribute
import org.gradle.kotlin.dsl.get
import org.gradle.kotlin.dsl.getting
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.TEST_COMPILATION_NAME
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType
import java.io.File
private val miraiPlatform = Attribute.of(
"net.mamoe.mirai.platform",
String::class.java
"net.mamoe.mirai.platform", String::class.java
)
val IDEA_ACTIVE = System.getProperty("idea.active") == "true" && System.getProperty("publication.test") != "true"
val NATIVE_ENABLED = System.getProperty("mirai.enable.native", "true").toBoolean()
val ANDROID_ENABLED = System.getProperty("mirai.enable.android", "true").toBoolean()
val OS_NAME = System.getProperty("os.name").toLowerCase()
enum class HostKind {
LINUX, WINDOWS, MACOS,
}
val HOST_KIND = when {
OS_NAME.contains("windows", true) -> HostKind.WINDOWS
OS_NAME.contains("mac", true) -> HostKind.MACOS
else -> HostKind.LINUX
}
enum class HostArch {
X86, X64, ARM32, ARM64
}
lateinit var osDetector: OsDetector
val MAC_TARGETS: Set<String> by lazy {
if (!IDEA_ACTIVE) setOf(
// "watchosX86",
"macosX64",
"macosArm64",
// Failed to generate cinterop for :mirai-core:cinteropOpenSSLIosX64: Process 'command '/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home/bin/java'' finished with non-zero exit value 1
// Exception in thread "main" java.lang.Error: /var/folders/kw/ndw272ns06s7cys2mcwwlb5c0000gn/T/1181140105365175708.c:1:10: fatal error: 'openssl/ec.h' file not found
//
// Note: the openssl/ec.h is actually there, seems Kotlin doesn't support that
// "iosX64",
// "iosArm64",
// "iosArm32",
// "iosSimulatorArm64",
// "watchosX64",
// "watchosArm32",
// "watchosArm64",
// "watchosSimulatorArm64",
// "tvosX64",
// "tvosArm64",
// "tvosSimulatorArm64",
) else setOf(
// IDEA active, reduce load
if (osDetector.arch.contains("aarch")) "macosArm64" else "macosX64"
)
}
val WIN_TARGETS = setOf("mingwX64")
val LINUX_TARGETS = setOf("linuxX64")
val UNIX_LIKE_TARGETS by lazy { LINUX_TARGETS + MAC_TARGETS }
fun Project.configureHMPPJvm() {
extensions.getByType(KotlinMultiplatformExtension::class.java).apply {
@ -37,7 +96,7 @@ fun Project.configureHMPPJvm() {
// attributes.attribute(miraiPlatform, "jvmBase")
}
if (isAndroidSDKAvailable) {
if (isAndroidSDKAvailable && ANDROID_ENABLED) {
jvm("android") {
attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.androidJvm)
// publishAllLibraryVariants()
@ -49,85 +108,215 @@ fun Project.configureHMPPJvm() {
jvm("jvm") {
}
val ideaActive = System.getProperty("idea.active") == "true" && System.getProperty("publication.test") != "true"
val nativeMainSets = mutableListOf<KotlinSourceSet>()
val nativeTestSets = mutableListOf<KotlinSourceSet>()
val nativeTargets = mutableListOf<KotlinNativeTarget>()
if (ideaActive) {
val target = when {
Os.isFamily(Os.FAMILY_MAC) -> if (Os.isArch("aarch64")) macosArm64("native") else macosX64("native")
Os.isFamily(Os.FAMILY_WINDOWS) -> mingwX64("native")
else -> linuxX64("native")
}
nativeTargets.add(target)
} else {
// 1.6.0
val nativeTargetNames: List<String> = arrayOf(
// serialization doesn't support those commented targets
// "androidNativeArm32, androidNativeArm64, androidNativeX86, androidNativeX64",
"iosArm32, iosArm64, iosX64, iosSimulatorArm64",
"watchosArm32, watchosArm64, watchosX86, watchosX64, watchosSimulatorArm64",
"tvosArm64, tvosX64, tvosSimulatorArm64",
"macosX64, macosArm64",
"linuxMips32, linuxMipsel32, linuxX64",
"mingwX64",
// "wasm32" // linuxArm32Hfp, mingwX86
).flatMap { it.split(",") }.map { it.trim() }
presets.filter { it.name in nativeTargetNames }
.forEach { preset ->
val target = targetFromPreset(preset, preset.name) as KotlinNativeTarget
nativeMainSets.add(target.compilations[MAIN_COMPILATION_NAME].kotlinSourceSets.first())
nativeTestSets.add(target.compilations[TEST_COMPILATION_NAME].kotlinSourceSets.first())
nativeTargets.add(target)
}
if (!ideaActive) {
configure(nativeMainSets) {
dependsOn(sourceSets.maybeCreate("nativeMain"))
}
configure(nativeTestSets) {
dependsOn(sourceSets.maybeCreate("nativeTest"))
}
}
}
configureNativeInterop("main", projectDir.resolve("src/nativeMainInterop"), nativeTargets)
configureNativeInterop("test", projectDir.resolve("src/nativeTestInterop"), nativeTargets)
val sourceSets = kotlinSourceSets.orEmpty()
val commonMain = sourceSets.single { it.name == "commonMain" }
val commonTest = sourceSets.single { it.name == "commonTest" }
val jvmBaseMain = this.sourceSets.maybeCreate("jvmBaseMain")
val jvmBaseTest = this.sourceSets.maybeCreate("jvmBaseTest")
val jvmMain = sourceSets.single { it.name == "jvmMain" }
val jvmTest = sourceSets.single { it.name == "jvmTest" }
val androidMain = sourceSets.single { it.name == "androidMain" }
val androidTest = sourceSets.single { it.name == "androidTest" }
val nativeMain = sourceSets.single { it.name == "nativeMain" }
val nativeTest = sourceSets.single { it.name == "nativeTest" }
jvmBaseMain.dependsOn(commonMain)
jvmBaseTest.dependsOn(commonTest)
jvmMain.dependsOn(jvmBaseMain)
androidMain.dependsOn(jvmBaseMain)
jvmTest.dependsOn(jvmBaseTest)
androidTest.dependsOn(jvmBaseTest)
nativeMain.dependsOn(commonMain)
nativeTest.dependsOn(commonTest)
}
}
///**
// * [IDEA_ACTIVE] 时配置单一 'native' target, 基于 host 平台; 否则配置所有 native targets 依赖 'native' 作为中间平台.
// */
//@Deprecated("")
//fun KotlinMultiplatformExtension.configureNativeTargets(
// project: Project
//) {
// val nativeMainSets = mutableListOf<KotlinSourceSet>()
// val nativeTestSets = mutableListOf<KotlinSourceSet>()
// val nativeTargets = mutableListOf<KotlinNativeTarget>()
//
// if (IDEA_ACTIVE) {
// val target = when {
// Os.isFamily(Os.FAMILY_MAC) -> if (Os.isArch("aarch64")) macosArm64("native") else macosX64(
// "native"
// )
// Os.isFamily(Os.FAMILY_WINDOWS) -> mingwX64("native")
// else -> linuxX64("native")
// }
// nativeTargets.add(target)
// } else {
// // 1.6.0
// val nativeTargetNames: List<String> = arrayOf(
// // serialization doesn't support those commented targets
//// "androidNativeArm32, androidNativeArm64, androidNativeX86, androidNativeX64",
// "iosArm32, iosArm64, iosX64, iosSimulatorArm64",
// "watchosArm32, watchosArm64, watchosX86, watchosX64, watchosSimulatorArm64",
// "tvosArm64, tvosX64, tvosSimulatorArm64",
// "macosX64, macosArm64",
//// "linuxMips32, linuxMipsel32, linuxX64",
// "linuxX64",
// "mingwX64",
//// "wasm32" // linuxArm32Hfp, mingwX86
// ).flatMap { it.split(",") }.map { it.trim() }
// presets.filter { it.name in nativeTargetNames }.forEach { preset ->
// val target = targetFromPreset(preset, preset.name) as KotlinNativeTarget
// nativeMainSets.add(target.compilations[MAIN_COMPILATION_NAME].kotlinSourceSets.first())
// nativeTestSets.add(target.compilations[TEST_COMPILATION_NAME].kotlinSourceSets.first())
// nativeTargets.add(target)
// }
//
// if (!IDEA_ACTIVE) {
// project.configure(nativeMainSets) {
// dependsOn(sourceSets.maybeCreate("nativeMain"))
// }
//
// project.configure(nativeTestSets) {
// dependsOn(sourceSets.maybeCreate("nativeTest"))
// }
// }
// }
//
// project.configureNativeInterop("main", project.projectDir.resolve("src/nativeMainInterop"), nativeTargets)
// project.configureNativeInterop("test", project.projectDir.resolve("src/nativeTestInterop"), nativeTargets)
// project.configureNativeLinkOptions(nativeTargets)
//
// val sourceSets = project.kotlinSourceSets.orEmpty()
// val commonMain = sourceSets.single { it.name == "commonMain" }
// val commonTest = sourceSets.single { it.name == "commonTest" }
// val jvmBaseMain = this.sourceSets.maybeCreate("jvmBaseMain")
// val jvmBaseTest = this.sourceSets.maybeCreate("jvmBaseTest")
// val jvmMain = sourceSets.single { it.name == "jvmMain" }
// val jvmTest = sourceSets.single { it.name == "jvmTest" }
// val androidMain = sourceSets.single { it.name == "androidMain" }
// val androidTest = sourceSets.single { it.name == "androidTest" }
//
// val nativeMain = sourceSets.single { it.name == "nativeMain" }
// val nativeTest = sourceSets.single { it.name == "nativeTest" }
//
//
// jvmBaseMain.dependsOn(commonMain)
// jvmBaseTest.dependsOn(commonTest)
//
// jvmMain.dependsOn(jvmBaseMain)
// androidMain.dependsOn(jvmBaseMain)
//
// jvmTest.dependsOn(jvmBaseTest)
// androidTest.dependsOn(jvmBaseTest)
//
// nativeMain.dependsOn(commonMain)
// nativeTest.dependsOn(commonTest)
//}
/**
* ```
* common
* |
* /---------------+---------------\
* jvmBase native
* / \ / \
* jvm android unix \
* / \ mingwX64
* / \
* darwin linuxX64
* |
* *
* <darwin targets>
* ```
*
* `<darwin targets>`: macosX64, macosArm64, tvosX64, iosArm64, iosArm32...
*/
fun KotlinMultiplatformExtension.configureNativeTargetsHierarchical(
project: Project
) {
val nativeMainSets = mutableListOf<KotlinSourceSet>()
val nativeTestSets = mutableListOf<KotlinSourceSet>()
val nativeTargets = mutableListOf<KotlinNativeTarget>()
fun KotlinMultiplatformExtension.addNativeTarget(
preset: KotlinTargetPreset<*>,
): KotlinNativeTarget {
val target = targetFromPreset(preset, preset.name) as KotlinNativeTarget
nativeMainSets.add(target.compilations[MAIN_COMPILATION_NAME].kotlinSourceSets.first())
nativeTestSets.add(target.compilations[TEST_COMPILATION_NAME].kotlinSourceSets.first())
nativeTargets.add(target)
return target
}
// project.configureNativeInterop("main", project.projectDir.resolve("src/nativeMainInterop"), nativeTargets)
// project.configureNativeInterop("test", project.projectDir.resolve("src/nativeTestInterop"), nativeTargets)
val sourceSets = project.objects.domainObjectContainer(KotlinSourceSet::class.java)
.apply { addAll(project.kotlinSourceSets.orEmpty()) }
val commonMain by sourceSets.getting
val commonTest by sourceSets.getting
val jvmBaseMain = this.sourceSets.maybeCreate("jvmBaseMain")
val jvmBaseTest = this.sourceSets.maybeCreate("jvmBaseTest")
val jvmMain by sourceSets.getting
val jvmTest by sourceSets.getting
val androidMain by sourceSets.getting
val androidTest by sourceSets.getting
val nativeMain = this.sourceSets.maybeCreate("nativeMain")
val nativeTest = this.sourceSets.maybeCreate("nativeTest")
val unixMain = this.sourceSets.maybeCreate("unixMain")
val unixTest = this.sourceSets.maybeCreate("unixTest")
val darwinMain = this.sourceSets.maybeCreate("darwinMain")
val darwinTest = this.sourceSets.maybeCreate("darwinTest")
presets.filter { it.name in MAC_TARGETS }.forEach { preset ->
addNativeTarget(preset).run {
compilations[MAIN_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(darwinMain) }
compilations[TEST_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(darwinTest) }
}
}
presets.filter { it.name in LINUX_TARGETS }.forEach { preset ->
addNativeTarget(preset).run {
compilations[MAIN_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(unixMain) }
compilations[TEST_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(unixTest) }
}
}
presets.filter { it.name in WIN_TARGETS }.forEach { preset ->
addNativeTarget(preset).run {
compilations[MAIN_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(nativeMain) }
compilations[TEST_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(nativeTest) }
}
}
jvmBaseMain.dependsOn(commonMain)
jvmBaseTest.dependsOn(commonTest)
nativeMain.dependsOn(commonMain)
nativeTest.dependsOn(commonTest)
unixMain.dependsOn(nativeMain)
unixTest.dependsOn(nativeTest)
darwinMain.dependsOn(unixMain)
darwinTest.dependsOn(unixTest)
jvmMain.dependsOn(jvmBaseMain)
jvmTest.dependsOn(jvmBaseTest)
androidMain.dependsOn(jvmBaseMain)
androidTest.dependsOn(jvmBaseTest)
}
// e.g. Linker will try to link curl for mingwX64 but this can't be done on macOS.
fun Project.disableCrossCompile() {
project.afterEvaluate {
if (HOST_KIND != HostKind.MACOS) {
MAC_TARGETS.forEach { target -> disableTargetLink(this, target) }
}
if (HOST_KIND != HostKind.WINDOWS) {
WIN_TARGETS.forEach { target -> disableTargetLink(this, target) }
}
if (HOST_KIND != HostKind.LINUX) {
LINUX_TARGETS.forEach { target -> disableTargetLink(this, target) }
}
}
}
private fun disableTargetLink(project: Project, target: String) {
project.tasks.getByName("linkDebugTest${target.titlecase()}").enabled = false
project.tasks.getByName("${target}Test").enabled = false
}
private fun Project.linkerDirs(): List<String> {
return listOf(
":mirai-core",
@ -149,23 +338,10 @@ private fun Project.includeDirs(): List<String> {
}
private fun Project.configureNativeInterop(
compilationName: String,
nativeInteropDir: File,
nativeTargets: MutableList<KotlinNativeTarget>
compilationName: String, nativeInteropDir: File, nativeTargets: MutableList<KotlinNativeTarget>
) {
val crateName = project.name.replace("-", "_") + "_i"
configure(nativeTargets) {
binaries {
for (buildType in NativeBuildType.values()) {
findTest(buildType)?.apply {
linkerOpts("-v")
linkerOpts(*linkerDirs().map { "-L$it" }.toTypedArray())
linkerOpts("-undefined", "dynamic_lookup") // resolve symbol in runtime
}
}
}
}
if (nativeInteropDir.exists() && nativeInteropDir.isDirectory && nativeInteropDir.resolve("build.rs").exists()) {
val kotlinDylibName = project.name.replace("-", "_") + "_i"
val kotlinDylibName = project.name.replace("-", "_")
@ -202,19 +378,14 @@ private fun Project.configureNativeInterop(
val cbindgen = tasks.register("cbindgen${compilationName.titlecase()}") {
group = "mirai"
description = "Generate C Headers from Rust"
inputs.files(
project.objects.fileTree().from(nativeInteropDir.resolve("src"))
.filterNot { it.name == "bindings.rs" }
)
inputs.files(project.objects.fileTree().from(nativeInteropDir.resolve("src"))
.filterNot { it.name == "bindings.rs" })
outputs.file(nativeInteropDir.resolve(headerName))
doLast {
exec {
workingDir(nativeInteropDir)
commandLine(
"cbindgen",
"--config", "cbindgen.toml",
"--crate", crateName,
"--output", headerName
"cbindgen", "--config", "cbindgen.toml", "--crate", crateName, "--output", headerName
)
}
}
@ -267,30 +438,45 @@ private fun Project.configureNativeInterop(
// targetCompilation!!.compileKotlinTask.dependsOn(cbindgen)
// tasks.getByName("cinteropNative$name").dependsOn(cbindgen)
}
targetCompilation!!
val compileRust = tasks.register("compileRust${compilationName.titlecase()}") {
group = "mirai"
inputs.files(nativeInteropDir.resolve("src"))
outputs.file(rustLibDir.resolve("lib$crateName.dylib"))
if (targetCompilation != null) {
val compileRust = tasks.register("compileRust${compilationName.titlecase()}") {
group = "mirai"
inputs.files(nativeInteropDir.resolve("src"))
outputs.file(rustLibDir.resolve("lib$crateName.dylib"))
// dependsOn(targetCompilation!!.compileKotlinTask)
dependsOn(bindgen)
dependsOn(tasks.findByName("linkDebugSharedNative")) // dylib to link
doLast {
exec {
workingDir(nativeInteropDir)
commandLine(
"cargo",
"build",
"--color", "always",
"--all",
dependsOn(bindgen)
dependsOn(tasks.findByName("linkDebugSharedNative")) // dylib to link
doLast {
exec {
workingDir(nativeInteropDir)
commandLine(
"cargo",
"build",
"--color", "always",
"--all",
// "--", "--color", "always", "2>&1"
)
)
}
}
}
tasks.getByName("assemble").dependsOn(compileRust)
}
}
}
private fun Project.configureNativeLinkOptions(nativeTargets: MutableList<KotlinNativeTarget>) {
configure(nativeTargets) {
binaries {
for (buildType in NativeBuildType.values()) {
findTest(buildType)?.apply {
linkerOpts("-v")
linkerOpts(*linkerDirs().map { "-L$it" }.toTypedArray())
linkerOpts("-undefined", "dynamic_lookup") // resolve symbol in runtime
}
}
}
tasks.getByName("assemble").dependsOn(compileRust)
}
}

View File

@ -24,11 +24,11 @@ fun Project.configureMppPublishing() {
// mirai does some magic on MPP targets
afterEvaluate {
tasks.findByName("compileKotlinCommon")?.enabled = false
tasks.findByName("compileTestKotlinCommon")?.enabled = false
// tasks.findByName("compileKotlinCommon")?.enabled = false
// tasks.findByName("compileTestKotlinCommon")?.enabled = false
tasks.findByName("compileCommonMainKotlinMetadata")?.enabled = false
tasks.findByName("compileKotlinMetadata")?.enabled = false
// tasks.findByName("compileCommonMainKotlinMetadata")?.enabled = false
// tasks.findByName("compileKotlinMetadata")?.enabled = false
// TODO: 2021/1/30 如果添加 JVM 到 root module, 这个 task 会失败因 root module artifacts 有变化
// tasks.findByName("generateMetadataFileForKotlinMultiplatformPublication")?.enabled = false // FIXME: 2021/1/21

View File

@ -28,7 +28,7 @@ object Versions {
const val kotlinCompilerForIdeaPlugin = "1.7.0-RC"
const val coroutines = "1.6.1"
const val coroutines = "1.6.2"
const val atomicFU = "0.17.2"
const val serialization = "1.3.2"
const val ktor = "1.6.7"
@ -108,6 +108,9 @@ val `ktor-serialization` = ktor("serialization", Versions.ktor)
val `ktor-client-core` = ktor("client-core", Versions.ktor)
val `ktor-client-cio` = ktor("client-cio", Versions.ktor)
val `ktor-client-mock` = ktor("client-mock", Versions.ktor)
val `ktor-client-curl` = ktor("client-curl", Versions.ktor)
val `ktor-client-ios` = ktor("client-ios", Versions.ktor)
val `ktor-client-okhttp` = ktor("client-okhttp", Versions.ktor)
val `ktor-client-android` = ktor("client-android", Versions.ktor)
val `ktor-client-logging` = ktor("client-logging", Versions.ktor)

View File

@ -21,6 +21,7 @@ kotlin {
explicitApi()
configureHMPP()
configureNativeTargetsHierarchical(project)
}
configureMppPublishing()

View File

@ -28,6 +28,8 @@ kotlin {
explicitApi()
configureHMPP()
configureNativeTargetsHierarchical(project)
sourceSets {
val commonMain by getting {

View File

@ -24,6 +24,7 @@ kotlin {
explicitApi()
configureHMPP()
configureNativeTargetsHierarchical(project)
sourceSets {
val commonMain by getting {

View File

@ -26,6 +26,7 @@ kotlin {
explicitApi()
configureHMPP()
configureNativeTargetsHierarchical(project)
sourceSets.apply {