From dcc418a557ca618aca110828baa7fe3ac7f924bd Mon Sep 17 00:00:00 2001 From: Him188 Date: Sun, 12 Jun 2022 17:57:16 +0100 Subject: [PATCH] Add docs for building core --- CONTRIBUTING.md | 103 --------- README.md | 5 +- docs/contributing/BuildingCore.md | 156 +++++++++++++ docs/contributing/README.md | 206 ++++++++++++++++++ docs/contributing/VerifyingABI.md | 13 ++ .../images/run-gradle-tasks-in-idea.png | Bin 0 -> 24635 bytes 6 files changed, 378 insertions(+), 105 deletions(-) delete mode 100644 CONTRIBUTING.md create mode 100644 docs/contributing/BuildingCore.md create mode 100644 docs/contributing/README.md create mode 100644 docs/contributing/VerifyingABI.md create mode 100644 docs/contributing/images/run-gradle-tasks-in-idea.png diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index e7dbf3a26..000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,103 +0,0 @@ -# 贡献 - -**感谢你来到这里和你对 mirai 做的所有贡献。** - -mirai 欢迎一切形式的代码贡献。你可以通过以下几种途径向 mirai 贡献。 - -## 主仓库 `mirai-core` - -### 构建项目 - -#### 更新 submodules - -mirai git 仓库含有 submodule, 请在 clone 时使用 `--recursive` 参数, 或在 clone 后使用如下命令更新 submodule: -```shell script -git submodule init -git submodule update -``` - -#### 安装 JDK - -mirai 2.9.0 在如下环境测试可以编译: -- macOS 12.0.1, AdoptOpenJDK 17 aarch64, Gradle 7.2, Kotlin 1.6.0 -- macOS 12.0.1, Amazon Corretto 11 amd64, Gradle 7.2, Kotlin 1.6.0 - -若在其他环境下无法正常编译, 请尝试选择上述一个环境配置. - -#### 运行 Gradle 构建 - -项目首次初始化和构建可能要花费较长时间。 - -- 要构建项目, 请运行 `gradlew assemble` -- 要运行测试, 请运行 `gradlew check` -- 要构建项目并运行测试, 请运行 `gradlew build` - -### 分支 - -- `1.x`: 1.x 版本的开发 (已停止) -- `dev`: 2.0 版本的开发 -- `-release` 后缀: 基于[版本规范](docs/Evolution.md#版本规范), 用于从 `dev` 中筛选 bugfix 并发布一个版本的 patch 的版本. 如 `2.0-release` 会包含 `2.0.x` 版本的更新. - -**请基于 `dev` 分支进行修改** - -### 能做什么? - -- 维护社区: 可以为 [mirai-console](/mirai-console) 编写插件, 并发布到论坛 - -- 代码优化: 优化任何功能设计或实现, 或是引入一个新的设计 -- 解决问题: 在 [issues](https://github.com/mamoe/mirai/issues) 查看 mirai 正遇到的所有问题, 或在 [里程碑](https://github.com/mamoe/mirai/milestones) 查看版本计划. 所有没有 assignee 的 issue 都处于 -- 协议支持: [添加新协议支持](#添加协议支持) - -### 加入开发组 - -你可以随时提交 PR 解决任何问题。而若有兴趣,我们也欢迎你加入开发组,请联系 support@mamoe.net - -[mirai-compose]: https://github.com/sonder-joker/mirai-compose -[plugin-center 服务端]: https://github.com/project-mirai/mirai-plugin-center -[mirai-api-http]: https://github.com/project-mirai/mirai-api-http -[project-mirai/docs]: https://github.com/project-mirai/docs -[docs.mirai.mamoe.net]: https://docs.mirai.mamoe.net - - -| 名称 | 描述 | -|:------------------------:|:------------------------------------------------------------------------------------------------------:| -| core 和 console 日常更新 | 在 milestone 安排的日常更新。我们目前版本速度是一个月到两个月发布一个次版本(2.x)。需要日常的开发。 | -| console 后端 | 架构稳定,现在格外需要在易用性上的提升,首先需要一个优化方案,再实现它们。 | -| console 文档 | 根据用户反馈,现在文档十分缺少。需要以用户的身份体验过 console 的人编写用户文档。 | -| 图形前端 [mirai-compose] | 各功能都缺目前尤其缺少对接 console PluginConfig 的图形化配置的实现。 | -| [plugin-center 服务端] | 插件中心正在建设中。后端 Spring,前端 Vuetify。由于开发人员学业繁忙,暂搁置。 | -| plugin-center 社区 | 插件中心计划支持所有语言的插件,因此需要与社区 SDK 作者沟通并帮助它们接入 Console 的 PluginLoader API 和插件中心的要求。 | -| plugin-center console 端 | 需要评估现在 console 架构是否足够支持插件中心及所有语言插件的管理,实现与插件中心的对接。 | -| plugin-center gradle | 对接插件中心,实现通过 Task 上传插件。还没有开始做。 | -| mirai-console-loader | console 启动器。对接插件中心的 API,支持下载和更新插件等。不确定之后是否会有人实现。 | -| IDE 插件 | IntelliJ IDEA 的插件的工作。可以为 mirai 框架添加检查等功能。这个部分目前基本满足需求。 | -| [mirai-api-http] v2 | 日常维护。 | -| [project-mirai/docs] | 用户友好文档自动部署,使用 VuePress , 部署于 [docs.mirai.mamoe.net],目前还有部分超链接错误的问题。 | - - -### 里程碑 - -[里程碑](https://github.com/mamoe/mirai/milestones) 为各版本的开发计划. 在完成所有任务后就会发布该版本. - -`Backlog` 为没有设定目标版本的计划. 如果有相关 PR, 这些计划就可能会被确定到一个最近的版本. - -### 添加协议支持 - -请查看 [PacketFactory.kt](mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt) 了解网络层架构. -参考现有的 `PacketFactory` 实现和一些有关协议的 PR (带有 `protocol` 标签) 了解如何添加新的 `PacketFactory`. - - -### 开发 mirai-core - -- 使用 IntelliJ IDEA 或 Android Studio -- 安装 IDE 插件 [kotlin-jvm-blocking-bridge](https://github.com/Him188/kotlin-jvm-blocking-bridge/blob/master/README-chs.md#%E5%AE%89%E8%A3%85-intellij-idea-%E6%88%96-android-studio-%E6%8F%92%E4%BB%B6) -- 若要添加一个 suspend 函数, 请为它添加 `@JvmBlockingBridge`, 使用 [kotlin-jvm-blocking-bridge](https://github.com/mamoe/kotlin-jvm-blocking-bridge/blob/master/README-chs.md) -- 在 mirai-core 和 mirai-core-api 使用纯 Kotlin 实现 -- 尽量不要引用新的库 -- 遵守 Kotlin 官方代码规范(提交前使用 IDE 格式化代码 (commit 时勾选 'Reformat code')) -- 保证二进制兼容性: 在提交前执行 `./gradlew build`, 若有不兼容变更会得到错误。 - 如果你正在添加一个新功能,可以忽略这个错误,执行 `./gradlew clean apiDumpAll`。这将会生成 `*.api`,文件的变化反映了你的修改情况。将这些文件一并提交。 (详细了解 [Kotlin/binary-compatibility-validator](https://github.com/Kotlin/binary-compatibility-validator)) -- 通过 GitHub 的 Pull Request 提交代码,很快就会有相关模块负责人员来审核 - - -如果你不太保证自己能达到上述要求也没关系,mirai 感谢你的每一行代码,维护者会审核代码并尽可能帮助你。 diff --git a/README.md b/README.md index ade963f1f..eb677c78f 100644 --- a/README.md +++ b/README.md @@ -129,9 +129,10 @@ mirai 是一个在全平台下运行,提供 QQ Android 协议支持的高效 - 在线讨论: [Gitter](https://gitter.im/mamoe/mirai?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) - mirai 开发组和官方系列项目: [project-mirai](https://github.com/project-mirai) -- mirai 社区相关项目 (旧): [awesome-mirai](https://github.com/project-mirai/awsome-mirai/blob/master/README.md) +- mirai 社区相关项目 ( + 旧): [awesome-mirai](https://github.com/project-mirai/awsome-mirai/blob/master/README.md) -- 帮助 mirai: [CONTRIBUTING](CONTRIBUTING.md) +- 帮助 mirai: [CONTRIBUTING](docs/contributing/README.md) ## 赞助 - 本着与更多 mirai 开发者、用户、支持者共建更好的学习环境为目的,mirai 自 2021 年 3 月 1 日发起官方社区的建设。社区建设可能涉及:[学习论坛](https://mirai.mamoe.net)、[插件中心(在建)](https://github.com/project-mirai/mirai-plugin-center)等。由于社区的运维需要经费,mirai 项目开启 sponsor 功能。 diff --git a/docs/contributing/BuildingCore.md b/docs/contributing/BuildingCore.md new file mode 100644 index 000000000..238d72f74 --- /dev/null +++ b/docs/contributing/BuildingCore.md @@ -0,0 +1,156 @@ +# 构建 Core + +本文介绍如何构建 core 的 JVM 和 Native 目标。 + +## 构建 core 的 JVM 目标 + +方法与[构建 JVM 目标项目](README.md#构建-jvm-目标项目) +类似,但需要使用 `:mirai-core:compileKotlinJvm` 和 `:mirai-core:jvmTest` +分别用于编译和测试。提示:直接执行测试时也会自动先完成编译。 + +## 构建 core 的 Native 目标 + +[OpenSSL.def]: ../../mirai-core/src/nativeMain/cinterop/OpenSSL.def + +Kotlin 会自动配置 Native 编译器,要构建 Mirai 的 Native 目标还需要准备相关依赖。 + +### 操作系统条件 + +主机操作系统为以下任一: + +- Windows x86_64 (amd64) +- macOS x86_64 (amd64) +- macOS aarch64 (arm64) +- Linux x86_64 (amd64) + +注意:32 位操作系统不受支持。未列举的操作系统不受支持。 + +目前 Kotlin 对交叉编译支持有限,只能在一个主机上编译该主机平台的目标。例如在 Windows x86_64 主机上只能编译 +Windows x86_64 目标;在 macOS aarch64 主机上只能编译 macOS aarch64 目标。 + +与其他 Native 语言相同,Kotlin 的应用使用依赖时同样需要配置链接,mirai +已经配置了常用目录。也可以在 `mirai-core/src/nativeMain/cinterop/OpenSSL.def` +修改 `linkerOpts` 即链接器参数,以增加自定义路径。 + +### 安装 OpenSSL + +所有上述主机都需要进行这一步。 + +可以访问 OpenSSL 官网 `https://curl.se/download.html` 安装。 + +#### 在 Ubuntu 通过 Aptitude 安装 OpenSSL + +```shell +$ sudo apt install libssl-dev # 安装 OpenSSL +$ sudo apt install gcc-multilib # 若遇到链接问题可额外尝试此命令 +``` + +#### 在 macOS 通过 Homebrew 安装 OpenSSL + +```shell +$ brew install openssl@3 +``` + +注意:若遇到无法链接等问题,可以尝试通过源码编译安装。 + +#### 在 macOS 或 Linux 通过源码编译安装 OpenSSL + +请参考 [OpenSSL 文档](https://github.com/openssl/openssl/blob/master/INSTALL.md#prerequisites) +准备 OpenSSL 的要求。 + +以下命令可能会帮助你(这是 mirai 的 GitHub Actions 使用的命令)。 + +```shell +$ git clone https://github.com/openssl/openssl.git --recursive +$ cd openssl +$ git checkout tags/openssl-3.0.3 +$ ./Configure --prefix=/opt/openssl --openssldir=/usr/local/ssl +$ make +$ sudo make install +``` + +若在 Ubuntu 遇到链接问题,可额外尝试此命令: + +```shell +$ sudo apt install gcc-multilib +``` + +#### 在 Windows 通过源码编译安装 OpenSSL + +在 Windows,可通过源码编译安装,请使用 Command Prompt (cmd)。 + +你需要提前安装 [Git](https://git-scm.com/) +和 [Microsoft Visual Studio](https://visualstudio.microsoft.com/zh-hans/) +,并修改以下命令中的路径。 + +请参考 [OpenSSL 文档](https://github.com/openssl/openssl/blob/master/INSTALL.md#prerequisites) +准备 OpenSSL 的要求。 + +以下命令可能会帮助你(这是 mirai 的 GitHub Actions 使用的命令)。 + +```shell +git clone https://github.com/openssl/openssl.git --recursive +cd openssl +git checkout tags/openssl-3.0.3 +perl Configure VC-WIN64A --prefix=C:/openssl --openssldir=C:/openssl/ssl no-asm +"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86_amd64 && nmake && nmake install +``` + +注意: + +- `--prefix=C:/openssl --openssldir=C:/openssl/ssl` 表示将 OpenSSL + 安装在 `C:/openssl`,mirai + 已经配置会使用此路径寻找链接库。你也可以更换为其他路径,但就需要同步修改配置(位于 `mirai-core/src/nativeMain/cinterop/OpenSSL.def` + ); +- 不要将修改路径后的 `OpenSSL.def` 通过 Git 推送到 mirai 仓库或 PR。 + +#### 安装 cURL + +mirai 在 Windows 上使用 +cURL,在其他平台使用 [Ktor CIO](https://ktor.io/docs/http-client-engines.html#cio) +,因此只有 Windows 系统需要进行这一步。 + +可以访问 cURL 官网 `https://curl.se/download.html` 安装。 + +提示:如果在[链接](#链接)时遇到找不到 cURL +相关符号的问题,请尝试修改链接器参数。尽管 `mirai-core/src/nativeMain/cinterop/OpenSSL.def` +是用于 `OpenSSL.def` 的,也可以在这个文件配置 cURL 路径。 + +#### 编译 + +在任意主机上可以执行所有目标的 Kotlin 编译,但不能执行链接。要执行特定目标的编译,运行 Gradle +任务 `compileKotlinXXX`,其中 `XXX` 可以是:`MacosX64`、`MacosArm64`、`MingwX64` +或 `LinuxX64`。 + +也可以执行 `compileKotlinHost`,将自动根据当前主机选择合适的目标。 + +#### 链接并测试 + +执行 core 模块的 `hostTest`,将根据主机选择合适的测试并运行。 + +详情参考 [Kotlin 官方文档](https://kotlinlang.org/docs/multiplatform-run-tests.html) +。 + +#### 链接并构建动态链接库 + +注意,只有 mirai-core 可以构建可用的动态链接库。 + +执行 `:mirai-core:linkDebugSharedHost` +或 `:mirai-core:linkReleaseSharedHost`。Debug 版本会保留调试符号,能显示完整错误堆栈;而 +Release 拥有更小体积(比 Debug 减小 50%)。 + +这也会同时生成一个头文件(`.h` +)供交互使用。详情查看 [Kotlin 官方文档](https://kotlinlang.org/docs/native-c-interop.html) +。 + +可以在 `mirai-core/build/bin/macosArm64/debugShared/` 类似路径找到生成的动态链接库和头文件。 + +#### 链接并构建静态链接库 + +注意,只有 mirai-core 可以构建可用的静态链接库。 + +执行 `:mirai-core:linkDebugStaticHost` +或 `:mirai-core:linkReleaseStaticHost`。Debug 版本会保留调试符号,能显示完整错误堆栈;而 +Release 拥有更小体积(比 Debug 减小 50%)。 + +可以在 `mirai-core/build/bin/macosArm64/debugStatic/` 类似路径找到生成的静态链接库和头文件。 diff --git a/docs/contributing/README.md b/docs/contributing/README.md new file mode 100644 index 000000000..ece8996d7 --- /dev/null +++ b/docs/contributing/README.md @@ -0,0 +1,206 @@ +# 贡献 + +**感谢你来到这里和你对 mirai 做的所有贡献。** + +mirai 欢迎一切形式的代码贡献。你可以通过以下几种途径向 mirai 贡献。 + +[mirai-core-api]: ../../mirai-core-api + +[mirai-core-utils]: ../../mirai-core-utils + +[mirai-core]: ../../mirai-core + +[mirai-console]: ../../mirai-console/backend/mirai-console + +[mirai-console-integration-test]: ../../mirai-console/backend/integration-test + +[mirai-console-codegen]: ../../mirai-console/backend/codegen + +[mirai-console-terminal]: ../../mirai-console/frontend/mirai-console-terminal + +[mirai-conosle-compiler-annotations]: ../../mirai-console/tools/compiler-annotations + +[mirai-conosle-compiler-common]: ../../mirai-console/tools/compiler-common + +[mirai-conosle-intellij]: ../../mirai-console/tools/intellij-plugin + +[mirai-conosle-gradle]: ../../mirai-console/tools/gradle-plugin + +[mirai-bom]: ../../mirai-bom + +[mirai-dokka]: ../../mirai-dokka + +[mirai-core-all]: ../../mirai-core-all + +[mirai-logging]: ../../logging/ + +[mirai-logging-log4j2]: ../../logging/mirai-logging-log4j2 + +[mirai-logging-slf4j]: ../../logging/mirai-logging-slf4j + +[mirai-logging-slf4j-simple]: ../../logging/mirai-logging-slf4j-simple + +[mirai-logging-slf4j-logback]: ../../logging/mirai-logging-slf4j-logback + +# 主仓库 mirai + +当前仓库 mamoe/mirai 包含 mirai 核心模块: + +| 名称 | 描述 | +|------------------------|---------------------| +| mirai-core-utils | 一些工具类,供其他模块使用 | +| mirai-core-api | mirai 机器人核心 API | +| mirai-core | mirai 机器人核心实现 | +| mirai-core-all | 上述三个模块的集合,用于启动器 | +| mirai-console | 插件模式机器人框架后端 | +| mirai-console-terminal | mirai-console 的终端前端 | +| mirai-console-intellij | IntelliJ IDEA 插件 | +| mirai-console-gradle | Gradle 插件 | +| mirai-bom | Maven BOM | +| mirai-logging | 常用日志库转接器 | + +## Git 分支 + +- `1.x`: 1.x 版本的开发 (已停止); +- `dev`: 2.x 版本的开发(当前); +- `-release` 后缀: 某个版本的小更新分支。如 `2.10-release` 会包含 `2.10.x` 小版本的更新。 + +通常请基于 `dev` 分支进行修改。 +基于[版本规范](../Evolution.md#版本规范) +,若一个修改适合发布为小版本更新,我们会从 `dev` 中提取该修复到目标 `-release` 分支。 + +## `mirai-core` 术语 + +根据语境,mirai-core 有时候可能指 `mirai-core` 这个模块,有时候可能指 `mirai-core-utils` +、`mirai-core-api`、 `mirai-core` 这三个模块的整体。 +本文中,`mirai-core` 将特指 `mirai-core` 模块,而用 'core' 或者 'mirai core' +指相关三个模块的整体。 + +## core 多平台架构 + +[HMPP]: https://kotlinlang.org/docs/multiplatform-discover-project.html + +core 三个模块都使用 Kotlin [HMPP] 功能,同时支持 JVM 和 Native +两种平台。你可以在 [Kotlin 官方英文文档][HMPP] 了解 HMPP 模式。 + +core 的源集结构如图所示: + +``` + common + | + /---------------+---------------\ + jvmBase native + / \ / \ + jvm android unix \ + / \ mingwX64 + / \ + darwin linuxX64 + | + * + +``` + +备注: + +- common 包含全平台通用代码,绝大部分代码都位于 common; +- jvmBase 包含针对 JVM 平台的通用代码; +- `` 为 macOS,iOS,WatchOS 等 Apple 平台目标。 + +## 安装 JDK + +需要安装 JDK 才能编译 mirai。mirai 2.12 在如下环境测试可以编译: + +- 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 + +若在其他环境下无法正常编译, 请尝试选择上述一个环境配置。 + +## 构建 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)。 + +## 其他项目支持 + +- 维护社区:可以为 [mirai-console](/mirai-console) + 编写插件并发布到[论坛](https://mirai.mamoe.net/); +- 代码优化:优化任何功能设计或实现, 或是引入一个新的设计; +- 解决问题:在 [issues](https://github.com/mamoe/mirai/issues) 查看 mirai + 正遇到的所有问题,或在 [里程碑](https://github.com/mamoe/mirai/milestones) 查看版本计划; +- 协议支持:[添加新协议支持](ImplementingProtocol.md)。 + +### 加入开发组 + +你可以随时提交 PR 解决任何问题。而若有兴趣,我们也欢迎你加入开发组,请联系 support@mamoe.net + +[mirai-compose]: https://github.com/sonder-joker/mirai-compose + +[plugin-center 服务端]: https://github.com/project-mirai/mirai-plugin-center + +[mirai-api-http]: https://github.com/project-mirai/mirai-api-http + +[project-mirai/docs]: https://github.com/project-mirai/docs + +[docs.mirai.mamoe.net]: https://docs.mirai.mamoe.net + +| 名称 | 描述 | +|:-----------------------:|:----------------------------------------------------------------------------:| +| core 和 console 日常更新 | 在 milestone 安排的日常更新。我们目前版本速度是一个月到两个月发布一个次版本(2.x)。需要日常的开发。 | +| console 后端 | 架构稳定,现在格外需要在易用性上的提升,首先需要一个优化方案,再实现它们。 | +| console 文档 | 根据用户反馈,现在文档十分缺少。需要以用户的身份体验过 console 的人编写用户文档。 | +| 图形前端 [mirai-compose] | 各功能都缺目前尤其缺少对接 console PluginConfig 的图形化配置的实现。 | +| [plugin-center 服务端] | 插件中心正在建设中。后端 Spring,前端 Vuetify。由于开发人员学业繁忙,暂搁置。 | +| plugin-center 社区 | 插件中心计划支持所有语言的插件,因此需要与社区 SDK 作者沟通并帮助它们接入 Console 的 PluginLoader API 和插件中心的要求。 | +| plugin-center console 端 | 需要评估现在 console 架构是否足够支持插件中心及所有语言插件的管理,实现与插件中心的对接。 | +| plugin-center gradle | 对接插件中心,实现通过 Task 上传插件。还没有开始做。 | +| mirai-console-loader | console 启动器。对接插件中心的 API,支持下载和更新插件等。不确定之后是否会有人实现。 | +| IDE 插件 | IntelliJ IDEA 的插件的工作。可以为 mirai 框架添加检查等功能。这个部分目前基本满足需求。 | +| [mirai-api-http] v2 | 日常维护。 | +| [project-mirai/docs] | 用户友好文档自动部署,使用 VuePress , 部署于 [docs.mirai.mamoe.net],目前还有部分超链接错误的问题。 | + +### 里程碑 + +[里程碑](https://github.com/mamoe/mirai/milestones) 为各版本的开发计划. +在完成所有任务后就会发布该版本. + +`Backlog` 为没有设定目标版本的计划. 如果有相关 PR, 这些计划就可能会被确定到一个最近的版本. + +### 开发 mirai-core + +- 使用 IntelliJ IDEA 或 Android Studio +- 安装 IDE + 插件 [kotlin-jvm-blocking-bridge](https://github.com/Him188/kotlin-jvm-blocking-bridge/blob/master/README-chs.md#%E5%AE%89%E8%A3%85-intellij-idea-%E6%88%96-android-studio-%E6%8F%92%E4%BB%B6) +- 若要添加一个 suspend 函数, 请为它添加 `@JvmBlockingBridge`, + 使用 [kotlin-jvm-blocking-bridge](https://github.com/mamoe/kotlin-jvm-blocking-bridge/blob/master/README-chs.md) +- 使用纯 Kotlin 实现 +- 尽量不要引用新的库 +- 遵守 Kotlin 官方代码规范(提交前使用 IDE 格式化代码即可 (commit 时勾选 'Reformat code')) +- 保证二进制兼容性: 在提交前进行 [ABI 验证](VerifyingABI.md) +- 通过 GitHub 的 Pull Request 提交代码,很快就会有相关模块负责人员来审核 + +如果你不太保证自己能达到上述要求也没关系,mirai 感谢你的每一行代码,维护者会审核代码并尽可能帮助你。 diff --git a/docs/contributing/VerifyingABI.md b/docs/contributing/VerifyingABI.md new file mode 100644 index 000000000..5ee556133 --- /dev/null +++ b/docs/contributing/VerifyingABI.md @@ -0,0 +1,13 @@ +# 进行 ABI 验证 + +mirai +通过 [binary-compatibility-validator](https://github.com/Kotlin/binary-compatibility-validator)) +维护 [ABI](https://zh.wikipedia.org/zh-cn/%E5%BA%94%E7%94%A8%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%8E%A5%E5%8F%A3) +稳定性。 + +若要修改 mirai-core-api,可执行 Gradle 任务 `apiCheckAll` 来检验 ABI 兼容性,也可以运行 IDEA +配置 `Check Binary Compatiblity`。 + +若正在添加一个新功能,可以执行 Gradle 任务 `apiDumpAll` 或 IDEA +配置 `Dump API Changes for ...` 来更新记录。这将会生成 `*.api` +文件,文件的变化反映了你的修改情况。请人工审核该文件以确保向下兼容。 \ No newline at end of file diff --git a/docs/contributing/images/run-gradle-tasks-in-idea.png b/docs/contributing/images/run-gradle-tasks-in-idea.png new file mode 100644 index 0000000000000000000000000000000000000000..122439e2d8b52402b19f0ba7e27436775b97a0db GIT binary patch literal 24635 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