mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-27 17:00:14 +08:00
Add android compilation target
This commit is contained in:
parent
2a7f0a3a8f
commit
bacfef584f
212
build.gradle.kts
212
build.gradle.kts
@ -11,11 +11,8 @@
|
||||
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import org.jetbrains.dokka.gradle.DokkaTask
|
||||
import org.jetbrains.kotlin.gradle.dsl.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
@ -36,7 +33,7 @@ buildscript {
|
||||
}
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version Versions.kotlinCompiler
|
||||
kotlin("jvm") // version Versions.kotlinCompiler
|
||||
kotlin("plugin.serialization") version Versions.kotlinCompiler
|
||||
id("org.jetbrains.dokka") version Versions.dokka
|
||||
id("net.mamoe.kotlin-jvm-blocking-bridge") version Versions.blockingBridge
|
||||
@ -63,8 +60,6 @@ configure<kotlinx.validation.ApiValidationExtension> {
|
||||
nonPublicMarkers.add("net.mamoe.mirai.MiraiExperimentalApi")
|
||||
}
|
||||
|
||||
project.ext.set("isAndroidSDKAvailable", false)
|
||||
|
||||
tasks.register("publishMiraiCoreArtifactsToMavenLocal") {
|
||||
group = "mirai"
|
||||
dependsOn(
|
||||
@ -74,24 +69,6 @@ tasks.register("publishMiraiCoreArtifactsToMavenLocal") {
|
||||
)
|
||||
}
|
||||
|
||||
// until
|
||||
// https://youtrack.jetbrains.com/issue/KT-37152,
|
||||
// are fixed.
|
||||
|
||||
/*
|
||||
runCatching {
|
||||
val keyProps = Properties().apply {
|
||||
file("local.properties").takeIf { it.exists() }?.inputStream()?.use { load(it) }
|
||||
}
|
||||
if (keyProps.getProperty("sdk.dir", "").isNotEmpty()) {
|
||||
project.ext.set("isAndroidSDKAvailable", true)
|
||||
} else {
|
||||
project.ext.set("isAndroidSDKAvailable", false)
|
||||
}
|
||||
}.exceptionOrNull()?.run {
|
||||
project.ext.set("isAndroidSDKAvailable", false)
|
||||
}*/
|
||||
|
||||
allprojects {
|
||||
group = "net.mamoe"
|
||||
version = Versions.project
|
||||
@ -136,12 +113,6 @@ subprojects {
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.useIr() {
|
||||
kotlinCompilations?.forEach { kotlinCompilation ->
|
||||
kotlinCompilation.kotlinOptions.freeCompilerArgs += "-Xuse-ir"
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureDokka() {
|
||||
apply(plugin = "org.jetbrains.dokka")
|
||||
tasks {
|
||||
@ -175,169 +146,44 @@ fun Project.configureDokka() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE") // or error
|
||||
fun Project.configureJvmTarget() {
|
||||
tasks.withType(KotlinJvmCompile::class.java) {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
kotlinTargets.orEmpty().filterIsInstance<KotlinJvmTarget>().forEach { target ->
|
||||
target.compilations.all {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
kotlinOptions.languageVersion = "1.4"
|
||||
}
|
||||
target.testRuns["test"].executionTask.configure { useJUnitPlatform() }
|
||||
}
|
||||
|
||||
extensions.findByType(JavaPluginExtension::class.java)?.run {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureMppShadow() {
|
||||
val kotlin =
|
||||
runCatching {
|
||||
|
||||
(this as ExtensionAware).extensions.getByName("kotlin") as? KotlinMultiplatformExtension
|
||||
}.getOrNull() ?: return
|
||||
|
||||
val shadowJvmJar by tasks.creating(ShadowJar::class) sd@{
|
||||
group = "mirai"
|
||||
archiveClassifier.set("-all")
|
||||
if (project.configurations.findByName("jvmRuntimeClasspath") != null) {
|
||||
val shadowJvmJar by tasks.creating(ShadowJar::class) sd@{
|
||||
group = "mirai"
|
||||
archiveClassifier.set("-all")
|
||||
|
||||
val compilations =
|
||||
kotlin.targets.filter { it.platformType == KotlinPlatformType.jvm }
|
||||
.map { it.compilations["main"] }
|
||||
val compilations =
|
||||
kotlin.targets.filter { it.platformType == KotlinPlatformType.jvm }
|
||||
.map { it.compilations["main"] }
|
||||
|
||||
compilations.forEach {
|
||||
dependsOn(it.compileKotlinTask)
|
||||
from(it.output)
|
||||
}
|
||||
|
||||
from(project.configurations.getByName("jvmRuntimeClasspath"))
|
||||
|
||||
this.exclude { file ->
|
||||
file.name.endsWith(".sf", ignoreCase = true)
|
||||
}
|
||||
|
||||
/*
|
||||
this.manifest {
|
||||
this.attributes(
|
||||
"Manifest-Version" to 1,
|
||||
"Implementation-Vendor" to "Mamoe Technologies",
|
||||
"Implementation-Title" to this.name.toString(),
|
||||
"Implementation-Version" to this.version.toString()
|
||||
)
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureEncoding() {
|
||||
tasks.withType(JavaCompile::class.java) {
|
||||
options.encoding = "UTF8"
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureKotlinTestSettings() {
|
||||
tasks.withType(Test::class) {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
when {
|
||||
isKotlinJvmProject -> {
|
||||
dependencies {
|
||||
testImplementation(kotlin("test-junit5"))
|
||||
|
||||
testApi("org.junit.jupiter:junit-jupiter-api:5.2.0")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.2.0")
|
||||
compilations.forEach {
|
||||
dependsOn(it.compileKotlinTask)
|
||||
from(it.output)
|
||||
}
|
||||
}
|
||||
isKotlinMpp -> {
|
||||
kotlinSourceSets?.forEach { sourceSet ->
|
||||
if (sourceSet.name == "common") {
|
||||
sourceSet.dependencies {
|
||||
implementation(kotlin("test"))
|
||||
implementation(kotlin("test-annotations-common"))
|
||||
}
|
||||
} else {
|
||||
sourceSet.dependencies {
|
||||
implementation(kotlin("test-junit5"))
|
||||
|
||||
implementation("org.junit.jupiter:junit-jupiter-api:5.2.0")
|
||||
implementation("org.junit.jupiter:junit-jupiter-engine:5.2.0")
|
||||
}
|
||||
}
|
||||
from(project.configurations.findByName("jvmRuntimeClasspath"))
|
||||
|
||||
this.exclude { file ->
|
||||
file.name.endsWith(".sf", ignoreCase = true)
|
||||
}
|
||||
|
||||
/*
|
||||
this.manifest {
|
||||
this.attributes(
|
||||
"Manifest-Version" to 1,
|
||||
"Implementation-Vendor" to "Mamoe Technologies",
|
||||
"Implementation-Title" to this.name.toString(),
|
||||
"Implementation-Version" to this.version.toString()
|
||||
)
|
||||
}*/
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun Project.configureKotlinCompilerSettings() {
|
||||
val kotlinCompilations = kotlinCompilations ?: return
|
||||
for (kotlinCompilation in kotlinCompilations) with(kotlinCompilation) {
|
||||
if (isKotlinJvmProject) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
this as KotlinCompilation<KotlinJvmOptions>
|
||||
}
|
||||
kotlinOptions.freeCompilerArgs += "-Xjvm-default=all"
|
||||
}
|
||||
}
|
||||
|
||||
val experimentalAnnotations = arrayOf(
|
||||
"kotlin.RequiresOptIn",
|
||||
"kotlin.contracts.ExperimentalContracts",
|
||||
"kotlin.experimental.ExperimentalTypeInference",
|
||||
"kotlin.ExperimentalUnsignedTypes",
|
||||
"kotlin.time.ExperimentalTime",
|
||||
"kotlin.io.path.ExperimentalPathApi",
|
||||
"io.ktor.util.KtorExperimentalAPI",
|
||||
|
||||
"kotlinx.serialization.ExperimentalSerializationApi",
|
||||
|
||||
"net.mamoe.mirai.utils.MiraiInternalApi",
|
||||
"net.mamoe.mirai.utils.MiraiExperimentalApi",
|
||||
"net.mamoe.mirai.LowLevelApi",
|
||||
"net.mamoe.mirai.utils.UnstableExternalImage",
|
||||
|
||||
"net.mamoe.mirai.message.data.ExperimentalMessageKey",
|
||||
"net.mamoe.mirai.console.ConsoleFrontEndImplementation",
|
||||
"net.mamoe.mirai.console.util.ConsoleInternalApi",
|
||||
"net.mamoe.mirai.console.util.ConsoleExperimentalApi"
|
||||
)
|
||||
|
||||
fun Project.configureKotlinExperimentalUsages() {
|
||||
val sourceSets = kotlinSourceSets ?: return
|
||||
|
||||
for (target in sourceSets) {
|
||||
target.languageSettings.progressiveMode = true
|
||||
target.languageSettings.enableLanguageFeature("InlineClasses")
|
||||
experimentalAnnotations.forEach { a ->
|
||||
target.languageSettings.useExperimentalAnnotation(a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureFlattenSourceSets() {
|
||||
sourceSets {
|
||||
findByName("main")?.apply {
|
||||
resources.setSrcDirs(listOf(projectDir.resolve("resources")))
|
||||
java.setSrcDirs(listOf(projectDir.resolve("src")))
|
||||
}
|
||||
findByName("test")?.apply {
|
||||
resources.setSrcDirs(listOf(projectDir.resolve("resources")))
|
||||
java.setSrcDirs(listOf(projectDir.resolve("test")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val Project.kotlinSourceSets get() = extensions.findByName("kotlin").safeAs<KotlinProjectExtension>()?.sourceSets
|
||||
|
||||
val Project.kotlinTargets
|
||||
get() =
|
||||
extensions.findByName("kotlin").safeAs<KotlinSingleTargetExtension>()?.target?.let { listOf(it) }
|
||||
?: extensions.findByName("kotlin").safeAs<KotlinMultiplatformExtension>()?.targets
|
||||
|
||||
val Project.isKotlinJvmProject: Boolean get() = extensions.findByName("kotlin") is KotlinJvmProjectExtension
|
||||
val Project.isKotlinMpp: Boolean get() = extensions.findByName("kotlin") is KotlinMultiplatformExtension
|
||||
|
||||
val Project.kotlinCompilations
|
||||
get() = kotlinTargets?.flatMap { it.compilations }
|
@ -14,6 +14,8 @@ plugins {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
@ -46,6 +48,9 @@ dependencies {
|
||||
|
||||
api("com.jfrog.bintray.gradle", "gradle-bintray-plugin", version("bintray"))
|
||||
api("com.github.jengelman.gradle.plugins", "shadow", version("shadow"))
|
||||
api("org.jetbrains.kotlin", "kotlin-gradle-plugin", version("kotlinCompiler"))
|
||||
api("org.jetbrains.kotlin", "kotlin-compiler-embeddable", version("kotlinCompiler"))
|
||||
api("com.android.tools.build", "gradle", version("androidGradlePlugin"))
|
||||
|
||||
api(gradleApi())
|
||||
}
|
@ -10,7 +10,7 @@
|
||||
import org.gradle.api.NamedDomainObjectCollection
|
||||
import org.gradle.api.NamedDomainObjectProvider
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.provideDelegate
|
||||
import java.util.*
|
||||
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
@ -21,11 +21,31 @@ import org.gradle.kotlin.dsl.provideDelegate
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
val Project.isAndroidSDKAvailable: Boolean
|
||||
get() {
|
||||
val isAndroidSDKAvailable: Boolean by this
|
||||
return isAndroidSDKAvailable
|
||||
private object ProjectAndroidSdkAvailability {
|
||||
val map: MutableMap<String, Boolean> = mutableMapOf()
|
||||
|
||||
@Synchronized
|
||||
operator fun get(project: Project): Boolean {
|
||||
if (map[project.path] != null) return map[project.path]!!
|
||||
|
||||
val projectAvailable = project.runCatching {
|
||||
val keyProps = Properties().apply {
|
||||
file("local.properties").takeIf { it.exists() }?.inputStream()?.use { load(it) }
|
||||
}
|
||||
keyProps.getProperty("sdk.dir", "").isNotEmpty()
|
||||
}.getOrElse { false }
|
||||
|
||||
|
||||
fun impl(): Boolean {
|
||||
if (project === project.rootProject) return projectAvailable
|
||||
return projectAvailable || get(project.rootProject)
|
||||
}
|
||||
map[project.path] = impl()
|
||||
return map[project.path]!!
|
||||
}
|
||||
}
|
||||
|
||||
val Project.isAndroidSDKAvailable: Boolean get() = ProjectAndroidSdkAvailability[this]
|
||||
|
||||
val <T> NamedDomainObjectCollection<T>.androidMain: NamedDomainObjectProvider<T>
|
||||
get() = named("androidMain")
|
||||
@ -43,16 +63,12 @@ val <T> NamedDomainObjectCollection<T>.commonMain: NamedDomainObjectProvider<T>
|
||||
get() = named("commonMain")
|
||||
|
||||
fun Project.printAndroidNotInstalled() {
|
||||
// println(
|
||||
// """Android SDK 可能未安装.
|
||||
// $name 的 Android 目标编译将不会进行.
|
||||
// 这不会影响 Android 以外的平台的编译.
|
||||
// """.trimIndent()
|
||||
// )
|
||||
// println(
|
||||
// """Android SDK might not be installed.
|
||||
// Android target of $name will not be compiled.
|
||||
// It does no influence on the compilation of other platforms.
|
||||
// """.trimIndent()
|
||||
// )
|
||||
println(
|
||||
"""Android SDK 可能未安装. $name 的 Android 目标编译将不会进行. 这不会影响 Android 以外的平台的编译.
|
||||
""".trimIndent()
|
||||
)
|
||||
println(
|
||||
"""Android SDK might not be installed. Android target of $name will not be compiled. It does no influence on the compilation of other platforms.
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
157
buildSrc/src/main/kotlin/ProjectConfigure.kt
Normal file
157
buildSrc/src/main/kotlin/ProjectConfigure.kt
Normal file
@ -0,0 +1,157 @@
|
||||
@file:Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
|
||||
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.api.tasks.testing.Test
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.jetbrains.kotlin.gradle.dsl.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
|
||||
|
||||
|
||||
fun Project.useIr() {
|
||||
kotlinCompilations?.forEach { kotlinCompilation ->
|
||||
kotlinCompilation.kotlinOptions.freeCompilerArgs += "-Xuse-ir"
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE") // or error
|
||||
fun Project.configureJvmTarget() {
|
||||
tasks.withType(KotlinJvmCompile::class.java) {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
kotlinTargets.orEmpty().filterIsInstance<KotlinJvmTarget>().forEach { target ->
|
||||
target.compilations.all {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
kotlinOptions.languageVersion = "1.4"
|
||||
}
|
||||
target.testRuns["test"].executionTask.configure { useJUnitPlatform() }
|
||||
}
|
||||
|
||||
extensions.findByType(JavaPluginExtension::class.java)?.run {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureEncoding() {
|
||||
tasks.withType(JavaCompile::class.java) {
|
||||
options.encoding = "UTF8"
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureKotlinTestSettings() {
|
||||
tasks.withType(Test::class) {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
when {
|
||||
isKotlinJvmProject -> {
|
||||
dependencies {
|
||||
"testImplementation"(kotlin("test-junit5"))
|
||||
|
||||
"testApi"("org.junit.jupiter:junit-jupiter-api:5.2.0")
|
||||
"testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.2.0")
|
||||
}
|
||||
}
|
||||
isKotlinMpp -> {
|
||||
kotlinSourceSets?.forEach { sourceSet ->
|
||||
if (sourceSet.name == "common") {
|
||||
sourceSet.dependencies {
|
||||
implementation(kotlin("test"))
|
||||
implementation(kotlin("test-annotations-common"))
|
||||
}
|
||||
} else {
|
||||
sourceSet.dependencies {
|
||||
implementation(kotlin("test-junit5"))
|
||||
|
||||
implementation("org.junit.jupiter:junit-jupiter-api:5.2.0")
|
||||
implementation("org.junit.jupiter:junit-jupiter-engine:5.2.0")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureKotlinCompilerSettings() {
|
||||
val kotlinCompilations = kotlinCompilations ?: return
|
||||
for (kotlinCompilation in kotlinCompilations) with(kotlinCompilation) {
|
||||
if (isKotlinJvmProject) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
this as KotlinCompilation<KotlinJvmOptions>
|
||||
}
|
||||
kotlinOptions.freeCompilerArgs += "-Xjvm-default=all"
|
||||
}
|
||||
}
|
||||
|
||||
val experimentalAnnotations = arrayOf(
|
||||
"kotlin.RequiresOptIn",
|
||||
"kotlin.contracts.ExperimentalContracts",
|
||||
"kotlin.experimental.ExperimentalTypeInference",
|
||||
"kotlin.ExperimentalUnsignedTypes",
|
||||
"kotlin.time.ExperimentalTime",
|
||||
"kotlin.io.path.ExperimentalPathApi",
|
||||
"io.ktor.util.KtorExperimentalAPI",
|
||||
|
||||
"kotlinx.serialization.ExperimentalSerializationApi",
|
||||
|
||||
"net.mamoe.mirai.utils.MiraiInternalApi",
|
||||
"net.mamoe.mirai.utils.MiraiExperimentalApi",
|
||||
"net.mamoe.mirai.LowLevelApi",
|
||||
"net.mamoe.mirai.utils.UnstableExternalImage",
|
||||
|
||||
"net.mamoe.mirai.message.data.ExperimentalMessageKey",
|
||||
"net.mamoe.mirai.console.ConsoleFrontEndImplementation",
|
||||
"net.mamoe.mirai.console.util.ConsoleInternalApi",
|
||||
"net.mamoe.mirai.console.util.ConsoleExperimentalApi"
|
||||
)
|
||||
|
||||
fun Project.configureKotlinExperimentalUsages() {
|
||||
val sourceSets = kotlinSourceSets ?: return
|
||||
|
||||
for (target in sourceSets) {
|
||||
target.configureKotlinExperimentalUsages()
|
||||
}
|
||||
}
|
||||
|
||||
fun KotlinSourceSet.configureKotlinExperimentalUsages() {
|
||||
languageSettings.progressiveMode = true
|
||||
languageSettings.enableLanguageFeature("InlineClasses")
|
||||
experimentalAnnotations.forEach { a ->
|
||||
languageSettings.useExperimentalAnnotation(a)
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureFlattenSourceSets() {
|
||||
sourceSets {
|
||||
findByName("main")?.apply {
|
||||
resources.setSrcDirs(listOf(projectDir.resolve("resources")))
|
||||
java.setSrcDirs(listOf(projectDir.resolve("src")))
|
||||
}
|
||||
findByName("test")?.apply {
|
||||
resources.setSrcDirs(listOf(projectDir.resolve("resources")))
|
||||
java.setSrcDirs(listOf(projectDir.resolve("test")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T> Any?.safeAs(): T? {
|
||||
return this as? T
|
||||
}
|
||||
|
||||
val Project.kotlinSourceSets get() = extensions.findByName("kotlin").safeAs<KotlinProjectExtension>()?.sourceSets
|
||||
|
||||
val Project.kotlinTargets
|
||||
get() =
|
||||
extensions.findByName("kotlin").safeAs<KotlinSingleTargetExtension>()?.target?.let { listOf(it) }
|
||||
?: extensions.findByName("kotlin").safeAs<KotlinMultiplatformExtension>()?.targets
|
||||
|
||||
val Project.isKotlinJvmProject: Boolean get() = extensions.findByName("kotlin") is KotlinJvmProjectExtension
|
||||
val Project.isKotlinMpp: Boolean get() = extensions.findByName("kotlin") is KotlinMultiplatformExtension
|
||||
|
||||
val Project.kotlinCompilations
|
||||
get() = kotlinTargets?.flatMap { it.compilations }
|
@ -34,7 +34,8 @@ object Versions {
|
||||
|
||||
const val blockingBridge = "1.7.4"
|
||||
|
||||
const val androidGradlePlugin = "3.5.3"
|
||||
const val androidGradlePlugin = "4.1.1"
|
||||
const val android = "4.1.1.4"
|
||||
|
||||
const val bintray = "1.8.5"
|
||||
const val shadow = "6.1.0"
|
||||
@ -104,3 +105,5 @@ const val `jetbrains-annotations` = "org.jetbrains:annotations:19.0.0"
|
||||
|
||||
|
||||
const val `caller-finder` = "io.github.karlatemp:caller:1.0.1"
|
||||
|
||||
const val `android-runtime` = "com.google.android:android:${Versions.android}"
|
@ -1 +1 @@
|
||||
Subproject commit a5481accb5f882d121ff9fc1d55e4e5f3e908e76
|
||||
Subproject commit 3f98d8ec2abfa963c5f720f32b7b27e863569bc8
|
@ -12,7 +12,6 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.serialization")
|
||||
id("java")
|
||||
`maven-publish`
|
||||
id("com.jfrog.bintray")
|
||||
id("net.mamoe.kotlin-jvm-blocking-bridge")
|
||||
|
@ -28,9 +28,13 @@ kotlin {
|
||||
explicitApi()
|
||||
|
||||
if (isAndroidSDKAvailable) {
|
||||
apply(from = rootProject.file("gradle/android.gradle"))
|
||||
android("android") {
|
||||
publishAllLibraryVariants()
|
||||
// apply(from = rootProject.file("gradle/android.gradle"))
|
||||
// android("android") {
|
||||
// publishAllLibraryVariants()
|
||||
// }
|
||||
jvm("android") {
|
||||
attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.androidJvm)
|
||||
// publishAllLibraryVariants()
|
||||
}
|
||||
} else {
|
||||
printAndroidNotInstalled()
|
||||
@ -47,7 +51,7 @@ kotlin {
|
||||
// }
|
||||
|
||||
sourceSets {
|
||||
commonMain {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(project(":mirai-core-utils"))
|
||||
api(kotlin("serialization"))
|
||||
@ -75,16 +79,20 @@ kotlin {
|
||||
}
|
||||
|
||||
if (isAndroidSDKAvailable) {
|
||||
androidMain {
|
||||
val androidMain by getting {
|
||||
dependsOn(commonMain)
|
||||
dependencies {
|
||||
compileOnly(`android-runtime`)
|
||||
api1(`ktor-client-android`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val jvmMain by getting
|
||||
val jvmMain by getting {
|
||||
|
||||
jvmTest {
|
||||
}
|
||||
|
||||
val jvmTest by getting {
|
||||
dependencies {
|
||||
runtimeOnly(files("build/classes/kotlin/jvm/test")) // classpath is not properly set by IDE
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2019-2020 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.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai
|
||||
|
||||
import java.util.*
|
||||
import kotlin.reflect.full.companionObjectInstance
|
||||
|
||||
@JvmSynthetic
|
||||
internal actual fun findMiraiInstance(): IMirai {
|
||||
return ServiceLoader.load(IMirai::class.java).firstOrNull()
|
||||
?: Class.forName("net.mamoe.mirai.internal.MiraiImpl").kotlin.companionObjectInstance as IMirai
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2019-2020 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.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||
|
||||
package net.mamoe.mirai.utils
|
||||
|
||||
import android.util.Log
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* JVM 控制台日志实现
|
||||
*
|
||||
*
|
||||
* 单条日志格式 (正则) 为:
|
||||
* ```regex
|
||||
* ^([\w-]*\s[\w:]*)\s(\w)\/(.*?):\s(.+)$
|
||||
* ```
|
||||
* 其中 group 分别为: 日期与时间, 严重程度, [identity], 消息内容.
|
||||
*
|
||||
* 示例:
|
||||
* ```log
|
||||
* 2020-05-21 19:51:09 V/Bot 1994701021: Send: OidbSvc.0x88d_7
|
||||
* ```
|
||||
*
|
||||
* 日期时间格式为 `yyyy-MM-dd HH:mm:ss`,
|
||||
*
|
||||
* 严重程度为 V, I, W, E. 分别对应 verbose, info, warning, error
|
||||
*
|
||||
* @param isColored 是否添加 ANSI 颜色
|
||||
*
|
||||
* @see MiraiLogger.create
|
||||
* @see SingleFileLogger 使用单一文件记录日志
|
||||
* @see DirectoryLogger 在一个目录中按日期存放文件记录日志, 自动清理过期日志
|
||||
*/
|
||||
@MiraiInternalApi
|
||||
public actual open class PlatformLogger constructor(
|
||||
public override val identity: String? = "Mirai",
|
||||
/**
|
||||
* 日志输出. 不会自动添加换行
|
||||
*/
|
||||
public open val output: (String) -> Unit,
|
||||
public val isColored: Boolean = true
|
||||
) : MiraiLoggerPlatformBase() {
|
||||
public actual constructor(identity: String?) : this(identity, ::println)
|
||||
public actual constructor(identity: String?, output: (String) -> Unit) : this(identity, output, true)
|
||||
|
||||
/**
|
||||
* 输出一条日志. [message] 末尾可能不带换行符.
|
||||
*/
|
||||
protected open fun printLog(message: String?, priority: SimpleLogger.LogPriority) {
|
||||
Log.e(identity, message)
|
||||
if (isColored) output("${priority.color}$currentTimeFormatted ${priority.simpleName}/$identity: $message${Color.RESET}")
|
||||
else output("$currentTimeFormatted ${priority.simpleName}/$identity: $message")
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 [SimpleLogger.LogPriority] 的颜色
|
||||
*/
|
||||
protected open val SimpleLogger.LogPriority.color: Color
|
||||
get() = when (this) {
|
||||
SimpleLogger.LogPriority.VERBOSE -> Color.RESET
|
||||
SimpleLogger.LogPriority.INFO -> Color.LIGHT_GREEN
|
||||
SimpleLogger.LogPriority.WARNING -> Color.LIGHT_RED
|
||||
SimpleLogger.LogPriority.ERROR -> Color.RED
|
||||
SimpleLogger.LogPriority.DEBUG -> Color.LIGHT_CYAN
|
||||
}
|
||||
|
||||
public override fun verbose0(message: String?): Unit = printLog(message, SimpleLogger.LogPriority.VERBOSE)
|
||||
|
||||
public override fun verbose0(message: String?, e: Throwable?) {
|
||||
if (e != null) verbose((message ?: e.toString()) + "\n${e.stackTraceString}")
|
||||
else verbose(message.toString())
|
||||
}
|
||||
|
||||
public override fun info0(message: String?): Unit = printLog(message, SimpleLogger.LogPriority.INFO)
|
||||
public override fun info0(message: String?, e: Throwable?) {
|
||||
if (e != null) info((message ?: e.toString()) + "\n${e.stackTraceString}")
|
||||
else info(message.toString())
|
||||
}
|
||||
|
||||
public override fun warning0(message: String?): Unit = printLog(message, SimpleLogger.LogPriority.WARNING)
|
||||
public override fun warning0(message: String?, e: Throwable?) {
|
||||
if (e != null) warning((message ?: e.toString()) + "\n${e.stackTraceString}")
|
||||
else warning(message.toString())
|
||||
}
|
||||
|
||||
public override fun error0(message: String?): Unit = printLog(message, SimpleLogger.LogPriority.ERROR)
|
||||
public override fun error0(message: String?, e: Throwable?) {
|
||||
if (e != null) error((message ?: e.toString()) + "\n${e.stackTraceString}")
|
||||
else error(message.toString())
|
||||
}
|
||||
|
||||
public override fun debug0(message: String?): Unit = printLog(message, SimpleLogger.LogPriority.DEBUG)
|
||||
public override fun debug0(message: String?, e: Throwable?) {
|
||||
if (e != null) debug((message ?: e.toString()) + "\n${e.stackTraceString}")
|
||||
else debug(message.toString())
|
||||
}
|
||||
protected open val timeFormat: DateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.SIMPLIFIED_CHINESE)
|
||||
|
||||
private val currentTimeFormatted get() = timeFormat.format(Date())
|
||||
|
||||
@MiraiExperimentalApi("This is subject to change.")
|
||||
protected enum class Color(private val format: String) {
|
||||
RESET("\u001b[0m"),
|
||||
|
||||
WHITE("\u001b[30m"),
|
||||
RED("\u001b[31m"),
|
||||
EMERALD_GREEN("\u001b[32m"),
|
||||
GOLD("\u001b[33m"),
|
||||
BLUE("\u001b[34m"),
|
||||
PURPLE("\u001b[35m"),
|
||||
GREEN("\u001b[36m"),
|
||||
|
||||
GRAY("\u001b[90m"),
|
||||
LIGHT_RED("\u001b[91m"),
|
||||
LIGHT_GREEN("\u001b[92m"),
|
||||
LIGHT_YELLOW("\u001b[93m"),
|
||||
LIGHT_BLUE("\u001b[94m"),
|
||||
LIGHT_PURPLE("\u001b[95m"),
|
||||
LIGHT_CYAN("\u001b[96m")
|
||||
;
|
||||
|
||||
override fun toString(): String = format
|
||||
}
|
||||
}
|
||||
|
||||
@get:JvmSynthetic
|
||||
internal val Throwable.stackTraceString
|
||||
get() = this.stackTraceToString()
|
@ -23,29 +23,20 @@ plugins {
|
||||
|
||||
description = "mirai-core utilities"
|
||||
|
||||
val isAndroidSDKAvailable: Boolean by project
|
||||
|
||||
kotlin {
|
||||
explicitApi()
|
||||
|
||||
if (isAndroidSDKAvailable) {
|
||||
apply(from = rootProject.file("gradle/android.gradle"))
|
||||
android("android") {
|
||||
publishAllLibraryVariants()
|
||||
// apply(from = rootProject.file("gradle/android.gradle"))
|
||||
// android("android") {
|
||||
// publishAllLibraryVariants()
|
||||
// }
|
||||
jvm("android") {
|
||||
attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.androidJvm)
|
||||
// publishAllLibraryVariants()
|
||||
}
|
||||
} else {
|
||||
println(
|
||||
"""Android SDK 可能未安装.
|
||||
$name 的 Android 目标编译将不会进行.
|
||||
这不会影响 Android 以外的平台的编译.
|
||||
""".trimIndent()
|
||||
)
|
||||
println(
|
||||
"""Android SDK might not be installed.
|
||||
Android target of $name will not be compiled.
|
||||
It does no influence on the compilation of other platforms.
|
||||
""".trimIndent()
|
||||
)
|
||||
printAndroidNotInstalled()
|
||||
}
|
||||
|
||||
jvm("common") {
|
||||
@ -79,8 +70,10 @@ kotlin {
|
||||
}
|
||||
|
||||
if (isAndroidSDKAvailable) {
|
||||
androidMain {
|
||||
val androidMain by getting {
|
||||
//
|
||||
dependencies {
|
||||
compileOnly(`android-runtime`)
|
||||
api1(`ktor-client-android`)
|
||||
}
|
||||
}
|
||||
|
14
mirai-core-utils/src/main/AndroidManifest.xml
Normal file
14
mirai-core-utils/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright 2019-2020 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.
|
||||
~
|
||||
~ https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
-->
|
||||
|
||||
<manifest package="net.mamoe.mirai" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
</manifest>
|
@ -18,13 +18,10 @@ plugins {
|
||||
id("net.mamoe.kotlin-jvm-blocking-bridge")
|
||||
`maven-publish`
|
||||
id("com.jfrog.bintray")
|
||||
java
|
||||
}
|
||||
|
||||
description = "Mirai Protocol implementation for QQ Android"
|
||||
|
||||
val isAndroidSDKAvailable: Boolean by project
|
||||
|
||||
afterEvaluate {
|
||||
tasks.getByName("compileKotlinCommon").enabled = false
|
||||
tasks.getByName("compileTestKotlinCommon").enabled = false
|
||||
@ -37,9 +34,13 @@ kotlin {
|
||||
explicitApi()
|
||||
|
||||
if (isAndroidSDKAvailable) {
|
||||
apply(from = rootProject.file("gradle/android.gradle"))
|
||||
android("android") {
|
||||
publishAllLibraryVariants()
|
||||
// apply(from = rootProject.file("gradle/android.gradle"))
|
||||
// android("android") {
|
||||
// publishAllLibraryVariants()
|
||||
// }
|
||||
jvm("android") {
|
||||
attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.androidJvm)
|
||||
// publishAllLibraryVariants()
|
||||
}
|
||||
} else {
|
||||
printAndroidNotInstalled()
|
||||
@ -58,7 +59,7 @@ kotlin {
|
||||
|
||||
sourceSets.apply {
|
||||
|
||||
commonMain {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
api(project(":mirai-core-api"))
|
||||
implementation(project(":mirai-core-utils"))
|
||||
@ -81,12 +82,13 @@ kotlin {
|
||||
}
|
||||
|
||||
if (isAndroidSDKAvailable) {
|
||||
androidMain {
|
||||
val androidMain by getting {
|
||||
dependsOn(commonMain)
|
||||
dependencies {
|
||||
compileOnly(`android-runtime`)
|
||||
}
|
||||
}
|
||||
|
||||
androidTest {
|
||||
val androidTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("test", Versions.kotlinCompiler))
|
||||
implementation(kotlin("test-junit", Versions.kotlinCompiler))
|
||||
@ -96,17 +98,17 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
jvmMain {
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
implementation("org.bouncycastle:bcprov-jdk15on:1.64")
|
||||
// api(kotlinx("coroutines-debug", Versions.coroutines))
|
||||
}
|
||||
}
|
||||
|
||||
jvmTest {
|
||||
val jvmTest by getting {
|
||||
dependencies {
|
||||
implementation("org.pcap4j:pcap4j-distribution:1.8.2")
|
||||
// implementation("net.mamoe:mirai-login-solver-selenium:1.0-dev-14")
|
||||
// implementation("net.mamoe:mirai-login-solver-selenium:1.0-dev-14")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.internal.utils.crypto
|
||||
|
||||
import net.mamoe.mirai.utils.md5
|
||||
import java.security.*
|
||||
import java.security.spec.ECGenParameterSpec
|
||||
import java.security.spec.X509EncodedKeySpec
|
||||
import javax.crypto.KeyAgreement
|
||||
|
||||
|
||||
@Suppress("ACTUAL_WITHOUT_EXPECT")
|
||||
internal actual typealias ECDHPrivateKey = PrivateKey
|
||||
@Suppress("ACTUAL_WITHOUT_EXPECT")
|
||||
internal actual typealias ECDHPublicKey = PublicKey
|
||||
|
||||
internal actual class ECDHKeyPairImpl(
|
||||
private val delegate: KeyPair
|
||||
) : ECDHKeyPair {
|
||||
override val privateKey: ECDHPrivateKey get() = delegate.private
|
||||
override val publicKey: ECDHPublicKey get() = delegate.public
|
||||
|
||||
override val initialShareKey: ByteArray by lazy { ECDH.calculateShareKey(privateKey, initialPublicKey) }
|
||||
}
|
||||
|
||||
internal actual fun ECDH() = ECDH(ECDH.generateKeyPair())
|
||||
|
||||
internal actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
|
||||
actual companion object {
|
||||
private const val curveName = "secp192k1" // p-256
|
||||
|
||||
actual val isECDHAvailable: Boolean
|
||||
|
||||
init {
|
||||
isECDHAvailable = kotlin.runCatching {
|
||||
fun testECDH() {
|
||||
ECDHKeyPairImpl(
|
||||
KeyPairGenerator.getInstance("ECDH")
|
||||
.also { it.initialize(ECGenParameterSpec(curveName)) }
|
||||
.genKeyPair()).let {
|
||||
calculateShareKey(it.privateKey, it.publicKey)
|
||||
}
|
||||
}
|
||||
|
||||
if (kotlin.runCatching { testECDH() }.isSuccess) {
|
||||
return@runCatching
|
||||
}
|
||||
|
||||
testECDH()
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}.isSuccess
|
||||
}
|
||||
|
||||
actual fun generateKeyPair(): ECDHKeyPair {
|
||||
if (!isECDHAvailable) {
|
||||
return ECDHKeyPair.DefaultStub
|
||||
}
|
||||
return ECDHKeyPairImpl(
|
||||
KeyPairGenerator.getInstance("ECDH")
|
||||
.also { it.initialize(ECGenParameterSpec(curveName)) }
|
||||
.genKeyPair())
|
||||
}
|
||||
|
||||
actual fun calculateShareKey(
|
||||
privateKey: ECDHPrivateKey,
|
||||
publicKey: ECDHPublicKey
|
||||
): ByteArray {
|
||||
val instance = KeyAgreement.getInstance("ECDH", "BC")
|
||||
instance.init(privateKey)
|
||||
instance.doPhase(publicKey, true)
|
||||
return instance.generateSecret().md5()
|
||||
}
|
||||
|
||||
actual fun constructPublicKey(key: ByteArray): ECDHPublicKey {
|
||||
return KeyFactory.getInstance("EC", "BC").generatePublic(X509EncodedKeySpec(key))
|
||||
}
|
||||
}
|
||||
|
||||
actual fun calculateShareKeyByPeerPublicKey(peerPublicKey: ECDHPublicKey): ByteArray {
|
||||
return calculateShareKey(keyPair.privateKey, peerPublicKey)
|
||||
}
|
||||
|
||||
actual override fun toString(): String {
|
||||
return "ECDH(keyPair=$keyPair)"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user