From 7325c1f7e2dd68d9f14904f952669d75d6b7929e Mon Sep 17 00:00:00 2001 From: AdoptOSS Date: Sat, 25 Jun 2022 10:40:29 +0800 Subject: [PATCH] =?UTF-8?q?native:=20=E4=BC=98=E5=8C=96=20build=20script?= =?UTF-8?q?=20=E5=8F=8A=20actions=EF=BC=8C=E4=BF=AE=E5=A4=8D=20ECDH=20?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E8=B5=84=E6=BA=90=E9=87=8A=E6=94=BE=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20(#2110)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * build: fix build on Windows * test: fix ContentEqualsTest on native * build: allow disabling targets with property * fix: free ECDH-related resources properly on native avoid memory leaking (usually on *nix) or crash (usually on Windows) Signed-off-by: AdoptOSS * build(workflow): remove invalid options * fix(styling): `mirai.target` property * build(workflow): try to limit memory usage during mirai-console:tools:gradle-plugin:integTest * enhance(test): use buildList * build(workflow): retry Co-authored-by: ArcticLampyrid --- .github/workflows/build.yml | 37 +++-- .../kotlin/BinaryCompatibilityConfigurator.kt | 2 +- buildSrc/src/main/kotlin/HmppConfigure.kt | 146 +++++++++++------- .../src/integTest/kotlin/AbstractTest.kt | 13 +- .../src/integTest/kotlin/TestBuildPlugin.kt | 36 ++--- .../src/integTest/kotlin/TestPluginApply.kt | 4 +- mirai-core-api/build.gradle.kts | 22 ++- .../kotlin/message/data/FileMessage.kt | 2 - mirai-core-utils/build.gradle.kts | 22 +-- mirai-core/build.gradle.kts | 42 +++-- .../kotlin/message/data/ContentEqualsTest.kt | 3 +- .../nativeMain/kotlin/utils/crypto/ECDH.kt | 38 +++-- 12 files changed, 201 insertions(+), 166 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 519c4d5bd..cd383b4d1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: - windows-2022 - macos-12 env: - gradleArgs: --scan "-Pkotlin.compiler.execution.strategy=in-process" "-Dorg.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 --illegal-access=permit -Dkotlin.daemon.jvm.options=--illegal-access=permit --add-opens java.base/java.util=ALL-UNNAMED" + gradleArgs: --scan "-Dmirai.target=jvm;android;!other" "-Pkotlin.compiler.execution.strategy=in-process" "-Dorg.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8" isMac: ${{ startsWith(matrix.os, 'macos') }} isWindows: ${{ startsWith(matrix.os, 'windows') }} isUbuntu: ${{ startsWith(matrix.os, 'ubuntu') }} @@ -34,6 +34,13 @@ jobs: - if: ${{ env.isUnix == 'true' }} run: chmod -R 777 * + - if: ${{ env.isWindows == 'true' }} + name: Setup Memory Environment on Windows + run: > + wmic pagefileset where name="D:\\pagefile.sys" set InitialSize=1024,MaximumSize=9216 & + net stop mongodb + shell: cmd + continue-on-error: true - name: Clean and download dependencies run: ./gradlew clean ${{ env.gradleArgs }} @@ -295,22 +302,24 @@ jobs: - macos-11 include: - os: windows-2022 - testTaskName: mingwX64Test + targetName: mingwX64 - os: ubuntu-20.04 - testTaskName: linuxX64Test + targetName: linuxX64 - os: ubuntu-18.04 - testTaskName: linuxX64Test + targetName: linuxX64 - os: macos-12 - testTaskName: macosX64Test + targetName: macosX64 - os: macos-11 - testTaskName: macosX64Test + targetName: macosX64 env: - gradleArgs: --scan "-Pkotlin.compiler.execution.strategy=in-process" "-Dorg.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 --illegal-access=permit -Dkotlin.daemon.jvm.options=--illegal-access=permit --add-opens java.base/java.util=ALL-UNNAMED" + # FIXME there must be two or more targets, or we'll get error on `@OptionalExpectation` + # > Declaration annotated with '@OptionalExpectation' can only be used in common module sources + gradleArgs: --scan "-Dmirai.target=jvm;${{ matrix.targetName }};!other" "-Pkotlin.compiler.execution.strategy=in-process" "-Dorg.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8" isMac: ${{ startsWith(matrix.os, 'macos') }} isWindows: ${{ startsWith(matrix.os, 'windows') }} isUbuntu: ${{ startsWith(matrix.os, 'ubuntu') }} isUnix: ${{ startsWith(matrix.os, 'macos') || startsWith(matrix.os, 'ubuntu') }} - VCPKG_DEFAULT_BINARY_CACHE: ${{ startsWith(matrix.os, 'windows') && 'C:\\vcpkg\\binary_cache' || '/usr/local/share/vcpkg/binary_cache' }} + VCPKG_DEFAULT_BINARY_CACHE: ${{ startsWith(matrix.os, 'windows') && 'C:\vcpkg\binary_cache' || '/usr/local/share/vcpkg/binary_cache' }} steps: - uses: actions/checkout@v2 with: @@ -373,12 +382,6 @@ jobs: shell: cmd continue-on-error: true - - if: ${{ env.isWindows == 'true' }} - name: Setup C++ Toolchain Environment on Windows - run: | - 'echo "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.32.31326\bin\Hostx64\x64" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append' - 'echo "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\Llvm\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append' - - if: ${{ env.isWindows == 'true' }} name: Install OpenSSL & cURL on Windows run: | @@ -393,10 +396,10 @@ jobs: run: ./gradlew clean ${{ env.gradleArgs }} - name: "Test mirai-core-utils for ${{ matrix.os }}" - run: ./gradlew :mirai-core-utils:${{ matrix.testTaskName }} ${{ env.gradleArgs }} + run: ./gradlew :mirai-core-utils:${{ matrix.targetName }}Test ${{ env.gradleArgs }} - name: "Test mirai-core-api for ${{ matrix.os }}" - run: ./gradlew :mirai-core-api:${{ matrix.testTaskName }} ${{ env.gradleArgs }} + run: ./gradlew :mirai-core-api:${{ matrix.targetName }}Test ${{ env.gradleArgs }} - name: "Test mirai-core for ${{ matrix.os }}" - run: ./gradlew :mirai-core:${{ matrix.testTaskName }} ${{ env.gradleArgs }} \ No newline at end of file + run: ./gradlew :mirai-core:${{ matrix.targetName }}Test ${{ env.gradleArgs }} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/BinaryCompatibilityConfigurator.kt b/buildSrc/src/main/kotlin/BinaryCompatibilityConfigurator.kt index f9de4aa97..5031a54c1 100644 --- a/buildSrc/src/main/kotlin/BinaryCompatibilityConfigurator.kt +++ b/buildSrc/src/main/kotlin/BinaryCompatibilityConfigurator.kt @@ -21,7 +21,7 @@ import java.io.File */ object BinaryCompatibilityConfigurator { - fun Project.configureBinaryValidators(vararg targetNames: String) { + fun Project.configureBinaryValidators(targetNames: Set) { targetNames.forEach { configureBinaryValidator(it) } } diff --git a/buildSrc/src/main/kotlin/HmppConfigure.kt b/buildSrc/src/main/kotlin/HmppConfigure.kt index c9a363bbb..104b7bda4 100644 --- a/buildSrc/src/main/kotlin/HmppConfigure.kt +++ b/buildSrc/src/main/kotlin/HmppConfigure.kt @@ -30,10 +30,6 @@ private val miraiPlatform = Attribute.of( ) val IDEA_ACTIVE = System.getProperty("idea.active") == "true" && System.getProperty("publication.test") != "true" -val WINDOWS_TARGET_ENABLED = System.getProperty("mirai.windows.target") != "false" - -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() @@ -50,7 +46,7 @@ sealed class HostKind( val targetName: String ) { object LINUX : HostKind("linuxX64") - object WINDOWS : HostKind("windowsX64") + object WINDOWS : HostKind("mingwX64") abstract class MACOS(targetName: String) : HostKind(targetName) @@ -68,6 +64,7 @@ val HOST_KIND by lazy { HostKind.MACOS_X64 } } + else -> HostKind.LINUX } } @@ -76,8 +73,31 @@ enum class HostArch { X86, X64, ARM32, ARM64 } +/// eg. "!a;!b" means to enable all targets but a or b +/// eg. "a;b;!other" means to disable all targets but a or b +val ENABLED_TARGETS by lazy { + System.getProperty( + "mirai.target", + if (IDEA_ACTIVE) + "jvm;android;${HOST_KIND.targetName};!other" + else + "" + ).split(';').toSet() +} + +fun isTargetEnabled(name: String): Boolean { + return when { + name in ENABLED_TARGETS -> true + "!$name" in ENABLED_TARGETS -> false + else -> "!other" !in ENABLED_TARGETS + } +} + +fun Set.filterTargets() = + this.filter { isTargetEnabled(it) }.toSet() + val MAC_TARGETS: Set by lazy { - if (!IDEA_ACTIVE) setOf( + setOf( // "watchosX86", "macosX64", "macosArm64", @@ -98,23 +118,22 @@ val MAC_TARGETS: Set by lazy { // "tvosX64", // "tvosArm64", // "tvosSimulatorArm64", - ) else setOf( - // IDEA active, reduce load - HOST_KIND.targetName - ) + ).filterTargets() } -val WIN_TARGETS = if (WINDOWS_TARGET_ENABLED) setOf("mingwX64") else emptySet() +val WIN_TARGETS by lazy { setOf("mingwX64").filterTargets() } -val LINUX_TARGETS = setOf("linuxX64") +val LINUX_TARGETS by lazy { setOf("linuxX64").filterTargets() } val UNIX_LIKE_TARGETS by lazy { LINUX_TARGETS + MAC_TARGETS } val NATIVE_TARGETS by lazy { UNIX_LIKE_TARGETS + WIN_TARGETS } - fun Project.configureJvmTargetsHierarchical() { extensions.getByType(KotlinMultiplatformExtension::class.java).apply { + val commonMain by sourceSets.getting + val commonTest by sourceSets.getting + if (IDEA_ACTIVE) { jvm("jvmBase") { // dummy target for resolution, not published compilations.all { @@ -125,19 +144,42 @@ fun Project.configureJvmTargetsHierarchical() { } } - if (isAndroidSDKAvailable && ANDROID_ENABLED) { - jvm("android") { - attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.androidJvm) - if (IDEA_ACTIVE) { - attributes.attribute(miraiPlatform, "android") // avoid resolution - } + val jvmBaseMain by lazy { + sourceSets.maybeCreate("jvmBaseMain").apply { + dependsOn(commonMain) + } + } + val jvmBaseTest by lazy { + sourceSets.maybeCreate("jvmBaseTest").apply { + dependsOn(commonTest) } - } else { - printAndroidNotInstalled() } - jvm("jvm") { + if (isTargetEnabled("android")) { + if (isAndroidSDKAvailable) { + jvm("android") { + attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.androidJvm) + if (IDEA_ACTIVE) { + attributes.attribute(miraiPlatform, "android") // avoid resolution + } + } + val androidMain by sourceSets.getting + val androidTest by sourceSets.getting + androidMain.dependsOn(jvmBaseMain) + androidTest.dependsOn(jvmBaseTest) + } else { + printAndroidNotInstalled() + } + } + if (isTargetEnabled("jvm")) { + jvm("jvm") { + + } + val jvmMain by sourceSets.getting + val jvmTest by sourceSets.getting + jvmMain.dependsOn(jvmBaseMain) + jvmTest.dependsOn(jvmBaseTest) } } } @@ -188,21 +230,39 @@ fun KotlinMultiplatformExtension.configureNativeTargetsHierarchical( 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 nativeMain by lazy { + this.sourceSets.maybeCreate("nativeMain").apply { + dependsOn(commonMain) + } + } + val nativeTest by lazy { + this.sourceSets.maybeCreate("nativeTest").apply { + dependsOn(commonTest) + } + } - val unixMain = this.sourceSets.maybeCreate("unixMain") - val unixTest = this.sourceSets.maybeCreate("unixTest") + val unixMain by lazy { + this.sourceSets.maybeCreate("unixMain").apply { + dependsOn(nativeMain) + } + } + val unixTest by lazy { + this.sourceSets.maybeCreate("unixTest").apply { + dependsOn(nativeTest) + } + } - val darwinMain = this.sourceSets.maybeCreate("darwinMain") - val darwinTest = this.sourceSets.maybeCreate("darwinTest") + val darwinMain by lazy { + this.sourceSets.maybeCreate("darwinMain").apply { + dependsOn(unixMain) + } + } + val darwinTest by lazy { + this.sourceSets.maybeCreate("darwinTest").apply { + dependsOn(unixTest) + } + } presets.filter { it.name in MAC_TARGETS }.forEach { preset -> addNativeTarget(preset).run { @@ -293,24 +353,6 @@ fun KotlinMultiplatformExtension.configureNativeTargetsHierarchical( } } } - - 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) } private fun KotlinNativeTarget.findOrCreateTest(buildType: NativeBuildType, configure: TestExecutable.() -> Unit) = diff --git a/mirai-console/tools/gradle-plugin/src/integTest/kotlin/AbstractTest.kt b/mirai-console/tools/gradle-plugin/src/integTest/kotlin/AbstractTest.kt index 3ebece2db..c242e7307 100644 --- a/mirai-console/tools/gradle-plugin/src/integTest/kotlin/AbstractTest.kt +++ b/mirai-console/tools/gradle-plugin/src/integTest/kotlin/AbstractTest.kt @@ -29,14 +29,21 @@ abstract class AbstractTest { lateinit var propertiesFile: File - fun gradleRunner(): GradleRunner { - println(PluginUnderTestMetadataReading.readImplementationClasspath()) - return GradleRunner.create() + @OptIn(ExperimentalStdlibApi::class) + fun runGradle(vararg arguments: String) { + System.gc() + GradleRunner.create() .withProjectDir(tempDir) .withPluginClasspath() .withGradleVersion("7.2") .forwardOutput() .withEnvironment(System.getenv()) + .withArguments(buildList { + addAll(arguments) + add("-Pkotlin.compiler.execution.strategy=in-process") + add("-Dorg.gradle.jvmargs=-Xmx256m -Dfile.encoding=UTF-8") + }) + .build() } @BeforeEach diff --git a/mirai-console/tools/gradle-plugin/src/integTest/kotlin/TestBuildPlugin.kt b/mirai-console/tools/gradle-plugin/src/integTest/kotlin/TestBuildPlugin.kt index c2a5d3e56..5571af6e5 100644 --- a/mirai-console/tools/gradle-plugin/src/integTest/kotlin/TestBuildPlugin.kt +++ b/mirai-console/tools/gradle-plugin/src/integTest/kotlin/TestBuildPlugin.kt @@ -88,9 +88,7 @@ class TestBuildPlugin : AbstractTest() { """.trimIndent() ) - gradleRunner() - .withArguments(":buildPlugin", "--stacktrace", "--info") - .build() + runGradle(":buildPlugin", "--stacktrace", "--info") ZipFile(findJar()).use { zipFile -> @@ -174,9 +172,7 @@ class TestBuildPlugin : AbstractTest() { """.trimIndent() ) - gradleRunner() - .withArguments(":buildPlugin", "--stacktrace", "--info") - .build() + runGradle(":buildPlugin", "--stacktrace", "--info") ZipFile(findJar()).use { zipFile -> @@ -242,9 +238,7 @@ class TestBuildPlugin : AbstractTest() { """.trimIndent() ) - gradleRunner() - .withArguments(":buildPlugin", "--stacktrace", "--info") - .build() + runGradle(":buildPlugin", "--stacktrace", "--info") ZipFile(findJar()).use { zipFile -> @@ -311,9 +305,7 @@ class TestBuildPlugin : AbstractTest() { """.trimIndent() ) - gradleRunner() - .withArguments(":buildPlugin", "dependencies", "--stacktrace", "--info") - .build() + runGradle(":buildPlugin", "dependencies", "--stacktrace", "--info") checkOutput() ZipFile(findJar()).use { zipFile -> @@ -334,9 +326,7 @@ class TestBuildPlugin : AbstractTest() { } """.trimIndent() ) - gradleRunner() - .withArguments("buildPlugin", "dependencies", "--stacktrace", "--info") - .build() + runGradle("buildPlugin", "dependencies", "--stacktrace", "--info") checkOutput() } @@ -349,9 +339,7 @@ class TestBuildPlugin : AbstractTest() { } """.trimIndent() ) - gradleRunner() - .withArguments("buildPlugin", "dependencies", "--stacktrace", "--info") - .build() + runGradle("buildPlugin", "dependencies", "--stacktrace", "--info") ZipFile(findJar()).use { zipFile -> @@ -372,9 +360,7 @@ class TestBuildPlugin : AbstractTest() { } """.trimIndent() ) - gradleRunner() - .withArguments("buildPlugin", "dependencies", "--stacktrace", "--info") - .build() + runGradle("buildPlugin", "dependencies", "--stacktrace", "--info") ZipFile(findJar()).use { zipFile -> @@ -397,9 +383,7 @@ class TestBuildPlugin : AbstractTest() { } """.trimIndent() ) - gradleRunner() - .withArguments("buildPlugin", "dependencies", "--stacktrace", "--info") - .build() + runGradle("buildPlugin", "dependencies", "--stacktrace", "--info") ZipFile(findJar()).use { zipFile -> @@ -423,9 +407,7 @@ class TestBuildPlugin : AbstractTest() { } """.trimIndent() ) - gradleRunner() - .withArguments("buildPlugin", "dependencies", "--stacktrace", "--info") - .build() + runGradle("buildPlugin", "dependencies", "--stacktrace", "--info") ZipFile(findJar()).use { zipFile -> assertNotNull(zipFile.getEntry("cn/hutool/core/annotation/Alias.class")) diff --git a/mirai-console/tools/gradle-plugin/src/integTest/kotlin/TestPluginApply.kt b/mirai-console/tools/gradle-plugin/src/integTest/kotlin/TestPluginApply.kt index bb0939757..acea047b2 100644 --- a/mirai-console/tools/gradle-plugin/src/integTest/kotlin/TestPluginApply.kt +++ b/mirai-console/tools/gradle-plugin/src/integTest/kotlin/TestPluginApply.kt @@ -15,8 +15,6 @@ class TestPluginApply : AbstractTest() { @Test fun `can apply plugin`() { - gradleRunner() - .withArguments("clean", "--stacktrace") - .build() + runGradle("clean", "--stacktrace") } } \ No newline at end of file diff --git a/mirai-core-api/build.gradle.kts b/mirai-core-api/build.gradle.kts index a65eb94a2..f39c75d7e 100644 --- a/mirai-core-api/build.gradle.kts +++ b/mirai-core-api/build.gradle.kts @@ -55,7 +55,7 @@ kotlin { } } - val jvmBaseMain by getting { + findByName("jvmBaseMain")?.apply { dependencies { api(`kotlinx-coroutines-jdk8`) // use -jvm modules for this magic target 'jvmBase' implementation(`jetbrains-annotations`) @@ -64,34 +64,32 @@ kotlin { } } - if (isAndroidSDKAvailable) { - val androidMain by getting { - dependsOn(commonMain) - dependencies { - compileOnly(`android-runtime`) + findByName("androidMain")?.apply { + dependsOn(commonMain) + dependencies { + compileOnly(`android-runtime`) // api(`ktor-client-android`) - } } } - val jvmMain by getting { + findByName("jvmMain")?.apply { } - val jvmTest by getting { + findByName("jvmTest")?.apply { dependencies { runtimeOnly(files("build/classes/kotlin/jvm/test")) // classpath is not properly set by IDE } } - val nativeMain by getting { + findByName("nativeMain")?.apply { dependencies { } } } } -if (isAndroidSDKAvailable) { +if (tasks.findByName("androidMainClasses") != null) { tasks.register("checkAndroidApiLevel") { doFirst { analyzes.AndroidApiLevelCheck.check( @@ -107,4 +105,4 @@ if (isAndroidSDKAvailable) { } configureMppPublishing() -configureBinaryValidators("jvm", "android") \ No newline at end of file +configureBinaryValidators(setOf("jvm", "android").filterTargets()) \ No newline at end of file diff --git a/mirai-core-api/src/nativeMain/kotlin/message/data/FileMessage.kt b/mirai-core-api/src/nativeMain/kotlin/message/data/FileMessage.kt index 583b60876..f8facc105 100644 --- a/mirai-core-api/src/nativeMain/kotlin/message/data/FileMessage.kt +++ b/mirai-core-api/src/nativeMain/kotlin/message/data/FileMessage.kt @@ -12,7 +12,6 @@ package net.mamoe.mirai.message.data import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.Mirai import net.mamoe.mirai.contact.FileSupported import net.mamoe.mirai.contact.file.AbsoluteFile @@ -42,7 +41,6 @@ import net.mamoe.mirai.utils.safeCast @Serializable(FileMessage.Serializer::class) @SerialName(FileMessage.SERIAL_NAME) @NotStableForInheritance -@JvmBlockingBridge public actual interface FileMessage : MessageContent, ConstrainSingle, CodableMessage { /** * 服务器需要的某种 ID. diff --git a/mirai-core-utils/build.gradle.kts b/mirai-core-utils/build.gradle.kts index ab64bb09a..494c1505d 100644 --- a/mirai-core-utils/build.gradle.kts +++ b/mirai-core-utils/build.gradle.kts @@ -47,31 +47,31 @@ kotlin { } } - val jvmBaseMain by getting { + findByName("jvmBaseMain")?.apply { dependencies { implementation(`jetbrains-annotations`) } } - if (isAndroidSDKAvailable) { - val androidMain by getting { - // - dependencies { - compileOnly(`android-runtime`) + findByName("androidMain")?.apply { + // + dependencies { + compileOnly(`android-runtime`) // api1(`ktor-client-android`) - } } } - val jvmMain by getting + findByName("jvmMain")?.apply { - val jvmTest by getting { + } + + findByName("jvmTest")?.apply { dependencies { runtimeOnly(files("build/classes/kotlin/jvm/test")) // classpath is not properly set by IDE } } - val nativeMain by getting { + findByName("nativeMain")?.apply { dependencies { // implementation("com.soywiz.korlibs.krypto:krypto:2.4.12") // ':mirai-core-utils:compileNativeMainKotlinMetadata' fails because compiler cannot find reference } @@ -79,7 +79,7 @@ kotlin { } } -if (isAndroidSDKAvailable) { +if (tasks.findByName("androidMainClasses") != null) { tasks.register("checkAndroidApiLevel") { doFirst { analyzes.AndroidApiLevelCheck.check( diff --git a/mirai-core/build.gradle.kts b/mirai-core/build.gradle.kts index 2a9a8e6ed..12a52d614 100644 --- a/mirai-core/build.gradle.kts +++ b/mirai-core/build.gradle.kts @@ -55,7 +55,7 @@ kotlin { } } - val jvmBaseMain by getting { + findByName("jvmBaseMain")?.apply { dependencies { implementation(bouncycastle) implementation(`log4j-api`) @@ -65,45 +65,43 @@ kotlin { } } - val jvmBaseTest by getting { + findByName("jvmBaseTest")?.apply { dependencies { implementation(`kotlinx-coroutines-debug`) } } - if (isAndroidSDKAvailable) { - val androidMain by getting { - dependsOn(commonMain) - dependencies { - compileOnly(`android-runtime`) - } + findByName("androidMain")?.apply { + dependsOn(commonMain) + dependencies { + compileOnly(`android-runtime`) } - val androidTest by getting { - dependencies { - implementation(kotlin("test", Versions.kotlinCompiler)) - implementation(kotlin("test-junit5", Versions.kotlinCompiler)) - implementation(kotlin("test-annotations-common")) - implementation(kotlin("test-common")) - //implementation("org.bouncycastle:bcprov-jdk15on:1.64") - } + } + findByName("androidTest")?.apply { + dependencies { + implementation(kotlin("test", Versions.kotlinCompiler)) + implementation(kotlin("test-junit5", Versions.kotlinCompiler)) + implementation(kotlin("test-annotations-common")) + implementation(kotlin("test-common")) + //implementation("org.bouncycastle:bcprov-jdk15on:1.64") } } - val jvmMain by getting { + findByName("jvmMain")?.apply { dependencies { //implementation("org.bouncycastle:bcprov-jdk15on:1.64") // api(kotlinx("coroutines-debug", Versions.coroutines)) } } - val jvmTest by getting { + findByName("jvmTest")?.apply { dependencies { api(`kotlinx-coroutines-debug`) // implementation("net.mamoe:mirai-login-solver-selenium:1.0-dev-14") } } - val nativeMain by getting { + findByName("nativeMain")?.apply { dependencies { } } @@ -151,7 +149,7 @@ kotlin { } } - val darwinMain by getting { + findByName("darwinMain")?.apply { dependencies { implementation(`ktor-client-darwin`) } @@ -188,7 +186,7 @@ afterEvaluate { } } -if (isAndroidSDKAvailable) { +if (tasks.findByName("androidMainClasses") != null) { tasks.register("checkAndroidApiLevel") { doFirst { analyzes.AndroidApiLevelCheck.check( @@ -204,4 +202,4 @@ if (isAndroidSDKAvailable) { } configureMppPublishing() -configureBinaryValidators("jvm", "android") \ No newline at end of file +configureBinaryValidators(setOf("jvm", "android").filterTargets()) \ No newline at end of file diff --git a/mirai-core/src/commonTest/kotlin/message/data/ContentEqualsTest.kt b/mirai-core/src/commonTest/kotlin/message/data/ContentEqualsTest.kt index f93073202..1003bbf35 100644 --- a/mirai-core/src/commonTest/kotlin/message/data/ContentEqualsTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/data/ContentEqualsTest.kt @@ -9,6 +9,7 @@ package net.mamoe.mirai.internal.message.data +import net.mamoe.mirai.internal.test.AbstractTest import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.safeCast import kotlin.test.Test @@ -26,7 +27,7 @@ internal class TestConstrainSingleMessage : ConstrainSingle, Any() { get() = Key } -internal class ContentEqualsTest { +internal class ContentEqualsTest: AbstractTest() { @Test fun testContentEquals() { diff --git a/mirai-core/src/nativeMain/kotlin/utils/crypto/ECDH.kt b/mirai-core/src/nativeMain/kotlin/utils/crypto/ECDH.kt index 064820081..987f68321 100644 --- a/mirai-core/src/nativeMain/kotlin/utils/crypto/ECDH.kt +++ b/mirai-core/src/nativeMain/kotlin/utils/crypto/ECDH.kt @@ -15,7 +15,6 @@ import net.mamoe.mirai.utils.md5 import net.mamoe.mirai.utils.toUHexString import openssl.* import platform.posix.errno -import platform.posix.free private const val curveId = NID_X9_62_prime256v1 @@ -27,13 +26,26 @@ private val convForm by lazy { EC_GROUP_get_point_conversion_form(group) } // shared, not freed! private val bnCtx by lazy { BN_CTX_new() } +// ====ATTENTION==== +// Do not use [platform.posix.free] easily +// For anything allocated by OpenSSL, _free or CRYPTO_free +// (the underlying of OPENSSL_free macro) should be called. +// It's more than dangerous to assume OpenSSL uses the same memory manager as general posix functions, +// easily causing memory leaking (usually on *nix) or crash (usually on Windows) internal actual interface ECDHPublicKey : OpenSSLKey { val encoded: ByteArray + + /** + * @return It is the caller's responsibility to free this memory with a subsequent call to [EC_POINT_free] + */ fun toPoint(): CPointer } internal actual interface ECDHPrivateKey : OpenSSLKey { + /** + * @return It is the caller's responsibility to free this memory with a subsequent call to [BN_free] + */ fun toBignum(): CPointer } @@ -52,16 +64,14 @@ internal class OpenSslPrivateKey( companion object { fun fromKey(key: CPointer): OpenSslPrivateKey { + // Note that the private key (bignum) is associated with the key + // We can't free it, or it'll crash when EC_KEY_free val bn = EC_KEY_get0_private_key(key) ?: error("Failed EC_KEY_get0_private_key") + val ptr = BN_bn2hex(bn) ?: error("Failed EC_POINT_bn2point") val hex = try { - val ptr = BN_bn2hex(bn) ?: error("Failed EC_POINT_bn2point") - try { - ptr.toKString() - } finally { - free(ptr) - } + ptr.toKString() } finally { - BN_free(bn) + CRYPTO_free(ptr, "OpenSslPrivateKey.Companion.fromKey(key: CPointer)", -1) } return OpenSslPrivateKey(hex) } @@ -115,7 +125,7 @@ private fun CPointer.toKtHex(): String { return try { ptr.toKString() } finally { - free(ptr) + CRYPTO_free(ptr, "CPointer.toKtHex()", -1) } } @@ -159,15 +169,13 @@ internal actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) { actual fun generateKeyPair(initialPublicKey: ECDHPublicKey): ECDHKeyPair { val key: CPointer = EC_KEY_new_by_curve_name(curveId) ?: throw IllegalStateException("Failed to create key curve, $errno") - - if (1 != EC_KEY_generate_key(key)) { - throw IllegalStateException("Failed to generate key, $errno") - } - try { + if (1 != EC_KEY_generate_key(key)) { + throw IllegalStateException("Failed to generate key, $errno") + } return ECDHKeyPairImpl.fromKey(key, initialPublicKey) } finally { - free(key) // TODO: THIS MAY CAUSE MEMORY LEAK. But EC_KEY_free() will terminate the process for unknown reason. + EC_KEY_free(key) } }