From f87c142a69f6e42e8a51bbb62f8125a65ec2dd27 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 26 Aug 2022 16:35:13 +0800 Subject: [PATCH] [build] Support disabling modules and targets --- build.gradle.kts | 1 + buildSrc/src/main/kotlin/HmppConfigure.kt | 84 ++++++++++--------- buildSrc/src/main/kotlin/LocalProperties.kt | 56 +++++++++++++ docs/contributing/Building.md | 41 ++++++++++ docs/contributing/README.md | 90 ++++++++++++++------- settings.gradle.kts | 70 +++++++++++++--- 6 files changed, 266 insertions(+), 76 deletions(-) create mode 100644 buildSrc/src/main/kotlin/LocalProperties.kt create mode 100644 docs/contributing/Building.md diff --git a/build.gradle.kts b/build.gradle.kts index 7f7bb20f1..991eb3957 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -45,6 +45,7 @@ plugins { } osDetector = osdetector +BuildSrcRootProjectHolder.value = rootProject GpgSigner.setup(project) diff --git a/buildSrc/src/main/kotlin/HmppConfigure.kt b/buildSrc/src/main/kotlin/HmppConfigure.kt index efaaf3217..201b2b218 100644 --- a/buildSrc/src/main/kotlin/HmppConfigure.kt +++ b/buildSrc/src/main/kotlin/HmppConfigure.kt @@ -35,6 +35,7 @@ val OS_NAME = System.getProperty("os.name").toLowerCase() lateinit var osDetector: OsDetector + // aarch = arm val OsDetector.isAarch get() = osDetector.arch.run { @@ -76,21 +77,26 @@ enum class HostArch { /// eg. "!a;!b" means to enable all targets but a or b /// eg. "a;b;!other" means to disable all targets but a or b val ENABLED_TARGETS by lazy { - System.getProperty( - "mirai.target", - if (IDEA_ACTIVE) - // "jvm;android;${HOST_KIND.targetName};!other" - "other" // we must enable all targets otherwise you won't be able to edit code for non-host targets - else - "" - ).split(';').toSet() + + val targets = System.getProperty( + "mirai.target" + ) ?: rootProject.getLocalProperty("projects.mirai-core.targets") + ?: "others" // enable all by default + + targets.split(';').toSet() } fun isTargetEnabled(name: String): Boolean { + val isNative = name in POSSIBLE_NATIVE_TARGETS return when { - name in ENABLED_TARGETS -> true - "!$name" in ENABLED_TARGETS -> false - else -> "!other" !in ENABLED_TARGETS + name in ENABLED_TARGETS -> true // explicitly enabled + "!$name" in ENABLED_TARGETS -> false // explicitly disabled + + "native" in ENABLED_TARGETS && isNative -> true // native targets explicitly enabled + "!native" in ENABLED_TARGETS && isNative -> false // native targets explicitly disabled + + "!other" in ENABLED_TARGETS -> false // others disabled + else -> true } } @@ -130,6 +136,8 @@ val UNIX_LIKE_TARGETS by lazy { LINUX_TARGETS + MAC_TARGETS } val NATIVE_TARGETS by lazy { UNIX_LIKE_TARGETS + WIN_TARGETS } +private val POSSIBLE_NATIVE_TARGETS by lazy { setOf("mingwX64", "macosX64", "macosArm64", "linuxX64") } + fun Project.configureJvmTargetsHierarchical() { extensions.getByType(KotlinMultiplatformExtension::class.java).apply { val commonMain by sourceSets.getting @@ -206,6 +214,8 @@ fun Project.configureJvmTargetsHierarchical() { fun KotlinMultiplatformExtension.configureNativeTargetsHierarchical( project: Project ) { + if (NATIVE_TARGETS.isEmpty()) return + val nativeMainSets = mutableListOf() val nativeTestSets = mutableListOf() val nativeTargets = mutableListOf() @@ -243,39 +253,41 @@ fun KotlinMultiplatformExtension.configureNativeTargetsHierarchical( } } - val unixMain by lazy { - this.sourceSets.maybeCreate("unixMain").apply { - dependsOn(nativeMain) + if (UNIX_LIKE_TARGETS.isNotEmpty()) { + val unixMain by lazy { + this.sourceSets.maybeCreate("unixMain").apply { + dependsOn(nativeMain) + } } - } - val unixTest by lazy { - this.sourceSets.maybeCreate("unixTest").apply { - dependsOn(nativeTest) + val unixTest by lazy { + this.sourceSets.maybeCreate("unixTest").apply { + dependsOn(nativeTest) + } } - } - val darwinMain by lazy { - this.sourceSets.maybeCreate("darwinMain").apply { - dependsOn(unixMain) + val darwinMain by lazy { + this.sourceSets.maybeCreate("darwinMain").apply { + dependsOn(unixMain) + } } - } - val darwinTest by lazy { - this.sourceSets.maybeCreate("darwinTest").apply { - dependsOn(unixTest) + val darwinTest by lazy { + this.sourceSets.maybeCreate("darwinTest").apply { + dependsOn(unixTest) + } } - } - presets.filter { it.name in MAC_TARGETS }.forEach { preset -> - addNativeTarget(preset).run { - compilations[MAIN_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(darwinMain) } - compilations[TEST_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(darwinTest) } + presets.filter { it.name in MAC_TARGETS }.forEach { preset -> + addNativeTarget(preset).run { + compilations[MAIN_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(darwinMain) } + compilations[TEST_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(darwinTest) } + } } - } - presets.filter { it.name in LINUX_TARGETS }.forEach { preset -> - addNativeTarget(preset).run { - compilations[MAIN_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(unixMain) } - compilations[TEST_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(unixTest) } + presets.filter { it.name in LINUX_TARGETS }.forEach { preset -> + addNativeTarget(preset).run { + compilations[MAIN_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(unixMain) } + compilations[TEST_COMPILATION_NAME].kotlinSourceSets.forEach { it.dependsOn(unixTest) } + } } } diff --git a/buildSrc/src/main/kotlin/LocalProperties.kt b/buildSrc/src/main/kotlin/LocalProperties.kt new file mode 100644 index 000000000..046ba3cda --- /dev/null +++ b/buildSrc/src/main/kotlin/LocalProperties.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2019-2022 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 org.gradle.api.Project +import java.util.* + +/* + * Copyright 2019-2022 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 + */ + +object BuildSrcRootProjectHolder { + lateinit var value: Project +} + +val rootProject: Project get() = BuildSrcRootProjectHolder.value + + +private lateinit var localProperties: Properties + +private fun Project.loadLocalPropertiesIfAbsent() { + if (::localProperties.isInitialized) return + localProperties = Properties().apply { + rootProject.projectDir.resolve("local.properties").bufferedReader().use { + load(it) + } + } +} + +fun Project.getLocalProperty(name: String): String? { + loadLocalPropertiesIfAbsent() + return localProperties.getProperty(name) +} + +fun Project.getLocalProperty(name: String, default: String): String { + return getLocalProperty(name) ?: default +} + +fun Project.getLocalProperty(name: String, default: Int): Int { + return getLocalProperty(name)?.toInt() ?: default +} + +fun Project.getLocalProperty(name: String, default: Boolean): Boolean { + return getLocalProperty(name)?.toBoolean() ?: default +} + diff --git a/docs/contributing/Building.md b/docs/contributing/Building.md new file mode 100644 index 000000000..4d6119c4e --- /dev/null +++ b/docs/contributing/Building.md @@ -0,0 +1,41 @@ +# 构建 + +本文介绍如何构建 mirai 的各模块。 + +## 构建 JVM 目标项目 + +要构建只有 JVM 目标的项目(如 `mirai-console`,只需在项目根目录使用如下命令执行 +Gradle 任务: + +```shell +$ ./gradlew :mirai-console:assemble # 编译 +$ ./gradlew :mirai-console:check # 测试 +$ ./gradlew :mirai-console:build # 编译和测试 +``` + +其中 `:mirai-console` 是目标项目的路径(path)。 + +你也可以在 IDEA 等有 Gradle 支持的 IDE +中在通过侧边栏等方式选择项目的 `assemble` 等任务: + +![](images/run-gradle-tasks-in-idea.png) + +### 获得 mirai-console JAR + +在项目根目录执行如下命令可以获得包含依赖的 mirai-console JAR。对于其他模块类似。 + +```shell +$ ./gradlew :mirai-console:shadowJar +``` + +## 构建多平台项目 + +core 是多平台项目。请参考 [构建 Core](BuildingCore.md)。 + +## 构建 IntelliJ 插件 + +可通过如下命令构建 IntelliJ 平台 IDE 的插件。构建成功的插件将可以在 `mirai-console/tools/intellij-plugin/build/distribution` 中找到。 + +```shell +$ ./graldew :mirai-console-intellij:buidlPlugin +``` diff --git a/docs/contributing/README.md b/docs/contributing/README.md index a47e844bc..7baa2fd23 100644 --- a/docs/contributing/README.md +++ b/docs/contributing/README.md @@ -75,6 +75,19 @@ mirai 等。 ,若一个修改适合发布为小版本更新,mirai 会从 `dev` 中提取该修复到目标 `-release` 分支。 +## 安装 JDK + +需要安装 JDK 才能编译 mirai。mirai 主分支最新提交在如下环境测试可以编译: + +| 操作系统 | JDK | 架构 | +|--------------|--------------------|---------| +| macOS 12.0.1 | AdoptOpenJDK 17 | aarch64 | +| macOS 12.0.1 | Amazon Corretto 11 | amd64 | +| Windows 10 | OpenJDK 17 | amd64 | +| Ubuntu 20.04 | AdoptOpenJDK 17 | amd64 | + +若在其他环境下无法正常编译, 请尝试选择上述一个环境配置。 + ## `mirai-core` 术语 根据语境,mirai-core 有时候可能指 `mirai-core` @@ -108,54 +121,75 @@ core 的源集结构如图所示: ``` +| 发布平台名称 | 描述 | +|------------|------------------| +| jvm | JVM | +| android | Android (Dalvik) | +| mingwX64 | Windows x64 | +| macosX64 | macOS x64 | +| macosArm64 | macOS arm64 | +| linuxX64 | Linux x64 | + 备注: - common 包含全平台通用代码,绝大部分代码都位于 common; - jvmBase 包含针对 JVM 平台的通用代码; - `` 为 macOS,iOS,WatchOS 等 Apple 平台目标。 -## 安装 JDK +## 开发提示 -需要安装 JDK 才能编译 mirai。mirai 主分支最新提交在如下环境测试可以编译: +建议使用 IntelliJ IDEA 或 Android Studio,并安装最新的 Kotlin 插件。 -| 操作系统 | JDK | 架构 | -|--------------|--------------------|---------| -| macOS 12.0.1 | AdoptOpenJDK 17 | aarch64 | -| macOS 12.0.1 | Amazon Corretto 11 | amd64 | -| Windows 10 | OpenJDK 17 | amd64 | -| Ubuntu 20.04 | AdoptOpenJDK 17 | amd64 | +建议设置 IntelliJ 的内存为至少 6GB,否则 IDE 可能会频繁冻结编辑器收集垃圾。(可在 `Help -> Edit Custom VM Options` 中添加 `-Xmx6000m`) -若在其他环境下无法正常编译, 请尝试选择上述一个环境配置。 +### 关闭部分项目以提升速度 -## 构建 JVM 目标项目 +你可以在项目根目录创建 `local.properties`,中按照如下配置,关闭部分项目来提升开发速度。 -要构建只有 JVM 目标的项目(如 `mirai-console`,只需在项目根目录使用如下命令执行 -Gradle 任务: - -```shell -$ ./gradlew :mirai-console:assemble # 编译 -$ ./gradlew :mirai-console:check # 测试 -$ ./gradlew :mirai-console:build # 编译和测试 +```properties +# 关闭 IntelliJ IDEA 插件模块 +projects.mirai-console-intellij.enabled=false +# 关闭 Gradle 插件模块 +projects.mirai-console-gradle.enabled=false +# 关闭 mirai 依赖测试模块 +projects.mirai-deps-test.enabled=false +# 用其他模块的路径替换 module-path,可关闭该模块 +projects.module-path.enabled=false +# 特殊配置,关闭 mirai-console 后端,这同时也会关闭全部 console 相关的项目 +projects.mirai-console.enabled=false +# 特殊配置,关闭 mirai-logging,这会关闭所有日志转接模块 +projects.mirai-logging.enabled=false ``` -其中 `:mirai-console` 是目标项目的路径(path)。 +通常关闭 IDEA 插件和 Gradle 插件可以显著提高初始化速度(IDEA 插件项目在初始化时需要下载 1G 左右编译依赖)。 -你也可以在 IDEA 等有 Gradle 支持的 IDE -中在通过侧边栏等方式选择项目的 `assemble` 等任务: +### 关闭 core 的部分构建目标 -![](images/run-gradle-tasks-in-idea.png) +可以在上述 `local.properties` 中,配置 `projects.mirai-core.targets=` 使用以下配置语法关闭部分构建目标。关闭后可以减轻 IDE 负担,也可以避免下载工具链而加快初始化速度。 -### 获得 mirai-console JAR +所有目标默认都启用。 -在项目根目录执行如下命令可以获得包含依赖的 mirai-console JAR。对于其他模块类似。 +**注意**,在关闭一个目标后,将无法编辑该目标的相关源集的源码。关闭 native 目标后也可能会影响 native 目标平台原生接口的数据类型。 +因此若非主机性能太差或在 CI 机器运行,**不建议**关闭 native 目标。 -```shell -$ ./gradlew :mirai-console:shadowJar -``` +- `xxx`:显式启用 `xxx` 目标 +- `!xxx`:显式禁用 `xxx` 目标 +- `native`:显式启用所有 native 目标 +- `!native`:禁用没有显式启用的所有 native 目标 +- `others`:显式启用其他所有所有目标 +- `!others`:禁用没有显式启用的所有目标 -## 构建多平台项目 +其中 xxx 表示构建目标名称。可用的目标名称有(区分大小写):`jvm`、`android`、`macosX64`、`macosArm64`、`mingwX64`、`linuxX64` -core 是多平台项目。请参考 [构建 Core](BuildingCore.md)。 +示例(前两条目前等价): + +- `!native;others` 指定禁用所有 native 目标,启用其他目标 +- `jvm;android;!others` 指定启用 `jvm` 和 `android` 目标,禁用其他所有目标 +- `jvm;macosX64;!others` 指定启用 `jvm` 和 `macosX64` 目标,禁用其他所有目标 + +## 构建 + +查看 [Building](Building.md) ## 寻找带解决的问题 diff --git a/settings.gradle.kts b/settings.gradle.kts index 04c7e2bbd..f3a4affb8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,3 +1,14 @@ +/* + * Copyright 2019-2022 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 java.util.* + /* * Copyright 2019-2022 Mamoe Technologies and contributors. * @@ -20,12 +31,21 @@ pluginManagement { rootProject.name = "mirai" + +val localProperties = Properties().apply { + rootProject.projectDir.resolve("local.properties").bufferedReader().use { + load(it) + } +} + + /** * Projects included so far */ val allProjects = mutableListOf() fun includeProject(projectPath: String, dir: String? = null) { + if (!getLocalProperty("projects." + projectPath.removePrefix(":") + ".enabled", true)) return include(projectPath) if (dir != null) project(projectPath).projectDir = file(dir) allProjects.add(project(projectPath)) @@ -44,22 +64,29 @@ includeProject(":mirai-bom") includeProject(":mirai-dokka") includeProject(":mirai-deps-test") -includeProject(":mirai-logging-log4j2", "logging/mirai-logging-log4j2") -includeProject(":mirai-logging-slf4j", "logging/mirai-logging-slf4j") -includeProject(":mirai-logging-slf4j-simple", "logging/mirai-logging-slf4j-simple") -includeProject(":mirai-logging-slf4j-logback", "logging/mirai-logging-slf4j-logback") - +if (getLocalProperty("projects.mirai-logging.enabled", true)) { + includeProject(":mirai-logging-log4j2", "logging/mirai-logging-log4j2") + includeProject(":mirai-logging-slf4j", "logging/mirai-logging-slf4j") + includeProject(":mirai-logging-slf4j-simple", "logging/mirai-logging-slf4j-simple") + includeProject(":mirai-logging-slf4j-logback", "logging/mirai-logging-slf4j-logback") +} +// mirai-core-api depends on this includeConsoleProject(":mirai-console-compiler-annotations", "tools/compiler-annotations") -includeConsoleProject(":mirai-console", "backend/mirai-console") -includeConsoleProject(":mirai-console.codegen", "backend/codegen") -includeConsoleProject(":mirai-console-terminal", "frontend/mirai-console-terminal") -includeConsoleIntegrationTestProjects() +if (getLocalProperty("projects.mirai-console.enabled", true)) { + includeConsoleProject(":mirai-console", "backend/mirai-console") + includeConsoleProject(":mirai-console.codegen", "backend/codegen") + includeConsoleProject(":mirai-console-terminal", "frontend/mirai-console-terminal") + includeConsoleIntegrationTestProjects() + + includeConsoleProject(":mirai-console-compiler-common", "tools/compiler-common") + includeConsoleProject(":mirai-console-intellij", "tools/intellij-plugin") + includeConsoleProject(":mirai-console-gradle", "tools/gradle-plugin") +} else { + // if mirai-console is disabled, disable all relevant projects +} -includeConsoleProject(":mirai-console-compiler-common", "tools/compiler-common") -includeConsoleProject(":mirai-console-intellij", "tools/intellij-plugin") -includeConsoleProject(":mirai-console-gradle", "tools/gradle-plugin") //includeConsoleFrontendGraphical() @@ -133,4 +160,23 @@ fun includeConsoleIntegrationTestProjects() { .listFiles()?.asSequence().orEmpty() .filter { it.isDirectory } .forEach { includeConsoleITPlugin(":mirai-console.integration-test:", it) } +} + + + + +fun getLocalProperty(name: String): String? { + return localProperties.getProperty(name) +} + +fun getLocalProperty(name: String, default: String): String { + return localProperties.getProperty(name) ?: default +} + +fun getLocalProperty(name: String, default: Int): Int { + return localProperties.getProperty(name)?.toInt() ?: default +} + +fun getLocalProperty(name: String, default: Boolean): Boolean { + return localProperties.getProperty(name)?.toBoolean() ?: default } \ No newline at end of file