diff --git a/tools/gradle-plugin/src/BuildMiraiPluginTask.kt b/tools/gradle-plugin/src/BuildMiraiPluginTask.kt new file mode 100644 index 000000000..f316b56ef --- /dev/null +++ b/tools/gradle-plugin/src/BuildMiraiPluginTask.kt @@ -0,0 +1,18 @@ +package net.mamoe.mirai.console.gradle + +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.gradle.api.tasks.CacheableTask +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget +import java.io.File + +@CacheableTask +public open class BuildMiraiPluginTask : ShadowJar() { + internal var targetField: KotlinTarget? = null + + public val target: KotlinTarget get() = targetField!! + + /** + * ShadowJar 打包结果 + */ + public val output: File get() = outputs.files.singleFile +} \ No newline at end of file diff --git a/tools/gradle-plugin/src/MiraiConsoleExtension.kt b/tools/gradle-plugin/src/MiraiConsoleExtension.kt index 9c7eed49d..c1c868b6f 100644 --- a/tools/gradle-plugin/src/MiraiConsoleExtension.kt +++ b/tools/gradle-plugin/src/MiraiConsoleExtension.kt @@ -13,8 +13,10 @@ package net.mamoe.mirai.console.gradle import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.jfrog.bintray.gradle.BintrayExtension +import com.jfrog.bintray.gradle.BintrayPlugin import org.gradle.api.JavaVersion import org.gradle.api.XmlProvider +import org.gradle.api.plugins.PluginContainer import org.gradle.api.publish.maven.MavenPublication /** @@ -126,31 +128,44 @@ public open class MiraiConsoleExtension { /** * Bintray 插件成品 JAR 发布 配置. * - * @see Publishing + * @see PluginPublishing * @since 1.1.0 */ - public val publishing: Publishing = Publishing() + public val publishing: PluginPublishing = PluginPublishing() /** - * 控制自动配置 Bintray 发布 - * @since 1.1.0 - */ - public var publishingEnabled: Boolean = true - - /** - * Bintray 插件成品 JAR 发布 配置. + * 控制自动配置 Bintray 发布. 默认为 `false`, 表示不自动配置发布. + * + * 开启后将会: + * - 创建名为 "mavenJava" 的 [MavenPublication] + * - [应用][PluginContainer.apply] [BintrayPlugin], 配置 Bintray 相关参数 + * - 创建 task "publishPlugin" * - * @see [Publishing] * @since 1.1.0 */ - public inline fun publishing(block: Publishing.() -> Unit): Unit = publishing.run(block) + public var publishingEnabled: Boolean = false /** + * 开启自动配置 Bintray 插件成品 JAR 发布, 并以 [configure] 配置 [PluginPublishing]. + * + * @see [PluginPublishing] * @see publishingEnabled * @since 1.1.0 */ - public fun disablePublishing() { - publishingEnabled = false + public inline fun publishing(crossinline configure: PluginPublishing.() -> Unit) { + publishingEnabled = true + publishing.run(configure) + } + + /** + * 开启自动配置 Bintray 插件成品 JAR 发布. + * + * @see [PluginPublishing] + * @see publishingEnabled + * @since 1.1.0 + */ + public fun publishing() { + publishingEnabled = true } /** @@ -166,36 +181,37 @@ public open class MiraiConsoleExtension { * 4. [System.getenv] "PROP" * * @see publishing + * @see publishingEnabled * @since 1.1.0 */ - public class Publishing internal constructor() { + public class PluginPublishing internal constructor() { /////////////////////////////////////////////////////////////////////////// // Required arguments /////////////////////////////////////////////////////////////////////////// /** * Bintray 账户名. 必须. - * 若为 `null`, 将会以 [Publishing] 中描述的步骤获取 "bintray.user" + * 若为 `null`, 将会以 [PluginPublishing] 中描述的步骤获取 "bintray.user" * - * @see [Publishing] + * @see [PluginPublishing] */ public var user: String? = null /** * Bintray 账户 key. 必须. - * 若为 `null`, 将会以 [Publishing] 中描述的步骤获取 "bintray.key" + * 若为 `null`, 将会以 [PluginPublishing] 中描述的步骤获取 "bintray.key" */ public var key: String? = null /** * 目标仓库名称. 必须. - * 若为 `null`, 将会以 [Publishing] 中描述的步骤获取 "bintray.repo" + * 若为 `null`, 将会以 [PluginPublishing] 中描述的步骤获取 "bintray.repo" */ public var repo: String? = null /** * 目标仓库名称. 必须. - * 若为 `null`, 将会以 [Publishing] 中描述的步骤获取 "bintray.package" + * 若为 `null`, 将会以 [PluginPublishing] 中描述的步骤获取 "bintray.package" */ public var packageName: String? = null @@ -227,11 +243,16 @@ public open class MiraiConsoleExtension { */ public var version: String? = null + /** + * 发布的描述, 默认为 `project.description` + */ + public var description: String? = null + // Bintray /** * Bintray organization 名. 可选. - * 若为 `null`, 将会以 [Publishing] 中描述的步骤获取 "bintray.org". + * 若为 `null`, 将会以 [PluginPublishing] 中描述的步骤获取 "bintray.org". * 仍然无法获取时发布到 [user] 账号下的仓库 [repo], 否则发布到指定 [org] 下的仓库 [repo]. */ public var org: String? = null @@ -256,29 +277,29 @@ public open class MiraiConsoleExtension { /** * 自定义配置 [BintrayExtension],覆盖 */ - public fun bintray(config: BintrayExtension.() -> Unit) { - bintrayConfigs.add(config) + public fun bintray(configure: BintrayExtension.() -> Unit) { + bintrayConfigs.add(configure) } /** * 自定义配置 [BintrayExtension.PackageConfig] */ - public fun packageConfig(config: BintrayExtension.PackageConfig.() -> Unit) { - bintrayPackageConfigConfigs.add(config) + public fun packageConfig(configure: BintrayExtension.PackageConfig.() -> Unit) { + bintrayPackageConfigConfigs.add(configure) } /** * 自定义配置 maven pom.xml [XmlProvider] */ - public fun mavenPom(config: XmlProvider.() -> Unit) { - mavenPomConfigs.add(config) + public fun mavenPom(configure: XmlProvider.() -> Unit) { + mavenPomConfigs.add(configure) } /** * 自定义配置 [MavenPublication] */ - public fun mavenPublication(config: MavenPublication.() -> Unit) { - mavenPublicationConfigs.add(config) + public fun mavenPublication(configure: MavenPublication.() -> Unit) { + mavenPublicationConfigs.add(configure) } } } diff --git a/tools/gradle-plugin/src/MiraiConsoleGradlePlugin.kt b/tools/gradle-plugin/src/MiraiConsoleGradlePlugin.kt index 30b79cc7b..7fa6ad695 100644 --- a/tools/gradle-plugin/src/MiraiConsoleGradlePlugin.kt +++ b/tools/gradle-plugin/src/MiraiConsoleGradlePlugin.kt @@ -13,7 +13,7 @@ package net.mamoe.mirai.console.gradle import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.jfrog.bintray.gradle.BintrayPlugin import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.plugins.JavaPlugin @@ -101,18 +101,18 @@ public class MiraiConsoleGradlePlugin : Plugin { tasks.findByName("shadowJar")?.enabled = false - fun registerBuildPluginTask(target: KotlinTarget, isSinglePlatform: Boolean) { - tasks.create(if (isSinglePlatform) "buildPlugin" else "buildPlugin${target.name.capitalize()}", ShadowJar::class.java).apply shadow@{ + fun registerBuildPluginTask(target: KotlinTarget, isSingleTarget: Boolean) { + tasks.create("buildPlugin".wrapNameWithPlatform(target, isSingleTarget), BuildMiraiPluginTask::class.java).apply shadow@{ group = "mirai" + targetField = target + + archiveExtension.set("mirai.jar") val compilations = target.compilations.filter { it.name == MAIN_COMPILATION_NAME } compilations.forEach { dependsOn(it.compileKotlinTask) - from(it.output) - for (allKotlinSourceSet in it.allKotlinSourceSets) { - from(allKotlinSourceSet.resources) - } + from(it.output.allOutputs) } from(project.configurations.getByName("runtimeClasspath").copyRecursive { dependency -> @@ -142,18 +142,20 @@ public class MiraiConsoleGradlePlugin : Plugin { } override fun apply(target: Project): Unit = with(target) { - target.extensions.create("mirai", MiraiConsoleExtension::class.java) + extensions.create("mirai", MiraiConsoleExtension::class.java) - target.plugins.apply(JavaPlugin::class.java) - target.plugins.apply(ShadowPlugin::class.java) - - target.repositories.maven { it.setUrl(BINTRAY_REPOSITORY_URL) } + plugins.apply(JavaPlugin::class.java) + plugins.apply("org.gradle.maven-publish") + plugins.apply("org.gradle.maven") + plugins.apply(ShadowPlugin::class.java) + plugins.apply(BintrayPlugin::class.java) + repositories.maven { it.setUrl(BINTRAY_REPOSITORY_URL) } afterEvaluate { configureCompileTarget() kotlinTargets.forEach { configureTarget(it) } registerBuildPluginTasks() - registerPublishPluginTask() + configurePublishing() } } } @@ -171,4 +173,7 @@ internal val Project.kotlinTargets: Collection is KotlinSingleTargetExtension -> listOf(kotlinExtension.target) else -> error("[MiraiConsole] Internal error: kotlinExtension is neither KotlinMultiplatformExtension nor KotlinSingleTargetExtension") } - } \ No newline at end of file + } + +internal val Project.kotlinJvmOrAndroidTargets: Collection + get() = kotlinTargets.filter { it.platformType == KotlinPlatformType.jvm || it.platformType == KotlinPlatformType.androidJvm } diff --git a/tools/gradle-plugin/src/publishing.kt b/tools/gradle-plugin/src/publishing.kt index c10f01d8c..db01b23de 100644 --- a/tools/gradle-plugin/src/publishing.kt +++ b/tools/gradle-plugin/src/publishing.kt @@ -9,13 +9,19 @@ package net.mamoe.mirai.console.gradle +import com.google.gson.Gson +import com.jfrog.bintray.gradle.tasks.BintrayUploadTask import org.gradle.api.Project +import org.gradle.api.Task import org.gradle.api.publish.maven.MavenPublication +import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.bundling.Jar import org.gradle.kotlin.dsl.get import org.gradle.kotlin.dsl.getValue import org.gradle.kotlin.dsl.provideDelegate import org.gradle.kotlin.dsl.registering +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget +import java.io.File private val Project.selfAndParentProjects: Sequence @@ -29,17 +35,105 @@ private fun Project.findPropertySmart(propName: String): String? { } private fun Project.findPropertySmartOrFail(propName: String): String { - return findPropertySmart(propName) ?: error("[Mirai Console] Cannot find property for publication: $propName. Please check your 'mirai' configuration.") + return findPropertySmart(propName) + ?: error("[Mirai Console] Cannot find property for publication: '$propName'. Please check your 'mirai' configuration.") } -internal fun Project.registerPublishPluginTask() { +internal fun Project.configurePublishing() { + if (!miraiExtension.publishingEnabled) return + val isSingleTarget = kotlinJvmOrAndroidTargets.size == 1 + + kotlinJvmOrAndroidTargets.forEach { + registerPublishPluginTasks(it, isSingleTarget) + registerMavenPublications(it, isSingleTarget) + } + + registerBintrayPublish() +} + +private inline fun TaskContainer.getSingleTask(): T = filterIsInstance().single() + +private fun Project.registerPublishPluginTasks() { + val isSingleTarget = kotlinJvmOrAndroidTargets.size == 1 + kotlinJvmOrAndroidTargets.forEach { registerPublishPluginTasks(it, isSingleTarget) } +} + +// effectively public +internal data class PluginMetadata( + val groupId: String, + val artifactId: String, + val version: String, + val description: String?, + val dependencies: List +) + +internal fun String.wrapNameWithPlatform(target: KotlinTarget, isSingleTarget: Boolean): String { + return if (isSingleTarget) this else "$this${target.name.capitalize()}" +} + +private fun Project.registerPublishPluginTasks(target: KotlinTarget, isSingleTarget: Boolean) { + val generateMetadataTask = + tasks.register("generatePluginMetadata".wrapNameWithPlatform(target, isSingleTarget)).get().apply { + group = "mirai" + + val metadataFile = + project.buildDir.resolve("mirai").resolve(if (isSingleTarget) "mirai-plugin.metadata" else "mirai-plugin-${target.name}.metadata") + outputs.file(metadataFile) + + + + doLast { + val mirai = miraiExtension + + val output = outputs.files.singleFile + output.parentFile.mkdir() + + val dependencies = configurations[target.compilations["main"].apiConfigurationName].allDependencies.map { + "${it.group}:${it.name}:${it.version}" + } + + val json = Gson().toJson(PluginMetadata( + groupId = mirai.publishing.groupId ?: project.group.toString(), + artifactId = mirai.publishing.artifactId ?: project.name, + version = mirai.publishing.version ?: project.version.toString(), + description = mirai.publishing.description ?: project.description, + dependencies = dependencies + )) + + logger.info("Generated mirai plugin metadata json: $json") + + output.writeText(json) + } + } + + val bintrayUpload = tasks.getByName(BintrayUploadTask.getTASK_NAME()).dependsOn( + "buildPlugin".wrapNameWithPlatform(target, isSingleTarget), + generateMetadataTask, + // "shadowJar", + tasks.filterIsInstance().single { it.target == target } + ) + tasks.register("publishPlugin".wrapNameWithPlatform(target, isSingleTarget)).get().apply { + group = "mirai" + dependsOn(bintrayUpload) + } +} + +internal inline fun File.renamed(block: File.(nameWithoutExtension: String) -> String): File = this.resolveSibling(block(this, nameWithoutExtension)) + +private fun Project.registerBintrayPublish() { val mirai = miraiExtension bintray { user = mirai.publishing.user ?: findPropertySmartOrFail("bintray.user") key = mirai.publishing.key ?: findPropertySmartOrFail("bintray.key") - setPublications("mavenJava") + val targets = kotlinJvmOrAndroidTargets + if (targets.size == 1) { + setPublications("mavenJava") + } else { + setPublications(*targets.map { "mavenJava".wrapNameWithPlatform(it, false) }.toTypedArray()) + } + setConfigurations("archives") publish = mirai.publishing.publish @@ -49,12 +143,17 @@ internal fun Project.registerPublishPluginTask() { repo = mirai.publishing.repo ?: findPropertySmartOrFail("bintray.repo") name = mirai.publishing.packageName ?: findPropertySmartOrFail("bintray.package") userOrg = mirai.publishing.org ?: findPropertySmart("bintray.org") + desc = mirai.publishing.description ?: project.description mirai.publishing.bintrayPackageConfigConfigs.forEach { it.invoke(this) } } mirai.publishing.bintrayConfigs.forEach { it.invoke(this) } } +} + +private fun Project.registerMavenPublications(target: KotlinTarget, isSingleTarget: Boolean) { + val mirai = miraiExtension @Suppress("DEPRECATION") val sourcesJar by tasks.registering(Jar::class) { @@ -70,7 +169,7 @@ internal fun Project.registerPublishPluginTask() { url = uri("$buildDir/repo") } }*/ - publications.register("mavenJava", MavenPublication::class.java) { publication -> + publications.register("mavenJava".wrapNameWithPlatform(target, isSingleTarget), MavenPublication::class.java) { publication -> with(publication) { from(components["java"]) @@ -89,9 +188,11 @@ internal fun Project.registerPublishPluginTask() { } artifact(sourcesJar.get()) - - // TODO: 2020/11/28 -miraip metadata artifact - // TODO: 2020/11/28 -all shadowed artifact + artifact(tasks.filterIsInstance().single { it.target == target }) + artifact(mapOf( + "source" to tasks.getByName("generatePluginMetadata".wrapNameWithPlatform(target, isSingleTarget)).outputs.files.singleFile, + "extension" to "metadata" + )) mirai.publishing.mavenPublicationConfigs.forEach { it.invoke(this) } }