mirror of
https://github.com/mamoe/mirai.git
synced 2024-12-26 00:20:10 +08:00
[build] Rewrite shadow relocation; fix dependency graph issues with Android, and improve build performance:
- Generate relocated JARs with classifier `relocated`, instead of replacing the output of `:jar` task. - Create `JvmRelocated` publications to publish relocated artifacts. - Patch Kotlin Metadata and Maven Pom for the added publication. - Updated deps test to be more strict
This commit is contained in:
parent
40d4957837
commit
178ca6c1b5
39
.run/Publish deps test artifacts.run.xml
Normal file
39
.run/Publish deps test artifacts.run.xml
Normal file
@ -0,0 +1,39 @@
|
||||
<!--
|
||||
~ Copyright 2019-2023 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/dev/LICENSE
|
||||
-->
|
||||
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Publish deps test artifacts" type="GradleRunConfiguration" factoryName="Gradle"
|
||||
folderName="Publishing Tests">
|
||||
<ExternalSystemSettings>
|
||||
<option name="env">
|
||||
<map>
|
||||
<entry key="mirai.build.project.version" value="2.99.0-deps-test"/>
|
||||
</map>
|
||||
</option>
|
||||
<option name="executionName"/>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$"/>
|
||||
<option name="externalSystemIdString" value="GRADLE"/>
|
||||
<option name="scriptParameters" value="--stacktrace"/>
|
||||
<option name="taskDescriptions">
|
||||
<list/>
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":mirai-deps-test:publishMiraiArtifactsToMavenLocal"/>
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions"/>
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<ForceTestExec>false</ForceTestExec>
|
||||
<method v="2"/>
|
||||
</configuration>
|
||||
</component>
|
@ -1,3 +1,12 @@
|
||||
<!--
|
||||
~ Copyright 2019-2023 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/dev/LICENSE
|
||||
-->
|
||||
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Publish local artifacts" type="GradleRunConfiguration" factoryName="Gradle" folderName="Build">
|
||||
<ExternalSystemSettings>
|
||||
@ -9,7 +18,7 @@
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="scriptParameters" value="--stacktrace"/>
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
@ -23,6 +32,7 @@
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<ForceTestExec>false</ForceTestExec>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
@ -11,6 +11,7 @@
|
||||
|
||||
import org.jetbrains.dokka.base.DokkaBase
|
||||
import org.jetbrains.dokka.base.DokkaBaseConfiguration
|
||||
import shadow.configureMppShadow
|
||||
import java.time.LocalDateTime
|
||||
|
||||
buildscript {
|
||||
@ -79,9 +80,6 @@ allprojects {
|
||||
substituteDependenciesUsingExpectedVersion()
|
||||
}
|
||||
}
|
||||
afterEvaluate {
|
||||
configureShadowDependenciesForPublishing()
|
||||
}
|
||||
|
||||
subprojects {
|
||||
afterEvaluate {
|
||||
|
@ -7,8 +7,6 @@
|
||||
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("UNUSED_VARIABLE")
|
||||
|
||||
import com.google.gradle.osdetector.OsDetector
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.attributes.Attribute
|
||||
@ -28,14 +26,14 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
val MIRAI_PLATFORM_ATTRIBUTE = Attribute.of(
|
||||
val MIRAI_PLATFORM_ATTRIBUTE: Attribute<String> = Attribute.of(
|
||||
"net.mamoe.mirai.platform", String::class.java
|
||||
)
|
||||
|
||||
/**
|
||||
* Flags a target as an HMPP intermediate target
|
||||
*/
|
||||
val MIRAI_PLATFORM_INTERMEDIATE = Attribute.of(
|
||||
val MIRAI_PLATFORM_INTERMEDIATE: Attribute<Boolean> = Attribute.of(
|
||||
"net.mamoe.mirai.platform.intermediate", Boolean::class.javaObjectType
|
||||
)
|
||||
|
||||
|
169
buildSrc/src/main/kotlin/KotlinMetadataPatcher.kt
Normal file
169
buildSrc/src/main/kotlin/KotlinMetadataPatcher.kt
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright 2019-2023 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/dev/LICENSE
|
||||
*/
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonPrimitive
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.gradle.api.publish.tasks.GenerateModuleMetadata
|
||||
import shadow.relocationFilters
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.security.MessageDigest
|
||||
|
||||
fun Project.configurePatchKotlinModuleMetadataTask(
|
||||
relocatedPublicationName: String,
|
||||
relocateDependencies: Task,
|
||||
originalPublicationName: String
|
||||
) {
|
||||
// We will modify Kotlin metadata, so do generate metadata before relocation
|
||||
val generateMetadataTask =
|
||||
tasks.getByName("generateMetadataFileFor${originalPublicationName.titlecase()}Publication") as GenerateModuleMetadata
|
||||
|
||||
publications.getByName(relocatedPublicationName) {
|
||||
this as MavenPublication
|
||||
this.artifact(generateMetadataTask.outputFile) {
|
||||
classifier = null
|
||||
extension = "module"
|
||||
}
|
||||
}
|
||||
|
||||
generateMetadataTask.dependsOn(relocateDependencies)
|
||||
val patchMetadataTask =
|
||||
tasks.create("patchMetadataFileFor${relocatedPublicationName.capitalize()}RelocatedPublication") {
|
||||
group = "mirai"
|
||||
generateMetadataTask.finalizedBy(this)
|
||||
dependsOn(generateMetadataTask)
|
||||
dependsOn(relocateDependencies)
|
||||
|
||||
// remove dependencies in Kotlin module metadata
|
||||
doLast {
|
||||
// mirai-core-jvm-2.13.0.module
|
||||
val file = generateMetadataTask.outputFile.asFile.get()
|
||||
val metadata = Gson().fromJson(
|
||||
file.readText(),
|
||||
JsonElement::class.java
|
||||
).asJsonObject
|
||||
|
||||
val metadataVersion = metadata["formatVersion"]?.asString
|
||||
check(metadataVersion == "1.1") {
|
||||
"Unsupported Kotlin metadata version. version=$metadataVersion, file=${file.absolutePath}"
|
||||
}
|
||||
for (variant in metadata["variants"]!!.asJsonArray) {
|
||||
patchKotlinMetadataVariant(variant, relocateDependencies.outputs.files.singleFile)
|
||||
}
|
||||
|
||||
|
||||
file.writeText(GsonBuilder().setPrettyPrinting().create().toJson(metadata))
|
||||
}
|
||||
}
|
||||
|
||||
// Set "publishKotlinMultiplatformPublicationTo*" and "publish${targetName.capitalize()}PublicationTo*" dependsOn patchMetadataTask
|
||||
if (project.kotlinMpp != null) {
|
||||
tasks.filter { it.name.startsWith("publishKotlinMultiplatformPublicationTo") }.let { publishTasks ->
|
||||
if (publishTasks.isEmpty()) {
|
||||
throw GradleException("[Shadow Relocation] Cannot find publishKotlinMultiplatformPublicationTo for project '${project.path}'.")
|
||||
}
|
||||
publishTasks.forEach { it.dependsOn(patchMetadataTask) }
|
||||
}
|
||||
|
||||
tasks.filter { it.name.startsWith("publish${relocatedPublicationName.capitalize()}PublicationTo") }
|
||||
.let { publishTasks ->
|
||||
if (publishTasks.isEmpty()) {
|
||||
throw GradleException("[Shadow Relocation] Cannot find publish${relocatedPublicationName.capitalize()}PublicationTo for project '${project.path}'.")
|
||||
}
|
||||
publishTasks.forEach { it.dependsOn(patchMetadataTask) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.patchKotlinMetadataVariant(variant: JsonElement, relocatedJar: File) {
|
||||
val dependencies = variant.asJsonObject["dependencies"]!!.asJsonArray
|
||||
dependencies.removeAll { dependency ->
|
||||
val dep = dependency.asJsonObject
|
||||
|
||||
val groupId = dep["group"]!!.asString
|
||||
val artifactId = dep["module"]!!.asString
|
||||
relocationFilters.any { filter ->
|
||||
filter.matchesDependency(
|
||||
groupId = groupId,
|
||||
artifactId = artifactId
|
||||
)
|
||||
}.also {
|
||||
println("[Shadow Relocation] Filtering out $groupId:$artifactId from Kotlin module")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
"files": [
|
||||
{
|
||||
"name": "mirai-core-jvm-2.99.0-local.jar",
|
||||
"url": "mirai-core-jvm-2.99.0-local.jar",
|
||||
"size": 14742378,
|
||||
"sha512": "7ab4afc88384a58687467ba13c6aefeda20fa53fd7759dc2bc78b2d46a6285f94ba6ccae426d192e7745f773401b3cb42a853e5445dc23bdcb1b5295e78ff71c",
|
||||
"sha256": "772f593bfb85a80794693d4d9dfe2f77c222cfe9ca7e0d571abaa320e7aa82d3",
|
||||
"sha1": "cb7937269d29b574725d6f28668847fd672de7cf",
|
||||
"md5": "3fca635ba5e55b7dd56c552e4ca01f7e"
|
||||
}
|
||||
]
|
||||
*/
|
||||
|
||||
val files = variant.asJsonObject["files"].asJsonArray
|
||||
val filesList = files.toList()
|
||||
files.removeAll { true }
|
||||
for (publishedFile0 in filesList) {
|
||||
val publishedFile = publishedFile0.asJsonObject
|
||||
|
||||
val name = publishedFile["name"].asJsonPrimitive.asString
|
||||
if (name.endsWith(".jar")) {
|
||||
logPublishing { "Patching Kotlin Metadata: file $name" }
|
||||
for (algorithm in ALGORITHMS) {
|
||||
publishedFile.add(algorithm, JsonPrimitive(relocatedJar.digest(algorithm)))
|
||||
}
|
||||
publishedFile.add("size", JsonPrimitive(relocatedJar.length()))
|
||||
} else {
|
||||
error("Unexpected file '$name' while patching Kotlin metadata")
|
||||
}
|
||||
|
||||
files.add(publishedFile)
|
||||
}
|
||||
}
|
||||
|
||||
private val ALGORITHMS = listOf("md5", "sha1", "sha256", "sha512")
|
||||
|
||||
fun File.digest(algorithm: String): String {
|
||||
val arr = inputStream().buffered().use { it.digest(algorithm) }
|
||||
return arr.toUHexString("").lowercase()
|
||||
}
|
||||
|
||||
fun InputStream.digest(algorithm: String): ByteArray {
|
||||
val digest = MessageDigest.getInstance(algorithm)
|
||||
digest.reset()
|
||||
use { input ->
|
||||
object : OutputStream() {
|
||||
override fun write(b: Int) {
|
||||
digest.update(b.toByte())
|
||||
}
|
||||
|
||||
override fun write(b: ByteArray, off: Int, len: Int) {
|
||||
digest.update(b, off, len)
|
||||
}
|
||||
}.use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
return digest.digest()
|
||||
}
|
||||
|
@ -15,9 +15,11 @@ import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.kotlin.dsl.register
|
||||
import shadow.RelocationConfig
|
||||
import shadow.relocationFilters
|
||||
|
||||
inline fun logPublishing(@Suppress("UNUSED_PARAMETER") message: () -> String) {
|
||||
// println("[Publishing] Configuring $message")
|
||||
inline fun Project.logPublishing(message: () -> String) {
|
||||
logger.debug("[Publishing] Configuring {}", message())
|
||||
}
|
||||
|
||||
fun Project.configureMppPublishing() {
|
||||
@ -45,7 +47,9 @@ fun Project.configureMppPublishing() {
|
||||
logPublishing { "Publications: ${publications.joinToString { it.name }}" }
|
||||
|
||||
val (nonJvmPublications, jvmPublications) = publications.filterIsInstance<MavenPublication>()
|
||||
.partition { publication -> tasks.findByName("relocate${publication.name.titlecase()}Dependencies") == null }
|
||||
.partition { publication ->
|
||||
tasks.findByName(RelocationConfig.taskNameForRelocateDependencies(publication.name)) == null
|
||||
}
|
||||
|
||||
for (publication in nonJvmPublications) {
|
||||
configureMultiplatformPublication(publication, stubJavadoc, publication.name)
|
||||
@ -76,7 +80,7 @@ fun Project.configureMppPublishing() {
|
||||
configureMultiplatformPublication(publication, stubJavadoc, publication.name)
|
||||
publication.apply {
|
||||
artifacts.filter { it.classifier.isNullOrEmpty() && it.extension == "jar" }.forEach {
|
||||
it.builtBy(tasks.findByName("relocate${publication.name.titlecase()}Dependencies"))
|
||||
it.builtBy(tasks.findByName(RelocationConfig.taskNameForRelocateDependencies(publication.name)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -108,6 +112,12 @@ private fun Project.configureMultiplatformPublication(
|
||||
publication.artifactId = "${project.name}-metadata"
|
||||
}
|
||||
|
||||
"jvm" -> {
|
||||
publication.artifactId = "${project.name}-$moduleName"
|
||||
|
||||
useRelocatedPublication(publication, moduleName)
|
||||
}
|
||||
|
||||
else -> {
|
||||
// "jvm", "native", "js", "common"
|
||||
publication.artifactId = "${project.name}-$moduleName"
|
||||
@ -115,6 +125,133 @@ private fun Project.configureMultiplatformPublication(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new publication and disables [publication].
|
||||
*/
|
||||
private fun Project.useRelocatedPublication(
|
||||
publication: MavenPublication,
|
||||
moduleName: String
|
||||
) {
|
||||
val relocatedPublicationName = RelocationConfig.relocatedPublicationName(publication.name)
|
||||
registerRelocatedPublication(relocatedPublicationName, publication, moduleName)
|
||||
|
||||
logPublishing { "Registered relocated publication `$relocatedPublicationName` for module $moduleName, for project ${project.path}" }
|
||||
|
||||
// Add task dependencies
|
||||
addTaskDependenciesForRelocatedPublication(moduleName, relocatedPublicationName)
|
||||
|
||||
val relocateDependencies = tasks.getByName(RelocationConfig.taskNameForRelocateDependencies(moduleName))
|
||||
|
||||
configurePatchKotlinModuleMetadataTask(relocatedPublicationName, relocateDependencies, publication.name)
|
||||
}
|
||||
|
||||
private fun Project.registerRelocatedPublication(
|
||||
relocatedPublicationName: String,
|
||||
publication: MavenPublication,
|
||||
moduleName: String
|
||||
) {
|
||||
// copy POM XML, since POM contains transitive dependencies
|
||||
|
||||
var patched = false
|
||||
|
||||
lateinit var oldXmlProvider: XmlProvider
|
||||
publication.pom.withXml { oldXmlProvider = this }
|
||||
|
||||
publications.register(relocatedPublicationName, MavenPublication::class.java) {
|
||||
this.artifactId = publication.artifactId
|
||||
this.groupId = publication.groupId
|
||||
this.version = publication.version
|
||||
this.artifacts.addAll(publication.artifacts.filterNot { it.classifier == null && it.extension == "jar" })
|
||||
|
||||
project.tasks.findByName(RelocationConfig.taskNameForRelocateDependencies(moduleName))
|
||||
?.let { relocateDependencies ->
|
||||
this.artifact(relocateDependencies) {
|
||||
this.classifier = null
|
||||
this.extension = "jar"
|
||||
}
|
||||
}
|
||||
|
||||
pom.withXml {
|
||||
val newXml = this
|
||||
for (newChild in newXml.asNode().childrenNodes()) {
|
||||
newXml.asNode().remove(newChild)
|
||||
}
|
||||
// Note: `withXml` is lazy, it is evaluated only when `generatePomFileFor...`
|
||||
for (oldChild in oldXmlProvider.asNode().childrenNodes()) {
|
||||
newXml.asNode().append(oldChild)
|
||||
}
|
||||
removeDependenciesInMavenPom(this)
|
||||
patched = true
|
||||
}
|
||||
}
|
||||
|
||||
tasks.matching { it.name.startsWith("publish${relocatedPublicationName.titlecase()}PublicationTo") }.all {
|
||||
dependsOn("generatePomFileFor${relocatedPublicationName.titlecase()}Publication")
|
||||
}
|
||||
|
||||
|
||||
tasks.matching { it.name == "generatePomFileFor${relocatedPublicationName.titlecase()}Publication" }.all {
|
||||
dependsOn(tasks.getByName("generatePomFileFor${publication.name.titlecase()}Publication"))
|
||||
doLast {
|
||||
check(patched) { "POM is not patched" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.addTaskDependenciesForRelocatedPublication(moduleName: String, relocatedPublicationName: String) {
|
||||
val originalTaskNamePrefix = "publish${moduleName.titlecase()}PublicationTo"
|
||||
val relocatedTaskName = "publish${relocatedPublicationName.titlecase()}PublicationTo"
|
||||
tasks.configureEach {
|
||||
if (!name.startsWith(originalTaskNamePrefix)) return@configureEach
|
||||
val originalTask = this
|
||||
|
||||
this.enabled = false
|
||||
this.description = "${this.description} ([mirai] disabled in favor of $relocatedTaskName)"
|
||||
|
||||
val relocatedTasks = project.tasks.filter { it.name.startsWith(relocatedTaskName) }.toTypedArray()
|
||||
check(relocatedTasks.isNotEmpty()) { "relocatedTasks is empty" }
|
||||
relocatedTasks.forEach { publishRelocatedPublication ->
|
||||
publishRelocatedPublication.dependsOn(*this.dependsOn.toTypedArray())
|
||||
logger.info(
|
||||
"[Publishing] $publishRelocatedPublication now dependsOn tasks: " +
|
||||
this.dependsOn.joinToString()
|
||||
)
|
||||
}
|
||||
|
||||
project.tasks.filter { it.dependsOn.contains(originalTask) }
|
||||
.forEach { it.dependsOn(*relocatedTasks) }
|
||||
}
|
||||
}
|
||||
|
||||
// Remove relocated dependencies in Maven pom
|
||||
private fun Project.removeDependenciesInMavenPom(xmlProvider: XmlProvider) {
|
||||
xmlProvider.run {
|
||||
val node = asNode().getSingleChild("dependencies")
|
||||
val dependencies = node.childrenNodes()
|
||||
logger.info("[Shadow Relocation] deps: {}", dependencies)
|
||||
logger.info(
|
||||
"[Shadow Relocation] All filter notations: {}",
|
||||
relocationFilters.flatMap { it.notations.notations() }.joinToString("\n")
|
||||
)
|
||||
|
||||
dependencies.forEach { dep ->
|
||||
val groupId = dep.getSingleChild("groupId").value().toString().removeSurrounding("[", "]")
|
||||
val artifactId = dep.getSingleChild("artifactId").value().toString().removeSurrounding("[", "]")
|
||||
logger.info("[Shadow Relocation] Checking $groupId:$artifactId")
|
||||
|
||||
if (
|
||||
relocationFilters.any { filter ->
|
||||
filter.matchesDependency(groupId = groupId, artifactId = artifactId)
|
||||
}
|
||||
) {
|
||||
logger.info("[Shadow Relocation] Filtering out '$groupId:$artifactId' from pom for project '${project.path}'")
|
||||
check(node.remove(dep)) { "Failed to remove dependency node" }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
val publishPlatformArtifactsInRootModule: Project.(MavenPublication) -> Unit = { platformPublication ->
|
||||
lateinit var platformPomBuilder: XmlProvider
|
||||
platformPublication.pom.withXml { platformPomBuilder = this }
|
||||
|
@ -1,292 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2023 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/dev/LICENSE
|
||||
*/
|
||||
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.execution.TaskExecutionGraph
|
||||
import org.gradle.api.publish.tasks.GenerateModuleMetadata
|
||||
import org.gradle.api.tasks.bundling.Jar
|
||||
import org.gradle.kotlin.dsl.DependencyHandlerScope
|
||||
import org.gradle.kotlin.dsl.create
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||
|
||||
/**
|
||||
* @see RelocationNotes
|
||||
*/
|
||||
fun Project.configureMppShadow() {
|
||||
val kotlin = kotlinMpp ?: return
|
||||
|
||||
configure(kotlin.targets.filter {
|
||||
it.platformType == org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.jvm
|
||||
&& (it.attributes.getAttribute(MIRAI_PLATFORM_INTERMEDIATE) != true)
|
||||
}) {
|
||||
configureRelocationForMppTarget(project)
|
||||
|
||||
registerRegularShadowTask(this, mapTaskNameForMultipleTargets = true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置 `publish` 和 `shadow` 相关依赖. 对于在本次构建的请求的任务及其直接或间接依赖, 以以下顺序执行:
|
||||
*
|
||||
* 1. 执行全部 `jar` 任务
|
||||
* 2. 执行全部 `relocate` 任务
|
||||
* 3. 执行全部 `publish` 任务
|
||||
*
|
||||
* 这是必要的因为 relocate 任务会覆盖 jar 任务的输出, 而在多模块并行编译时, Kotlin 编译器会依赖 jar 任务的输出. 如果在编译同时修改 JAR 文件, 就会导致 `ZipException`.
|
||||
*
|
||||
* 这也会让 publish 集中执行, Maven Central 不容易出问题.
|
||||
*/
|
||||
fun Project.configureShadowDependenciesForPublishing() {
|
||||
check(this.rootProject === this) {
|
||||
"configureShadowDependenciesForPublishing can only be used on root project."
|
||||
}
|
||||
|
||||
val jarTaskNames = arrayOf("jvmJar", "jvmBaseJar")
|
||||
gradle.projectsEvaluated {
|
||||
// Tasks requested to run in this build
|
||||
val allTasks = rootProject.allprojects.asSequence().flatMap { it.tasks }
|
||||
|
||||
val publishTasks = allTasks.filter { it.name.contains("publish", ignoreCase = true) }
|
||||
val relocateTasks = allTasks.filter { it.name.contains("relocate", ignoreCase = true) }
|
||||
val jarTasks = allTasks.filter {
|
||||
it.name in jarTaskNames
|
||||
}
|
||||
val compileKotlinTasks = allTasks.filter { it.name.contains("compileKotlin", ignoreCase = true) }
|
||||
val compileTestKotlinTasks = allTasks.filter { it.name.contains("compileTestKotlin", ignoreCase = true) }
|
||||
|
||||
relocateTasks.dependsOn(compileKotlinTasks.toList())
|
||||
relocateTasks.dependsOn(compileTestKotlinTasks.toList())
|
||||
relocateTasks.dependsOn(jarTasks.toList())
|
||||
publishTasks.dependsOn(relocateTasks.toList())
|
||||
}
|
||||
}
|
||||
|
||||
val TaskExecutionGraph.hierarchicalTasks: Sequence<Task>
|
||||
get() = sequence {
|
||||
suspend fun SequenceScope<Task>.addTask(task: Task) {
|
||||
yield(task)
|
||||
for (dependency in getDependencies(task)) {
|
||||
addTask(dependency)
|
||||
}
|
||||
}
|
||||
|
||||
for (task in allTasks) {
|
||||
addTask(task)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Relocate some dependencies for `.jar`
|
||||
* @see RelocationNotes
|
||||
*/
|
||||
private fun KotlinTarget.configureRelocationForMppTarget(project: Project) = project.run {
|
||||
val configuration = project.configurations.findByName(SHADOW_RELOCATION_CONFIGURATION_NAME)
|
||||
|
||||
// e.g. relocateJvmDependencies
|
||||
// do not change task name. see `configureShadowDependenciesForPublishing`
|
||||
val relocateDependencies = tasks.create("relocate${targetName.titlecase()}Dependencies", ShadowJar::class) {
|
||||
group = "mirai"
|
||||
description = "Relocate dependencies to internal package"
|
||||
destinationDirectory.set(buildDir.resolve("libs")) // build/libs
|
||||
archiveBaseName.set("${project.name}-${targetName.lowercase()}") // e.g. "mirai-core-api-jvm"
|
||||
|
||||
dependsOn(compilations["main"].compileTaskProvider) // e.g. compileKotlinJvm
|
||||
|
||||
from(compilations["main"].output) // Add compilation result of mirai sourcecode, not including dependencies
|
||||
configuration?.let {
|
||||
from(it) // Include runtime dependencies
|
||||
}
|
||||
|
||||
// Relocate packages
|
||||
afterEvaluate {
|
||||
val relocationFilters = project.relocationFilters
|
||||
relocationFilters.forEach { relocation ->
|
||||
relocation.packages.forEach { aPackage ->
|
||||
relocate(aPackage, "$RELOCATION_ROOT_PACKAGE.$aPackage")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We will modify Kotlin metadata, so do generate metadata before relocation
|
||||
val generateMetadataTask =
|
||||
tasks.getByName("generateMetadataFileFor${targetName.capitalize()}Publication") as GenerateModuleMetadata
|
||||
generateMetadataTask.dependsOn(relocateDependencies)
|
||||
|
||||
val patchMetadataTask = tasks.create("patchMetadataFileFor${targetName.capitalize()}RelocatedPublication") {
|
||||
dependsOn(generateMetadataTask)
|
||||
dependsOn(relocateDependencies)
|
||||
|
||||
// remove dependencies in Kotlin module metadata
|
||||
doLast {
|
||||
// mirai-core-jvm-2.13.0.module
|
||||
val file = generateMetadataTask.outputFile.asFile.get()
|
||||
val metadata = Gson().fromJson(
|
||||
file.readText(),
|
||||
com.google.gson.JsonElement::class.java
|
||||
).asJsonObject
|
||||
|
||||
val metadataVersion = metadata["formatVersion"]?.asString
|
||||
check(metadataVersion == "1.1") {
|
||||
"Unsupported Kotlin metadata version. version=$metadataVersion, file=${file.absolutePath}"
|
||||
}
|
||||
for (variant in metadata["variants"]!!.asJsonArray) {
|
||||
val dependencies = variant.asJsonObject["dependencies"]!!.asJsonArray
|
||||
dependencies.removeAll { dependency ->
|
||||
val dep = dependency.asJsonObject
|
||||
|
||||
val groupId = dep["group"]!!.asString
|
||||
val artifactId = dep["module"]!!.asString
|
||||
relocationFilters.any { filter ->
|
||||
filter.matchesDependency(
|
||||
groupId = groupId,
|
||||
artifactId = artifactId
|
||||
)
|
||||
}.also {
|
||||
println("[Shadow Relocation] Filtering out $groupId:$artifactId from Kotlin module")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
file.writeText(GsonBuilder().setPrettyPrinting().create().toJson(metadata))
|
||||
}
|
||||
}
|
||||
|
||||
// Set "publishKotlinMultiplatformPublicationTo*" and "publish${targetName.capitalize()}PublicationTo*" dependsOn patchMetadataTask
|
||||
if (project.kotlinMpp != null) {
|
||||
tasks.filter { it.name.startsWith("publishKotlinMultiplatformPublicationTo") }.let { publishTasks ->
|
||||
if (publishTasks.isEmpty()) {
|
||||
throw GradleException("[Shadow Relocation] Cannot find publishKotlinMultiplatformPublicationTo for project '${project.path}'.")
|
||||
}
|
||||
publishTasks.forEach { it.dependsOn(patchMetadataTask) }
|
||||
}
|
||||
|
||||
tasks.filter { it.name.startsWith("publish${targetName.capitalize()}PublicationTo") }.let { publishTasks ->
|
||||
if (publishTasks.isEmpty()) {
|
||||
throw GradleException("[Shadow Relocation] Cannot find publish${targetName.capitalize()}PublicationTo for project '${project.path}'.")
|
||||
}
|
||||
publishTasks.forEach { it.dependsOn(patchMetadataTask) }
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
// Remove relocated dependencies in Maven pom
|
||||
mavenPublication {
|
||||
pom.withXml {
|
||||
val node = this.asNode().getSingleChild("dependencies")
|
||||
val dependencies = node.childrenNodes()
|
||||
logger.trace("[Shadow Relocation] deps: $dependencies")
|
||||
dependencies.forEach { dep ->
|
||||
val groupId = dep.getSingleChild("groupId").value().toString()
|
||||
val artifactId = dep.getSingleChild("artifactId").value().toString()
|
||||
logger.trace("[Shadow Relocation] Checking $groupId:$artifactId")
|
||||
|
||||
if (
|
||||
relocationFilters.any { filter ->
|
||||
filter.matchesDependency(groupId = groupId, artifactId = artifactId)
|
||||
}
|
||||
) {
|
||||
logger.info("[Shadow Relocation] Filtering out '$groupId:$artifactId' from pom for project '${project.path}'")
|
||||
check(node.remove(dep)) { "Failed to remove dependency node" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Sequence<Task>.dependsOn(
|
||||
task: Task,
|
||||
) {
|
||||
return forEach { it.dependsOn(task) }
|
||||
}
|
||||
|
||||
private fun Sequence<Task>.dependsOn(
|
||||
tasks: Iterable<Task>,
|
||||
) {
|
||||
return forEach { it.dependsOn(tasks) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 `implementation` 和 `shadow`
|
||||
*/
|
||||
fun DependencyHandlerScope.shadowImplementation(dependencyNotation: Any) {
|
||||
"implementation"(dependencyNotation)
|
||||
"shadow"(dependencyNotation)
|
||||
}
|
||||
|
||||
fun Project.registerRegularShadowTaskForJvmProject(
|
||||
configurations: List<Configuration> = listOfNotNull(
|
||||
project.configurations.findByName("runtimeClasspath"),
|
||||
project.configurations.findByName("${kotlinJvm!!.target.name}RuntimeClasspath"),
|
||||
project.configurations.findByName("runtime")
|
||||
)
|
||||
): ShadowJar {
|
||||
return project.registerRegularShadowTask(kotlinJvm!!.target, mapTaskNameForMultipleTargets = false, configurations)
|
||||
}
|
||||
|
||||
fun Project.registerRegularShadowTask(
|
||||
target: KotlinTarget,
|
||||
mapTaskNameForMultipleTargets: Boolean,
|
||||
configurations: List<Configuration> = listOfNotNull(
|
||||
project.configurations.findByName("runtimeClasspath"),
|
||||
project.configurations.findByName("${target.targetName}RuntimeClasspath"),
|
||||
project.configurations.findByName("runtime")
|
||||
),
|
||||
): ShadowJar {
|
||||
return tasks.create(
|
||||
if (mapTaskNameForMultipleTargets) "shadow${target.targetName.capitalize()}Jar" else "shadowJar",
|
||||
ShadowJar::class
|
||||
) {
|
||||
group = "mirai"
|
||||
archiveClassifier.set("all")
|
||||
|
||||
(tasks.findByName("jar") as? Jar)?.let {
|
||||
manifest.inheritFrom(it.manifest)
|
||||
}
|
||||
|
||||
val compilation = target.compilations["main"]
|
||||
dependsOn(compilation.compileTaskProvider)
|
||||
from(compilation.output)
|
||||
|
||||
// components.findByName("java")?.let { from(it) }
|
||||
project.sourceSets.findByName("main")?.output?.let { from(it) } // for JVM projects
|
||||
this.configurations = configurations
|
||||
|
||||
// Relocate packages
|
||||
afterEvaluate {
|
||||
val relocationFilters = project.relocationFilters
|
||||
relocationFilters.forEach { relocation ->
|
||||
relocation.packages.forEach { aPackage ->
|
||||
relocate(aPackage, "$RELOCATION_ROOT_PACKAGE.$aPackage")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exclude { file ->
|
||||
file.name.endsWith(".sf", ignoreCase = true)
|
||||
}
|
||||
exclude("META-INF/INDEX.LIST", "META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA", "module-info.class")
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureRelocatedShadowJarForJvmProject(kotlin: KotlinJvmProjectExtension): ShadowJar {
|
||||
return registerRegularShadowTask(kotlin.target, mapTaskNameForMultipleTargets = false)
|
||||
}
|
||||
|
||||
const val RELOCATION_ROOT_PACKAGE = "net.mamoe.mirai.internal.deps"
|
@ -120,6 +120,13 @@ class RelocatedDependency(
|
||||
* Kotlin packages. e.g. `io.ktor`
|
||||
*/
|
||||
vararg val packages: String,
|
||||
/**
|
||||
* Exclude them, so no transitive dependencies exposed to Maven and Kotlin JVM consumers
|
||||
*/
|
||||
val notationsToExcludeInPom: RelocatableDependency = MultiplatformDependency.jvm(
|
||||
notation.substringBefore(":"),
|
||||
notation.substringAfter(":").substringBeforeLast(":")
|
||||
),
|
||||
/**
|
||||
* Additional exclusions apart from everything from `org.jetbrains.kotlin` and `org.jetbrains.kotlinx`.
|
||||
*/
|
||||
@ -142,14 +149,49 @@ fun KotlinDependencyHandler.implementationKotlinxIo(module: String) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DependencyNotation(
|
||||
val groupId: String,
|
||||
val artifactId: String,
|
||||
) {
|
||||
fun toMap(): Map<String, String> {
|
||||
return mapOf("group" to groupId, "module" to artifactId)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "$groupId:$artifactId"
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface RelocatableDependency {
|
||||
fun notations(): Sequence<DependencyNotation>
|
||||
}
|
||||
|
||||
class SinglePlatformDependency(
|
||||
val groupId: String,
|
||||
val artifactId: String
|
||||
) : RelocatableDependency {
|
||||
override fun notations(): Sequence<DependencyNotation> {
|
||||
return sequenceOf(DependencyNotation(groupId, artifactId))
|
||||
}
|
||||
}
|
||||
|
||||
class CompositeDependency(
|
||||
private val dependencies: List<RelocatableDependency>
|
||||
) : RelocatableDependency {
|
||||
constructor(vararg dependencies: RelocatableDependency) : this(dependencies.toList())
|
||||
|
||||
override fun notations(): Sequence<DependencyNotation> = dependencies.asSequence().flatMap { it.notations() }
|
||||
}
|
||||
|
||||
class MultiplatformDependency private constructor(
|
||||
private val groupId: String,
|
||||
private val baseArtifactId: String,
|
||||
vararg val targets: String,
|
||||
) {
|
||||
fun notations(): Sequence<Map<String, String>> {
|
||||
return sequenceOf(mapOf("group" to groupId, "module" to baseArtifactId))
|
||||
.plus(targets.asSequence().map { mapOf("group" to groupId, "module" to "$baseArtifactId.$it") })
|
||||
) : RelocatableDependency {
|
||||
override fun notations(): Sequence<DependencyNotation> {
|
||||
return sequenceOf(DependencyNotation(groupId, baseArtifactId))
|
||||
.plus(targets.asSequence().map { DependencyNotation(groupId, "$baseArtifactId-$it") })
|
||||
}
|
||||
|
||||
companion object {
|
||||
@ -161,7 +203,7 @@ class MultiplatformDependency private constructor(
|
||||
|
||||
fun ModuleDependency.exclude(multiplatformDependency: MultiplatformDependency) {
|
||||
multiplatformDependency.notations().forEach {
|
||||
exclude(it)
|
||||
exclude(it.toMap())
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,7 +233,10 @@ object ExcludeProperties {
|
||||
}
|
||||
|
||||
val `ktor-io` = ktor("io", Versions.ktor)
|
||||
val `ktor-io_relocated` = RelocatedDependency(`ktor-io`, "io.ktor.utils.io") {
|
||||
val `ktor-io_relocated` = RelocatedDependency(
|
||||
`ktor-io`, "io.ktor.utils.io",
|
||||
notationsToExcludeInPom = MultiplatformDependency.jvm("io.ktor", "ktor-io")
|
||||
) {
|
||||
exclude(ExcludeProperties.`everything from slf4j`)
|
||||
exclude(ExcludeProperties.`slf4j-api`)
|
||||
}
|
||||
@ -202,7 +247,16 @@ val `ktor-serialization` = ktor("serialization", Versions.ktor)
|
||||
val `ktor-websocket-serialization` = ktor("websocket-serialization", Versions.ktor)
|
||||
|
||||
val `ktor-client-core` = ktor("client-core", Versions.ktor)
|
||||
val `ktor-client-core_relocated` = RelocatedDependency(`ktor-client-core`, "io.ktor") {
|
||||
val `ktor-client-core_relocated` = RelocatedDependency(
|
||||
`ktor-client-core`, "io.ktor",
|
||||
notationsToExcludeInPom = CompositeDependency(
|
||||
MultiplatformDependency.jvm("io.ktor", "ktor-io"),
|
||||
MultiplatformDependency.jvm("io.ktor", "ktor-client-core"),
|
||||
MultiplatformDependency.jvm("io.ktor", "ktor-client-okhttp"),
|
||||
MultiplatformDependency.jvm("io.ktor", "ktor-http"),
|
||||
MultiplatformDependency.jvm("io.ktor", "ktor-utils"),
|
||||
)
|
||||
) {
|
||||
exclude(ExcludeProperties.`ktor-io`)
|
||||
exclude(ExcludeProperties.`everything from slf4j`)
|
||||
}
|
||||
@ -213,7 +267,21 @@ val `ktor-client-curl` = ktor("client-curl", Versions.ktor)
|
||||
val `ktor-client-darwin` = ktor("client-darwin", Versions.ktor)
|
||||
val `ktor-client-okhttp` = ktor("client-okhttp", Versions.ktor)
|
||||
val `ktor-client-okhttp_relocated` =
|
||||
RelocatedDependency(ktor("client-okhttp", Versions.ktor), "io.ktor", "okhttp", "okio") {
|
||||
RelocatedDependency(
|
||||
ktor("client-okhttp", Versions.ktor), "io.ktor", "okhttp", "okio",
|
||||
notationsToExcludeInPom = CompositeDependency(
|
||||
MultiplatformDependency.jvm("io.ktor", "ktor-io"),
|
||||
MultiplatformDependency.jvm("io.ktor", "ktor-client-core"),
|
||||
MultiplatformDependency.jvm("io.ktor", "ktor-client-okhttp"),
|
||||
MultiplatformDependency.jvm("io.ktor", "ktor-http"),
|
||||
MultiplatformDependency.jvm("io.ktor", "ktor-serialization"),
|
||||
MultiplatformDependency.jvm("io.ktor", "ktor-utils"),
|
||||
MultiplatformDependency.jvm("io.ktor", "ktor-websockets"),
|
||||
MultiplatformDependency.jvm("io.ktor", "ktor-websockets-serialization"),
|
||||
MultiplatformDependency.jvm("com.squareup.okhttp3", "okhttp3"),
|
||||
MultiplatformDependency.jvm("com.squareup.okio", "okio"),
|
||||
)
|
||||
) {
|
||||
exclude(ExcludeProperties.`ktor-io`)
|
||||
exclude(ExcludeProperties.`everything from slf4j`)
|
||||
}
|
||||
|
@ -7,6 +7,11 @@
|
||||
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||
*/
|
||||
|
||||
package shadow
|
||||
|
||||
import ExcludeProperties
|
||||
import RelocatableDependency
|
||||
import RelocatedDependency
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.DomainObjectCollection
|
||||
import org.gradle.api.Project
|
||||
@ -66,7 +71,7 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
|
||||
*
|
||||
* 如果你都使用 [relocateImplementation], 就会导致在 Android 平台发生 'Duplicated Class' 问题. 如果你都使用 [relocateCompileOnly] 则会在 clinit 阶段遇到 [NoClassDefFoundError]
|
||||
*
|
||||
* ## relocation 发生的时机晚于编译
|
||||
* ## relocation 发生的时机晚于编译 (Jar)
|
||||
*
|
||||
* mirai-core-utils relocate 了 ktor-io, 然后 mirai-core 在 `build.gradle.kts` 使用了 `implementation(project(":mirai-core-utils"))`.
|
||||
* 在 mirai-core 编译时, 编译器仍然会使用 relocate 之前的 `io.ktor`. 为了在 mirai-core 将对 `io.ktor` 的调用转为对 `net.mamoe.mirai.internal.deps.io.ktor` 的调用, 需要配置 relocation.
|
||||
@ -74,6 +79,13 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler
|
||||
*
|
||||
* 所以你需要为所有依赖了 mirai-core-utils 的模块都分别配置 [relocateCompileOnly].
|
||||
*
|
||||
* ## relocation 仅在发布 (e.g. `publishToMavenLocal`) 时自动使用
|
||||
*
|
||||
* 其他任何时候, 比如在 mirai-console 编译时, mirai-console 依赖的是未 relocate 的 JAR. 使用 `jar` 任务打包的也是未 relocate 的.
|
||||
*
|
||||
* 若需要 relocated 的 JAR, 使用 `relocateJvmDependencies`. 其中 `Jvm` 可换为其他启动了 relocate 的 Kotlin target 名.
|
||||
* 可在 IDEA Gradle 视图中找到 mirai 文件夹, 查看可用的 task 列表.
|
||||
*
|
||||
* ### "在运行时包含" 是如何实现的?
|
||||
*
|
||||
* 被 relocate 的类会被直接当做是当前模块的类打包进 JAR.
|
||||
@ -89,7 +101,7 @@ object RelocationNotes
|
||||
/**
|
||||
* 添加一个通常的 [compileOnly][KotlinDependencyHandler.compileOnly] 依赖, 并按 [relocatedDependency] 定义的配置 relocate.
|
||||
*
|
||||
* 在发布版本时, 全部对 [RelocatedDependency.packages] 中的 API 的调用**都不会**被 relocate 到 [RELOCATION_ROOT_PACKAGE].
|
||||
* 在发布版本时, 全部对 [RelocatedDependency.packages] 中的 API 的调用**都不会**被 relocate 到 [RelocationConfig.RELOCATION_ROOT_PACKAGE].
|
||||
* 运行时 (runtime) **不会**包含被 relocate 的依赖及其所有间接依赖.
|
||||
*
|
||||
* @see RelocationNotes
|
||||
@ -102,7 +114,9 @@ fun KotlinDependencyHandler.relocateCompileOnly(
|
||||
}
|
||||
project.relocationFilters.add(
|
||||
RelocationFilter(
|
||||
dependency.groupNotNull, dependency.name, relocatedDependency.packages.toList(), includeInRuntime = false,
|
||||
relocatedDependency.notationsToExcludeInPom,
|
||||
relocatedDependency.packages.toList(),
|
||||
includeInRuntime = false,
|
||||
)
|
||||
)
|
||||
// Don't add to runtime
|
||||
@ -112,7 +126,7 @@ fun KotlinDependencyHandler.relocateCompileOnly(
|
||||
/**
|
||||
* 添加一个通常的 [compileOnly][KotlinDependencyHandler.compileOnly] 依赖, 并按 [relocatedDependency] 定义的配置 relocate.
|
||||
*
|
||||
* 在发布版本时, 全部对 [RelocatedDependency.packages] 中的 API 的调用**都不会**被 relocate 到 [RELOCATION_ROOT_PACKAGE].
|
||||
* 在发布版本时, 全部对 [RelocatedDependency.packages] 中的 API 的调用**都不会**被 relocate 到 [RelocationConfig.RELOCATION_ROOT_PACKAGE].
|
||||
* 运行时 (runtime) **不会**包含被 relocate 的依赖及其所有间接依赖.
|
||||
*
|
||||
* @see RelocationNotes
|
||||
@ -127,7 +141,9 @@ fun DependencyHandler.relocateCompileOnly(
|
||||
})
|
||||
project.relocationFilters.add(
|
||||
RelocationFilter(
|
||||
dependency.groupNotNull, dependency.name, relocatedDependency.packages.toList(), includeInRuntime = false,
|
||||
relocatedDependency.notationsToExcludeInPom,
|
||||
relocatedDependency.packages.toList(),
|
||||
includeInRuntime = false,
|
||||
)
|
||||
)
|
||||
// Don't add to runtime
|
||||
@ -137,7 +153,7 @@ fun DependencyHandler.relocateCompileOnly(
|
||||
/**
|
||||
* 添加一个通常的 [implementation][KotlinDependencyHandler.implementation] 依赖, 并按 [relocatedDependency] 定义的配置 relocate.
|
||||
*
|
||||
* 在发布版本时, 全部对 [RelocatedDependency.packages] 中的 API 的调用**都会**被 relocate 到 [RELOCATION_ROOT_PACKAGE].
|
||||
* 在发布版本时, 全部对 [RelocatedDependency.packages] 中的 API 的调用**都会**被 relocate 到 [RelocationConfig.RELOCATION_ROOT_PACKAGE].
|
||||
* 运行时 (runtime) 将**会**包含被 relocate 的依赖及其所有间接依赖.
|
||||
*
|
||||
* @see RelocationNotes
|
||||
@ -151,13 +167,14 @@ fun KotlinDependencyHandler.relocateImplementation(
|
||||
}
|
||||
project.relocationFilters.add(
|
||||
RelocationFilter(
|
||||
dependency.groupNotNull, dependency.name, relocatedDependency.packages.toList(), includeInRuntime = true,
|
||||
relocatedDependency.notationsToExcludeInPom, relocatedDependency.packages.toList(), includeInRuntime = true,
|
||||
)
|
||||
)
|
||||
project.configurations.maybeCreate(SHADOW_RELOCATION_CONFIGURATION_NAME)
|
||||
val configurationName = RelocationConfig.SHADOW_RELOCATION_CONFIGURATION_NAME
|
||||
project.configurations.maybeCreate(configurationName)
|
||||
addDependencyTo(
|
||||
project.dependencies,
|
||||
SHADOW_RELOCATION_CONFIGURATION_NAME,
|
||||
configurationName,
|
||||
relocatedDependency.notation,
|
||||
Action<ExternalModuleDependency> {
|
||||
relocatedDependency.exclusionAction(this)
|
||||
@ -171,7 +188,7 @@ fun KotlinDependencyHandler.relocateImplementation(
|
||||
/**
|
||||
* 添加一个通常的 [implementation][KotlinDependencyHandler.implementation] 依赖, 并按 [relocatedDependency] 定义的配置 relocate.
|
||||
*
|
||||
* 在发布版本时, 全部对 [RelocatedDependency.packages] 中的 API 的调用都会被 relocate 到 [RELOCATION_ROOT_PACKAGE].
|
||||
* 在发布版本时, 全部对 [RelocatedDependency.packages] 中的 API 的调用都会被 relocate 到 [RelocationConfig.RELOCATION_ROOT_PACKAGE].
|
||||
* 运行时 (runtime) 将会包含被 relocate 的依赖及其所有间接依赖.
|
||||
*
|
||||
* @see RelocationNotes
|
||||
@ -187,13 +204,14 @@ fun DependencyHandler.relocateImplementation(
|
||||
})
|
||||
project.relocationFilters.add(
|
||||
RelocationFilter(
|
||||
dependency.groupNotNull, dependency.name, relocatedDependency.packages.toList(), includeInRuntime = true,
|
||||
relocatedDependency.notationsToExcludeInPom, relocatedDependency.packages.toList(), includeInRuntime = true,
|
||||
)
|
||||
)
|
||||
project.configurations.maybeCreate(SHADOW_RELOCATION_CONFIGURATION_NAME)
|
||||
val configurationName = RelocationConfig.SHADOW_RELOCATION_CONFIGURATION_NAME
|
||||
project.configurations.maybeCreate(configurationName)
|
||||
addDependencyTo(
|
||||
project.dependencies,
|
||||
SHADOW_RELOCATION_CONFIGURATION_NAME,
|
||||
configurationName,
|
||||
relocatedDependency.notation,
|
||||
Action<ExternalModuleDependency> {
|
||||
relocatedDependency.exclusionAction(this)
|
||||
@ -204,8 +222,7 @@ fun DependencyHandler.relocateImplementation(
|
||||
return dependency
|
||||
}
|
||||
|
||||
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION") // compiler bug
|
||||
private val ExternalModuleDependency.groupNotNull get() = group!!
|
||||
private val ExternalModuleDependency.groupNotNull: String get() = group.toString()
|
||||
|
||||
private fun ExternalModuleDependency.intrinsicExclusions() {
|
||||
exclude(ExcludeProperties.`everything from kotlin`)
|
||||
@ -213,14 +230,10 @@ private fun ExternalModuleDependency.intrinsicExclusions() {
|
||||
}
|
||||
|
||||
|
||||
const val SHADOW_RELOCATION_CONFIGURATION_NAME = "shadowRelocation"
|
||||
|
||||
|
||||
data class RelocationFilter(
|
||||
val groupId: String,
|
||||
val artifactId: String? = null,
|
||||
val packages: List<String> = listOf(groupId),
|
||||
val filesFilter: String = groupId.replace(".", "/"),
|
||||
val notations: RelocatableDependency,
|
||||
val packages: List<String>,
|
||||
// val filesFilter: String = groupId.replace(".", "/"),
|
||||
/**
|
||||
* Pack relocated dependency into the fat jar. If set to `false`, dependencies will be removed.
|
||||
* This is to avoid duplicated classes. See #2291.
|
||||
@ -229,10 +242,9 @@ data class RelocationFilter(
|
||||
) {
|
||||
|
||||
fun matchesDependency(groupId: String?, artifactId: String?): Boolean {
|
||||
if (this.groupId == groupId) return true
|
||||
if (this.artifactId != null && this.artifactId == artifactId) return true
|
||||
|
||||
return false
|
||||
return notations.notations().any {
|
||||
it.groupId == groupId && it.artifactId == artifactId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
24
buildSrc/src/main/kotlin/shadow/RelocationConfig.kt
Normal file
24
buildSrc/src/main/kotlin/shadow/RelocationConfig.kt
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2019-2023 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/dev/LICENSE
|
||||
*/
|
||||
|
||||
package shadow
|
||||
|
||||
import titlecase
|
||||
|
||||
object RelocationConfig {
|
||||
const val RELOCATION_ROOT_PACKAGE = "net.mamoe.mirai.internal.deps"
|
||||
|
||||
const val SHADOW_RELOCATION_CONFIGURATION_NAME = "shadowRelocation"
|
||||
|
||||
fun taskNameForRelocateDependencies(
|
||||
targetName: String
|
||||
) = "relocate${targetName.titlecase()}Dependencies"
|
||||
|
||||
fun relocatedPublicationName(originalPublicationName: String): String = originalPublicationName + "Relocated"
|
||||
}
|
145
buildSrc/src/main/kotlin/shadow/Shadow.kt
Normal file
145
buildSrc/src/main/kotlin/shadow/Shadow.kt
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2019-2023 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/dev/LICENSE
|
||||
*/
|
||||
|
||||
package shadow
|
||||
|
||||
import MIRAI_PLATFORM_INTERMEDIATE
|
||||
import capitalize
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import kotlinJvm
|
||||
import kotlinMpp
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.tasks.bundling.Jar
|
||||
import org.gradle.kotlin.dsl.DependencyHandlerScope
|
||||
import org.gradle.kotlin.dsl.create
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||
import sourceSets
|
||||
|
||||
/**
|
||||
* @see RelocationNotes
|
||||
*/
|
||||
fun Project.configureMppShadow() {
|
||||
val kotlin = kotlinMpp ?: return
|
||||
|
||||
configure(kotlin.targets.filter {
|
||||
it.platformType == KotlinPlatformType.jvm
|
||||
&& (it.attributes.getAttribute(MIRAI_PLATFORM_INTERMEDIATE) != true)
|
||||
}) {
|
||||
configureRelocationForMppTarget(project)
|
||||
|
||||
registerRegularShadowTask(this, mapTaskNameForMultipleTargets = true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Relocate some dependencies for `.jar`
|
||||
* @see RelocationNotes
|
||||
*/
|
||||
private fun KotlinTarget.configureRelocationForMppTarget(project: Project) = project.run {
|
||||
val configuration = project.configurations.findByName(RelocationConfig.SHADOW_RELOCATION_CONFIGURATION_NAME)
|
||||
|
||||
// e.g. relocateJvmDependencies
|
||||
// do not change task name. see `configureShadowDependenciesForPublishing`
|
||||
val relocateDependenciesName = RelocationConfig.taskNameForRelocateDependencies(targetName)
|
||||
tasks.create(relocateDependenciesName, ShadowJar::class) {
|
||||
group = "mirai"
|
||||
description = "Relocate dependencies to internal package"
|
||||
destinationDirectory.set(buildDir.resolve("libs")) // build/libs
|
||||
archiveBaseName.set("${project.name}-${targetName.lowercase()}-relocated") // e.g. "mirai-core-api-jvm"
|
||||
|
||||
dependsOn(compilations["main"].compileTaskProvider) // e.g. compileKotlinJvm
|
||||
|
||||
from(compilations["main"].output) // Add the compilation result of mirai sourcecode, not including dependencies
|
||||
configuration?.let {
|
||||
from(it) // Include runtime dependencies
|
||||
}
|
||||
|
||||
// Relocate packages
|
||||
afterEvaluate {
|
||||
val relocationFilters = project.relocationFilters
|
||||
relocationFilters.forEach { relocation ->
|
||||
relocation.packages.forEach { aPackage ->
|
||||
relocate(aPackage, "${RelocationConfig.RELOCATION_ROOT_PACKAGE}.$aPackage")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 `implementation` 和 `shadow`
|
||||
*/
|
||||
fun DependencyHandlerScope.shadowImplementation(dependencyNotation: Any) {
|
||||
"implementation"(dependencyNotation)
|
||||
"shadow"(dependencyNotation)
|
||||
}
|
||||
|
||||
fun Project.registerRegularShadowTaskForJvmProject(
|
||||
configurations: List<Configuration> = listOfNotNull(
|
||||
project.configurations.findByName("runtimeClasspath"),
|
||||
project.configurations.findByName("${kotlinJvm!!.target.name}RuntimeClasspath"),
|
||||
project.configurations.findByName("runtime")
|
||||
)
|
||||
): ShadowJar {
|
||||
return project.registerRegularShadowTask(kotlinJvm!!.target, mapTaskNameForMultipleTargets = false, configurations)
|
||||
}
|
||||
|
||||
fun Project.registerRegularShadowTask(
|
||||
target: KotlinTarget,
|
||||
mapTaskNameForMultipleTargets: Boolean,
|
||||
configurations: List<Configuration> = listOfNotNull(
|
||||
project.configurations.findByName("runtimeClasspath"),
|
||||
project.configurations.findByName("${target.targetName}RuntimeClasspath"),
|
||||
project.configurations.findByName("runtime")
|
||||
),
|
||||
): ShadowJar {
|
||||
return tasks.create(
|
||||
if (mapTaskNameForMultipleTargets) "shadow${target.targetName.capitalize()}Jar" else "shadowJar",
|
||||
ShadowJar::class
|
||||
) {
|
||||
group = "mirai"
|
||||
archiveClassifier.set("all")
|
||||
|
||||
(tasks.findByName("jar") as? Jar)?.let {
|
||||
manifest.inheritFrom(it.manifest)
|
||||
}
|
||||
|
||||
val compilation = target.compilations["main"]
|
||||
dependsOn(compilation.compileTaskProvider)
|
||||
from(compilation.output)
|
||||
|
||||
// components.findByName("java")?.let { from(it) }
|
||||
project.sourceSets.findByName("main")?.output?.let { from(it) } // for JVM projects
|
||||
this.configurations = configurations
|
||||
|
||||
// Relocate packages
|
||||
afterEvaluate {
|
||||
val relocationFilters = project.relocationFilters
|
||||
relocationFilters.forEach { relocation ->
|
||||
relocation.packages.forEach { aPackage ->
|
||||
relocate(aPackage, "${RelocationConfig.RELOCATION_ROOT_PACKAGE}.$aPackage")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exclude { file ->
|
||||
file.name.endsWith(".sf", ignoreCase = true)
|
||||
}
|
||||
exclude("META-INF/INDEX.LIST", "META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA", "module-info.class")
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureRelocatedShadowJarForJvmProject(kotlin: KotlinJvmProjectExtension): ShadowJar {
|
||||
return registerRegularShadowTask(kotlin.target, mapTaskNameForMultipleTargets = false)
|
||||
}
|
||||
|
@ -7,6 +7,9 @@
|
||||
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||
*/
|
||||
|
||||
import shadow.registerRegularShadowTaskForJvmProject
|
||||
import shadow.shadowImplementation
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.serialization")
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2022 Mamoe Technologies and contributors.
|
||||
* Copyright 2019-2023 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.
|
||||
@ -9,6 +9,9 @@
|
||||
|
||||
@file:Suppress("UnusedImport")
|
||||
|
||||
import shadow.configureRelocatedShadowJarForJvmProject
|
||||
import shadow.relocateImplementation
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.serialization")
|
||||
@ -34,7 +37,7 @@ dependencies {
|
||||
val shadow = configureRelocatedShadowJarForJvmProject(kotlin)
|
||||
|
||||
if (System.getenv("MIRAI_IS_SNAPSHOTS_PUBLISHING")?.toBoolean() != true) {
|
||||
// Do not publish -all jars to snapshot server since they are too large.
|
||||
// Do not publish `-all` jars to snapshot server since they are too large.
|
||||
|
||||
configurePublishing("mirai-core-all", addShadowJar = false)
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
@file:Suppress("UNUSED_VARIABLE")
|
||||
|
||||
import BinaryCompatibilityConfigurator.configureBinaryValidators
|
||||
import shadow.relocateCompileOnly
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
|
@ -9,6 +9,8 @@
|
||||
|
||||
@file:Suppress("UNUSED_VARIABLE")
|
||||
|
||||
import shadow.relocateImplementation
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
kotlin("multiplatform")
|
||||
@ -98,7 +100,7 @@ if (tasks.findByName("androidMainClasses") != null) {
|
||||
tasks.getByName("androidBaseTest").dependsOn("checkAndroidApiLevel")
|
||||
}
|
||||
|
||||
//configureMppPublishing()
|
||||
configureMppPublishing()
|
||||
|
||||
//mavenCentralPublish {
|
||||
// artifactId = "mirai-core-utils"
|
||||
|
@ -12,6 +12,8 @@
|
||||
import BinaryCompatibilityConfigurator.configureBinaryValidators
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractNativeLibrary
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
||||
import shadow.relocateCompileOnly
|
||||
import shadow.relocateImplementation
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
@ -49,6 +51,9 @@ kotlin {
|
||||
implementation(`kotlinx-serialization-protobuf`)
|
||||
implementation(`kotlinx-atomicfu`)
|
||||
|
||||
// runtime from mirai-core-utils
|
||||
relocateCompileOnly(`ktor-io_relocated`)
|
||||
|
||||
// relocateImplementation(`ktor-http_relocated`)
|
||||
// relocateImplementation(`ktor-serialization_relocated`)
|
||||
// relocateImplementation(`ktor-websocket-serialization_relocated`)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2022 Mamoe Technologies and contributors.
|
||||
* Copyright 2019-2023 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.
|
||||
@ -48,16 +48,10 @@ val publishMiraiArtifactsToMavenLocal by tasks.registering {
|
||||
// Always print this very important message
|
||||
logger.warn("[publishMiraiArtifactsToMavenLocal] Project version is '${project.version}'.")
|
||||
}
|
||||
}
|
||||
|
||||
doLast {
|
||||
// delete shadowed Jars, since Kotlin can't compile modules that depend on them.
|
||||
rootProject.subprojects
|
||||
.asSequence()
|
||||
.flatMap { proj -> proj.tasks.filter { task -> task.name.contains("relocate") } }
|
||||
.flatMap { it.outputs.files }
|
||||
.filter { it.isFile && it.name.endsWith(".jar") }
|
||||
.forEach { it.delete() }
|
||||
}
|
||||
tasks.getByName("test") {
|
||||
mustRunAfter(publishMiraiArtifactsToMavenLocal)
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2022 Mamoe Technologies and contributors.
|
||||
* Copyright 2019-2023 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.
|
||||
@ -33,6 +33,9 @@ abstract class AbstractTest {
|
||||
|
||||
@JvmStatic
|
||||
fun isMiraiLocalAvailable(): Boolean {
|
||||
val commandLine =
|
||||
"""./gradlew publishMiraiArtifactsToMavenLocal -Dmirai.build.project.version=$miraiLocalVersion"""
|
||||
|
||||
return if (mavenLocalDir.resolve("net/mamoe/mirai-core/$miraiLocalVersion").exists()) {
|
||||
println(
|
||||
"""
|
||||
@ -41,7 +44,8 @@ abstract class AbstractTest {
|
||||
- added/removed a dependency for mirai-core series modules
|
||||
- changed version of any of the dependencies for mirai-core series modules
|
||||
|
||||
You can update by running `./gradlew publishMiraiLocalArtifacts`.
|
||||
You can update by running the following command:
|
||||
$commandLine
|
||||
""".trimIndent()
|
||||
)
|
||||
true
|
||||
@ -58,7 +62,9 @@ abstract class AbstractTest {
|
||||
Note that you can ignore this test if you did not change project (dependency) structure.
|
||||
And you don't need to worry if you does not run this test — this test is always executed on the CI when you make a PR.
|
||||
|
||||
You can run `./gradlew publishMiraiLocalArtifacts` to publish local artifacts.
|
||||
You can run the following command to publish local artifacts:
|
||||
$commandLine
|
||||
|
||||
Then you can run this test again. (By your original way or ./gradlew :mirai-deps-test:test)
|
||||
""".trimIndent()
|
||||
System.err.println(
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2022 Mamoe Technologies and contributors.
|
||||
* Copyright 2019-2023 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.
|
||||
@ -117,8 +117,8 @@ class CoreShadowRelocationTest : AbstractTest() {
|
||||
"""
|
||||
dependencies {
|
||||
implementation("net.mamoe:mirai-core:$miraiLocalVersion") {
|
||||
exclude("net.mamoe", "mirai-core-api")
|
||||
exclude("net.mamoe", "mirai-core-utils")
|
||||
exclude("net.mamoe", "mirai-core-api-jvm")
|
||||
exclude("net.mamoe", "mirai-core-utils-jvm")
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
@ -130,6 +130,7 @@ class CoreShadowRelocationTest : AbstractTest() {
|
||||
@EnabledIf("isMiraiLocalAvailable", disabledReason = REASON_LOCAL_ARTIFACT_NOT_AVAILABLE)
|
||||
fun `test mirai-core-api without transitive mirai-core-utils`() {
|
||||
val fragment = buildTestCases {
|
||||
-`mirai-core-utils`
|
||||
-both(`ktor-io`)
|
||||
-both(`ktor-client-core`)
|
||||
-both(`ktor-client-okhttp`)
|
||||
@ -143,7 +144,7 @@ class CoreShadowRelocationTest : AbstractTest() {
|
||||
"""
|
||||
dependencies {
|
||||
implementation("net.mamoe:mirai-core-api:$miraiLocalVersion") {
|
||||
exclude("net.mamoe", "mirai-core-utils")
|
||||
exclude("net.mamoe", "mirai-core-utils-jvm")
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
@ -233,6 +234,7 @@ class CoreShadowRelocationTest : AbstractTest() {
|
||||
val relocated: (FunctionTestCase.() -> FunctionTestCase)? = null,
|
||||
)
|
||||
|
||||
val `mirai-core-utils` = ClassTestCase("mirai-core-utils Symbol", "net.mamoe.mirai.utils.Symbol")
|
||||
val `ktor-io` = ClassTestCase("ktor-io ByteBufferChannel", ByteBufferChannel)
|
||||
val `ktor-client-core` = ClassTestCase("ktor-client-core HttpClient", HttpClient)
|
||||
val `ktor-client-okhttp` = ClassTestCase("ktor-client-core OkHttp", KtorOkHttp)
|
||||
@ -302,7 +304,7 @@ class CoreShadowRelocationTest : AbstractTest() {
|
||||
result.append(
|
||||
"""
|
||||
@Test
|
||||
fun `no relocated ${name}`() {
|
||||
fun `no ${name}`() {
|
||||
assertThrows<ClassNotFoundException> { Class.forName("$qualifiedClassName") }
|
||||
}
|
||||
""".trimIndent()
|
||||
|
Loading…
Reference in New Issue
Block a user