From 4fe56409ec4ff87aa76f78f881ab89f7d9f8cea4 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Wed, 1 Jun 2022 01:34:01 +0100
Subject: [PATCH] HMPP

---
 build.gradle.kts                          |  11 +-
 buildSrc/build.gradle.kts                 |   2 +
 buildSrc/src/main/kotlin/HmppConfigure.kt | 164 +++++++++++++---------
 buildSrc/src/main/kotlin/Versions.kt      |   3 +-
 4 files changed, 108 insertions(+), 72 deletions(-)

diff --git a/build.gradle.kts b/build.gradle.kts
index afdfbeb88..6746529a4 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,10 +1,10 @@
 /*
- * Copyright 2019-2021 Mamoe Technologies and contributors.
+ * Copyright 2019-2022 Mamoe Technologies and contributors.
  *
- *  此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
- *  Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
  *
- *  https://github.com/mamoe/mirai/blob/master/LICENSE
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
  */
 
 @file:Suppress("UnstableApiUsage", "UNUSED_VARIABLE", "NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
@@ -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() }
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index 8445d4d16..6fbc735dc 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -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())
 }
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/HmppConfigure.kt b/buildSrc/src/main/kotlin/HmppConfigure.kt
index b7c5be436..f209393c6 100644
--- a/buildSrc/src/main/kotlin/HmppConfigure.kt
+++ b/buildSrc/src/main/kotlin/HmppConfigure.kt
@@ -7,6 +7,7 @@
  * 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
@@ -24,8 +25,7 @@ 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"
@@ -36,40 +36,50 @@ val ANDROID_ENABLED = System.getProperty("mirai.enable.android", "true").toBoole
 val OS_NAME = System.getProperty("os.name").toLowerCase()
 
 enum class HostKind {
-    LINUX,
-    WINDOWS,
-    MACOS,
+    LINUX, WINDOWS, MACOS,
 }
 
 val HOST_KIND = when {
-    OS_NAME.contains("linux", true) -> HostKind.LINUX
     OS_NAME.contains("windows", true) -> HostKind.WINDOWS
     OS_NAME.contains("mac", true) -> HostKind.MACOS
-    else -> error("Unsupported host kind `$OS_NAME`")
+    else -> HostKind.LINUX
 }
 
-val MAC_TARGETS = setOf(
-    "macosX64",
-    "macosArm64",
-    "iosX64",
-    "iosArm64",
-    "iosArm32",
-    "iosSimulatorArm64",
-    "watchosX86",
-    "watchosX64",
-    "watchosArm32",
-    "watchosArm64",
-    "watchosSimulatorArm64",
-    "tvosX64",
-    "tvosArm64",
-    "tvosSimulatorArm64",
-)
+enum class HostArch {
+    X86, X64, ARM32, ARM64
+}
+
+lateinit var osDetector: OsDetector
+
+val MAC_TARGETS: Set<String> by lazy {
+    if (!IDEA_ACTIVE) setOf(
+        "watchosX86", // enable a x86 target to force handling number of unstable sizes.
+        // enable only on CI
+        "macosX64",
+        "macosArm64",
+
+        "iosX64",
+        "iosArm64",
+        "iosArm32",
+        "iosSimulatorArm64",
+        "watchosX64",
+        "watchosArm32",
+        "watchosArm64",
+        "watchosSimulatorArm64",
+        "tvosX64",
+        "tvosArm64",
+        "tvosSimulatorArm64",
+    ) else setOf(
+        // IDEA active
+        if (osDetector.arch.contains("aarch")) "macosArm64" else "macosX64"
+    )
+}
 
 val WIN_TARGETS = setOf("mingwX64")
 
 val LINUX_TARGETS = setOf("linuxX64")
 
-val UNIX_LIKE_TARGETS = LINUX_TARGETS + MAC_TARGETS
+val UNIX_LIKE_TARGETS by lazy { LINUX_TARGETS + MAC_TARGETS }
 
 
 fun Project.configureHMPP() {
@@ -131,13 +141,12 @@ fun KotlinMultiplatformExtension.configureNativeTargets(
             "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)
-            }
+        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) {
@@ -188,14 +197,16 @@ fun KotlinMultiplatformExtension.configureNativeTargets(
  *        /---------------+---------------\
  *     jvmBase                          native
  *     /    \                          /     \
- *   jvm   android                  unix     mingw
- *                                   |         \
- *                                   |        mingwX64
- *                                   |
- *                             <unix targets>
+ *   jvm   android                  unix      \
+ *                                 /    \     mingwX64
+ *                                /      \
+ *                            darwin     linuxX64
+ *                               |
+ *                               *
+ *                        <darwin targets>
  * ```
  *
- * `<unix targets>` include: macosX64, macosArm64, tvosX64, iosArm64, iosArm32, linuxX64...
+ * `<darwin targets>`: macosX64, macosArm64, tvosX64, iosArm64, iosArm32...
  */
 fun KotlinMultiplatformExtension.configureNativeTargetsHierarchical(
     project: Project
@@ -235,27 +246,32 @@ fun KotlinMultiplatformExtension.configureNativeTargetsHierarchical(
     val nativeMain = this.sourceSets.maybeCreate("nativeMain")
     val nativeTest = this.sourceSets.maybeCreate("nativeTest")
 
-    val mingwMain = this.sourceSets.maybeCreate("mingwMain")
-    val mingwTest = this.sourceSets.maybeCreate("mingwTest")
-
     val unixMain = this.sourceSets.maybeCreate("unixMain")
     val unixTest = this.sourceSets.maybeCreate("unixTest")
 
-    presets.filter { it.name in UNIX_LIKE_TARGETS }
-        .forEach { preset ->
-            addNativeTarget(preset).run {
-                compilations[MAIN_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(unixMain) }
-                compilations[TEST_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(unixTest) }
-            }
-        }
+    val darwinMain = this.sourceSets.maybeCreate("darwinMain")
+    val darwinTest = this.sourceSets.maybeCreate("darwinTest")
 
-    presets.filter { it.name in WIN_TARGETS }
-        .forEach { preset ->
-            addNativeTarget(preset).run {
-                compilations[MAIN_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(mingwMain) }
-                compilations[TEST_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(mingwTest) }
-            }
+    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)
@@ -266,8 +282,8 @@ fun KotlinMultiplatformExtension.configureNativeTargetsHierarchical(
     unixMain.dependsOn(nativeMain)
     unixTest.dependsOn(nativeTest)
 
-    mingwMain.dependsOn(nativeMain)
-    mingwTest.dependsOn(nativeTest)
+    darwinMain.dependsOn(unixMain)
+    darwinTest.dependsOn(unixTest)
 
     jvmMain.dependsOn(jvmBaseMain)
     jvmTest.dependsOn(jvmBaseTest)
@@ -276,6 +292,27 @@ fun KotlinMultiplatformExtension.configureNativeTargetsHierarchical(
     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",
@@ -297,9 +334,7 @@ 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"
 
@@ -342,19 +377,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
                     )
                 }
             }
@@ -408,7 +438,7 @@ private fun Project.configureNativeInterop(
 //            tasks.getByName("cinteropNative$name").dependsOn(cbindgen)
         }
 
-        if (targetCompilation != null)  {
+        if (targetCompilation != null) {
             val compileRust = tasks.register("compileRust${compilationName.titlecase()}") {
                 group = "mirai"
                 inputs.files(nativeInteropDir.resolve("src"))
diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt
index 4e56c89eb..fb5e722c1 100644
--- a/buildSrc/src/main/kotlin/Versions.kt
+++ b/buildSrc/src/main/kotlin/Versions.kt
@@ -28,7 +28,7 @@ object Versions {
 
     const val kotlinCompilerForIdeaPlugin = "1.7.0-RC"
 
-    const val coroutines = "1.6.1-native-mt"
+    const val coroutines = "1.6.2"
     const val atomicFU = "0.17.2"
     const val serialization = "1.3.2"
     const val ktor = "1.6.8"
@@ -108,6 +108,7 @@ 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)