diff --git a/.github/workflows/check-publishing.yml b/.github/workflows/check-publishing.yml index 52c237dd5..f3f24e9e0 100644 --- a/.github/workflows/check-publishing.yml +++ b/.github/workflows/check-publishing.yml @@ -78,7 +78,7 @@ jobs: - name: Build and Publish Local Artifacts if: ${{ env.enableLocalPublishingTest == 'true' }} - run: ./gradlew :mirai-deps-test:publishMiraiArtifactsToMavenLocal ${{ env.gradleArgs }} "-Dmirai.build.project.version=2.99.0-deps-test" + run: ./gradlew :mirai-deps-test:publishMiraiLocalArtifacts ${{ env.gradleArgs }} "-Dmirai.build.project.version=2.99.0-deps-test" - name: Check Publication if: ${{ env.enableLocalPublishingTest == 'true' }} @@ -204,7 +204,7 @@ jobs: - name: Build and Publish Local Artifacts if: ${{ env.enableLocalPublishingTest == 'true' }} - run: ./gradlew :mirai-deps-test:publishMiraiArtifactsToMavenLocal ${{ env.gradleArgs }} "-Dmirai.build.project.version=2.99.0-deps-test" + run: ./gradlew :mirai-deps-test:publishMiraiLocalArtifacts ${{ env.gradleArgs }} "-Dmirai.build.project.version=2.99.0-deps-test" - name: Check Publication if: ${{ env.enableLocalPublishingTest == 'true' }} diff --git a/build.gradle.kts b/build.gradle.kts index 1bd773e45..7c48ae132 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -87,6 +87,9 @@ allprojects { substituteDependenciesUsingExpectedVersion() } } +afterEvaluate { + configureShadowDependenciesForPublishing() +} subprojects { afterEvaluate { diff --git a/buildSrc/src/main/kotlin/HmppConfigure.kt b/buildSrc/src/main/kotlin/HmppConfigure.kt index 71b66c0a6..b349f6259 100644 --- a/buildSrc/src/main/kotlin/HmppConfigure.kt +++ b/buildSrc/src/main/kotlin/HmppConfigure.kt @@ -86,14 +86,17 @@ enum class HostArch { /// eg. "a;b;!other" means to disable all targets but a or b val ENABLED_TARGETS by lazy { - val targets = System.getProperty( - "mirai.target" - ) ?: rootProject.getLocalProperty("projects.mirai-core.targets") - ?: "others" // enable all by default + val targets = getMiraiTargetFromGradle() // enable all by default targets.split(';').toSet() } +fun getMiraiTargetFromGradle() = + System.getProperty("mirai.target") + ?: System.getenv("mirai.target") + ?: rootProject.getLocalProperty("projects.mirai-core.targets") + ?: "others" + fun isTargetEnabled(name: String): Boolean { val isNative = name in POSSIBLE_NATIVE_TARGETS return when { diff --git a/buildSrc/src/main/kotlin/Shadow.kt b/buildSrc/src/main/kotlin/Shadow.kt index d25ba3bc3..b97b5e74f 100644 --- a/buildSrc/src/main/kotlin/Shadow.kt +++ b/buildSrc/src/main/kotlin/Shadow.kt @@ -13,6 +13,7 @@ import com.google.gson.GsonBuilder import org.gradle.api.GradleException import org.gradle.api.Project import org.gradle.api.Task +import org.gradle.api.execution.TaskExecutionGraph import org.gradle.api.publish.tasks.GenerateModuleMetadata import org.gradle.kotlin.dsl.create import org.gradle.kotlin.dsl.get @@ -29,20 +30,68 @@ fun Project.configureMppShadow() { it.platformType == org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.jvm && (it.attributes.getAttribute(MIRAI_PLATFORM_INTERMEDIATE) != true) }) { - configureRelocationForTarget(project) + 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." + } + + 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.contains("jar", ignoreCase = true) } + 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 + get() = sequence { + suspend fun SequenceScope.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.configureRelocationForTarget(project: Project) = project.run { +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" @@ -67,15 +116,13 @@ private fun KotlinTarget.configureRelocationForTarget(project: Project) = projec } } - // Relocate before packing Jar. Projects that depends on this project actually (explicitly or implicitly) depends on the Jar. - tasks.getByName("${targetName}Jar").dependsOn(relocateDependencies) - // We will modify Kotlin metadata, so do generate metadata before relocation val generateMetadataTask = tasks.getByName("generateMetadataFileFor${targetName.capitalize()}Publication") as GenerateModuleMetadata val patchMetadataTask = tasks.create("patchMetadataFileFor${targetName.capitalize()}RelocatedPublication") { dependsOn(generateMetadataTask) + dependsOn(relocateDependencies) // remove dependencies in Kotlin module metadata doLast { @@ -157,23 +204,15 @@ private fun KotlinTarget.configureRelocationForTarget(project: Project) = projec } private fun Sequence.dependsOn( - relocateDependencies: ShadowJar, - kotlinTarget: KotlinTarget, -): Int { - return onEach { relocateDependencies.dependsOn(it) } - .count().also { - check(it > 0) { "[Shadow Relocation] Expected at least one task task matched for target ${kotlinTarget.targetName}." } - } + task: Task, +) { + return forEach { it.dependsOn(task) } } -private fun Sequence.mustRunAfter( - relocateDependencies: ShadowJar, - kotlinTarget: KotlinTarget, -): Int { - return onEach { relocateDependencies.mustRunAfter(it) } - .count().also { - check(it > 0) { "[Shadow Relocation] Expected at least one task task matched for target ${kotlinTarget.targetName}." } - } +private fun Sequence.dependsOn( + tasks: Iterable, +) { + return forEach { it.dependsOn(tasks) } } private fun Project.registerRegularShadowTask(target: KotlinTarget, mapTaskNameForMultipleTargets: Boolean): ShadowJar { diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 22fb9354c..6229379b6 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -23,7 +23,7 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler object Versions { val project = System.getenv("mirai.build.project.version")?.takeIf { it.isNotBlank() } ?: System.getProperty("mirai.build.project.version")?.takeIf { it.isNotBlank() } - ?: /*PROJECT_VERSION_START*/"2.14.0-dev-shadow-1"/*PROJECT_VERSION_END*/ + ?: /*PROJECT_VERSION_START*/"2.14.0-dev-shadow-6"/*PROJECT_VERSION_END*/ val core = project val console = project diff --git a/mirai-deps-test/build.gradle.kts b/mirai-deps-test/build.gradle.kts index aefa8c970..6b5e18d80 100644 --- a/mirai-deps-test/build.gradle.kts +++ b/mirai-deps-test/build.gradle.kts @@ -86,19 +86,59 @@ fun generateBuildConfig() { } } -tasks.register("publishMiraiLocalArtifacts", Exec::class) { +// keep this property for Search Everywhere +val publishMiraiLocalArtifacts = tasks.register("publishMiraiLocalArtifacts", Exec::class) { group = "mirai" description = "Starts a child process to publish v2.99.0-deps-test artifacts to MavenLocal" workingDir(rootProject.projectDir) + + // The following code configures the new Gradle instance to inheriting configuration. + // This is important especially for "mirai.target" settings + // — On CI machines we didn't configure them to cross-compilation. + // Note that IntelliJ listener is also inherited, so you will see normal execution feedbacks in your IDE 'Run' view. + environment(System.getenv()) environment("mirai.build.project.version", "2.99.0-deps-test") + environment("mirai.target", getMiraiTargetFromGradle()) + + val projectProperties = +// gradle.startParameter.projectProperties + mapOf() + .toMutableMap().apply { + put("kotlin.compiler.execution.strategy", "in-process") + } + .map { "-P${it.key}=${it.value}" } + .toTypedArray() + + val allowedProperties = arrayOf("org.gradle.parallel") + val systemProperties = +// gradle.startParameter.systemPropertiesArgs + gradle.startParameter.systemPropertiesArgs + .filter { it.key in allowedProperties } + .map { "-D${it.key}=${it.value}" } + .toTypedArray() + commandLine( "./gradlew", publishMiraiArtifactsToMavenLocal.name, "--no-daemon", "--stacktrace", - "-Pkotlin.compiler.execution.strategy=in-process" - ) + "--scan", + *projectProperties, + *systemProperties, + ) // ignore other Gradle args + + doFirst { + + // TODO: 2022/11/22 fix tips + logger.info( + "[publishMiraiLocalArtifacts] Starting a Gradle daemon to run requested publishing tasks. " + + "Your system environment, JVM properties, and Gradle properties are inherited, " + + "but note that any other Gradle arguments are IGNORED!" + ) + logger.info("[publishMiraiLocalArtifacts] Oh, and don't worry, the daemon will be stopped after the task finishes so it won't waste your memory!") + } + standardOutput = System.out errorOutput = System.err }