Fix gradle multi-projects packaging error; fix #1973

This commit is contained in:
Karlatemp 2022-04-22 23:46:54 +08:00
parent fbadf4a256
commit 7f3b67ad9e
No known key found for this signature in database
GPG Key ID: C6B606FF23D8FED7
4 changed files with 231 additions and 13 deletions

View File

@ -70,6 +70,20 @@ dependencies {
}
```
特别的, 如果使用了子项目 (Gradle MultiProjects), Mirai Console Gradle 默认也会打包进 JAR.
如果您希望 Mirai Console Gradle 像处理一般依赖一样处理 Gradle 子项目, 请使用以下配置告知
```groovy
dependencies {
implementation project(":nested")
asNormalDep project(":nested")
// build.gradle.kts
"asNormalDep"(project(":nested"))
}
```
### `publishPlugin`
配置好 Bintray 参数,使用 `./gradlew publishPlugin` 可自动发布并上传插件到 Bintray。

View File

@ -7,9 +7,13 @@
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
@file:Suppress("DuplicatedCode")
package net.mamoe.mirai.console.gradle
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import java.io.File
import java.util.zip.ZipFile
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
@ -17,6 +21,148 @@ import kotlin.test.assertTrue
class TestBuildPlugin : AbstractTest() {
@Test
@DisplayName("project as normal dependency")
fun buildWithMultiProjectsAsNormalDependency() {
settingsFile.appendText(
"""
include("nested")
""".trimIndent()
)
tempDir.resolve("nested").also { it.mkdirs() }.resolve("build.gradle").writeText(
"""
plugins {
id("org.jetbrains.kotlin.jvm")
id("net.mamoe.mirai-console")
}
dependencies {
api "com.zaxxer:SparseBitSet:1.2"
}
repositories {
mavenCentral()
}
""".trimIndent()
)
tempDir.resolve("build.gradle").appendText(
"""
dependencies {
implementation project(":nested")
asNormalDep project(":nested")
}
""".trimIndent()
)
gradleRunner()
.withArguments(":buildPlugin", "--stacktrace", "--info")
.build()
ZipFile(findJar()).use { zipFile ->
val dpPrivate = zipFile.getInputStream(
zipFile.getEntry("META-INF/mirai-console-plugin/dependencies-private.txt")
).use { it.readBytes().decodeToString() }
val dpShared = zipFile.getInputStream(
zipFile.getEntry("META-INF/mirai-console-plugin/dependencies-shared.txt")
).use { it.readBytes().decodeToString() }
assertFalse { dpShared.contains("com.zaxxer:SparseBitSet:1.2") }
assertTrue { dpPrivate.contains("com.zaxxer:SparseBitSet:1.2") }
assertTrue { dpPrivate.contains(":nested") }
}
}
@Test
@DisplayName("no api extends if using implementation")
fun buildWithMultiProjectsWithoutApi() {
settingsFile.appendText(
"""
include("nested")
""".trimIndent()
)
tempDir.resolve("nested").also { it.mkdirs() }.resolve("build.gradle").writeText(
"""
plugins {
id("org.jetbrains.kotlin.jvm")
id("net.mamoe.mirai-console")
}
dependencies {
api "com.zaxxer:SparseBitSet:1.2"
}
repositories {
mavenCentral()
}
""".trimIndent()
)
tempDir.resolve("build.gradle").appendText(
"""
dependencies {
implementation project(":nested")
}
""".trimIndent()
)
gradleRunner()
.withArguments(":buildPlugin", "--stacktrace", "--info")
.build()
ZipFile(findJar()).use { zipFile ->
val dpPrivate = zipFile.getInputStream(
zipFile.getEntry("META-INF/mirai-console-plugin/dependencies-private.txt")
).use { it.readBytes().decodeToString() }
val dpShared = zipFile.getInputStream(
zipFile.getEntry("META-INF/mirai-console-plugin/dependencies-shared.txt")
).use { it.readBytes().decodeToString() }
assertFalse { dpShared.contains("com.zaxxer:SparseBitSet:1.2") }
assertTrue { dpPrivate.contains("com.zaxxer:SparseBitSet:1.2") }
}
}
@Test
@DisplayName("build with multi projects")
fun buildWithMultiProjects() {
settingsFile.appendText(
"""
include("nested")
""".trimIndent()
)
tempDir.resolve("nested").also { it.mkdirs() }.resolve("build.gradle").writeText(
"""
plugins {
id("org.jetbrains.kotlin.jvm")
id("net.mamoe.mirai-console")
}
dependencies {
api "com.zaxxer:SparseBitSet:1.2"
implementation "com.google.code.gson:gson:2.8.9"
api "org.slf4j:slf4j-simple:1.7.32"
}
repositories {
mavenCentral()
}
""".trimIndent()
)
tempDir.resolve("build.gradle").appendText(
"""
dependencies {
api project(":nested")
shadowLink "org.slf4j:slf4j-simple"
}
""".trimIndent()
)
gradleRunner()
.withArguments(":buildPlugin", "dependencies", "--stacktrace", "--info")
.build()
checkOutput()
}
@Test
fun `can build plugin`() {
tempDir.resolve("build.gradle").appendText(
@ -32,7 +178,13 @@ class TestBuildPlugin : AbstractTest() {
gradleRunner()
.withArguments("buildPlugin", "dependencies", "--stacktrace", "--info")
.build()
val jar = tempDir.resolve("build/libs").listFiles()!!.first { it.name.endsWith(".mirai.jar") }
checkOutput()
}
private fun findJar(): File = tempDir.resolve("build/libs").listFiles()!!.first { it.name.endsWith(".mirai.jar") }
private fun checkOutput() {
val jar = findJar()
ZipFile(jar).use { zipFile ->
assertNotNull(zipFile.getEntry("org/slf4j/impl/SimpleLogger.class"))

View File

@ -10,9 +10,9 @@
package net.mamoe.mirai.console.gradle
import org.gradle.api.DefaultTask
import org.gradle.api.artifacts.ExternalModuleDependency
import org.gradle.api.artifacts.ResolvedArtifact
import org.gradle.api.artifacts.ResolvedDependency
import org.gradle.api.Project
import org.gradle.api.artifacts.*
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.capabilities.Capability
import org.gradle.api.file.DuplicatesStrategy
@ -58,6 +58,8 @@ public open class BuildMiraiPluginV2 : Jar() {
"net.mamoe:mirai-console-terminal",
)
}
@Suppress("LocalVariableName")
@TaskAction
internal fun run() {
val runtime = mutableSetOf<String>()
@ -66,6 +68,8 @@ public open class BuildMiraiPluginV2 : Jar() {
val linkToApi = mutableSetOf<String>()
val shadowedFiles = mutableSetOf<File>()
val shadowedDependencies = mutableSetOf<String>()
val subprojects = mutableSetOf<String>()
val subprojects_fullpath = mutableSetOf<String>()
project.configurations.findByName(MiraiConsoleGradlePlugin.MIRAI_SHADOW_CONF_NAME)?.allDependencies?.forEach { dep ->
if (dep is ExternalModuleDependency) {
@ -73,18 +77,52 @@ public open class BuildMiraiPluginV2 : Jar() {
shadowedDependencies.add(artId)
}
}
project.configurations.findByName("apiElements")?.allDependencies?.forEach { dep ->
if (dep is ExternalModuleDependency) {
val artId = "${dep.group}:${dep.name}"
linkedDependencies.add(artId)
linkToApi.add(artId)
}
}
project.configurations.findByName("implementation")?.allDependencies?.forEach { dep ->
if (dep is ExternalModuleDependency) {
project.configurations.findByName(MiraiConsoleGradlePlugin.MIRAI_AS_NORMAL_DEP_CONF_NAME)?.allDependencies?.forEach { dep ->
if (dep is ProjectDependency) {
linkedDependencies.add("${dep.group}:${dep.name}")
}
}
fun deepForeachDependencies(conf: Configuration?, action: (Dependency) -> Unit) {
(conf ?: return).allDependencies.forEach { dep ->
action(dep)
if (dep is ProjectDependency) {
subprojects.add("${dep.group}:${dep.name}")
deepForeachDependencies(dep.dependencyProject.configurations.findByName(conf.name), action)
}
}
}
fun resolveProject(project: Project, doResolveApi: Boolean) {
deepForeachDependencies(project.configurations.findByName("apiElements")) { dep ->
if (dep is ExternalModuleDependency) {
val artId = "${dep.group}:${dep.name}"
linkedDependencies.add(artId)
if (doResolveApi) {
linkToApi.add(artId)
}
}
if (dep is ProjectDependency) {
subprojects_fullpath.add(dep.dependencyProject.path)
subprojects.add("${dep.group}:${dep.name}")
resolveProject(dep.dependencyProject, doResolveApi)
}
}
project.configurations.findByName("implementation")?.allDependencies?.forEach { dep ->
if (dep is ExternalModuleDependency) {
linkedDependencies.add("${dep.group}:${dep.name}")
}
if (dep is ProjectDependency) {
subprojects_fullpath.add(dep.dependencyProject.path)
subprojects.add("${dep.group}:${dep.name}")
resolveProject(dep.dependencyProject, false)
}
}
}
resolveProject(project, true)
linkedDependencies.removeAll(shadowedDependencies)
linkToApi.removeAll(shadowedDependencies)
linkedDependencies.addAll(miraiDependencies)
@ -105,6 +143,7 @@ public open class BuildMiraiPluginV2 : Jar() {
fun resolveDependency(resolvedDependency: ResolvedDependency) {
val depId = resolvedDependency.depId()
logger.info { "resolving : $depId" }
if (depId in linkedDependencies) {
markAsResolved(resolvedDependency)
linkDependencyTo(resolvedDependency, runtime)
@ -113,6 +152,10 @@ public open class BuildMiraiPluginV2 : Jar() {
}
return
}
if (depId in subprojects) {
resolvedDependency.children.forEach { resolveDependency(it) }
return
}
}
runtimeClasspath.firstLevelModuleDependencies.forEach { resolveDependency(it) }
@ -120,6 +163,7 @@ public open class BuildMiraiPluginV2 : Jar() {
logger.info { "linkToAPi : $linkToApi" }
logger.info { "api : $api" }
logger.info { "runtime : $runtime" }
logger.info { "subprojects : $subprojects" }
val lenientConfiguration = runtimeClasspath.lenientConfiguration
if (lenientConfiguration is DefaultLenientConfiguration) {
@ -152,6 +196,12 @@ public open class BuildMiraiPluginV2 : Jar() {
return@forEach
}
}
val cid = artId.componentIdentifier
if (cid is ProjectComponentIdentifier) {
if (cid.projectPath in subprojects_fullpath) {
return@forEach
}
}
logger.info { " `- $artId - ${artId.javaClass}" }
shadowedFiles.add(artifact.file)
}

View File

@ -32,6 +32,7 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
public class MiraiConsoleGradlePlugin : Plugin<Project> {
internal companion object {
const val MIRAI_SHADOW_CONF_NAME: String = "shadowLink"
const val MIRAI_AS_NORMAL_DEP_CONF_NAME: String = "asNormalDep"
}
private fun KotlinSourceSet.configureSourceSet(project: Project, target: KotlinTarget) {
@ -167,6 +168,7 @@ public class MiraiConsoleGradlePlugin : Plugin<Project> {
private fun Project.setupConfigurations() {
configurations.create(MIRAI_SHADOW_CONF_NAME).isCanBeResolved = false
configurations.create(MIRAI_AS_NORMAL_DEP_CONF_NAME).isCanBeResolved = false
}
override fun apply(target: Project): Unit = with(target) {