Add android compilation target

This commit is contained in:
Him188 2021-02-01 12:23:04 +08:00
parent 2a7f0a3a8f
commit bacfef584f
14 changed files with 535 additions and 241 deletions

View File

@ -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 }

View File

@ -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())
}

View File

@ -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()
)
}

View 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 }

View File

@ -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

View File

@ -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")

View File

@ -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
}

View File

@ -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
}

View File

@ -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()

View File

@ -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`)
}
}

View 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>

View File

@ -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")
}
}
}

View File

@ -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)"
}
}