diff --git a/content/posts/2024/version-catalog-plugin-wordaround/index.en.md b/content/posts/2024/version-catalog-plugin-wordaround/index.en.md index 4ff7fcb..d96ba43 100644 --- a/content/posts/2024/version-catalog-plugin-wordaround/index.en.md +++ b/content/posts/2024/version-catalog-plugin-wordaround/index.en.md @@ -3,7 +3,7 @@ title: "A beautiful workaround for accessing Gradle Version Catalogs from Precom # slug: "" # if :slug is in the permalinks configuration, use this to resolve URL conflict with other posts date: 2024-05-12T14:52:55Z # if year month day in the permalinks configuration and other posts have the same date, modify this to resolve URL conflict with other posts lastmod: 2024-05-12T14:52:55Z # no longer needed if enableGitInfo = true -draft: true # remember to change it back to false before opening the PR for publishing +draft: false # remember to change it back to false before opening the PR for publishing authors: [CXwudi] # no quotes featuredImage: "img/featured image.webp" description: "Using the gradle-buildconfig-plugin or the BuildKonfig plugin to access Gradle Version Catalogs from precompiled script plugins" @@ -163,7 +163,7 @@ Several intelligent people have proposed great workarounds to this issue. The mo Are there other workarounds, preferably without hacks? -Fortunately, there is one for the `plugins {}` block mentioned by [this comment](https://github.com/gradle/gradle/issues/15383#issuecomment-1855984127). Since applying external plugins to the precompiled script plugin requires adding the corresponding dependency of the external plugin to the `build.gradle.kts` file, you can apply the version catalog to that dependency. This method assumes that the `settings.gradle.kts` file in the build project [imports the same version catalog](https://docs.gradle.org/current/userguide/platforms.html#sec:importing-catalog-from-file) used in the main project. I also discovered that the method works for setting plugins, as I described in [this forum post](https://discuss.gradle.org/t/how-to-use-version-catalog-in-the-root-settings-gradle-kts-file/44603/5). +Fortunately, there is one for the `plugins {}` block mentioned by [this comment](https://github.com/gradle/gradle/issues/15383#issuecomment-1855984127). Since applying external plugins to the precompiled script plugin requires adding the corresponding dependency of the external plugin to the `build.gradle.kts` file, you can add that dependency to the version catalog. This method assumes that the `settings.gradle.kts` file in the build project [imports the same version catalog](https://docs.gradle.org/current/userguide/platforms.html#sec:importing-catalog-from-file) used in the main project. I also discovered that the method works for setting plugins, as I described in [this forum post](https://discuss.gradle.org/t/how-to-use-version-catalog-in-the-root-settings-gradle-kts-file/44603/5). @@ -274,6 +274,6 @@ dependencies { ## Conclusion -The method that utilizes the gradle-buildconfig-plugin or the BuildKonfig plugin is a beautiful workaround for accessing Gradle Version Catalogs from precompiled script plugins. It is not too hacky, guaranteed to work in any Gradle project, and doesn't have the limitation where it is only accessible from the `plugins {}` block or the `dependencies {}` block. It is a great solution for centralizing version management in your Gradle project. +The method, that utilizes the gradle-buildconfig-plugin or the BuildKonfig plugin for accessing Gradle Version Catalogs from precompiled script plugins, is a beautiful workaround. It is not too hacky, guaranteed to work in any Gradle project, and doesn't have the limitation where it is only accessible from the `plugins {}` block or the `dependencies {}` block. It is a great solution for centralizing version management in your Gradle project. I hope this workaround can help you in your Gradle project. If you have any questions or suggestions, feel free to leave a comment below. 😊 diff --git a/content/posts/2024/version-catalog-plugin-wordaround/index.zh-cn.md b/content/posts/2024/version-catalog-plugin-wordaround/index.zh-cn.md index 7c20f58..4dd1c33 100644 --- a/content/posts/2024/version-catalog-plugin-wordaround/index.zh-cn.md +++ b/content/posts/2024/version-catalog-plugin-wordaround/index.zh-cn.md @@ -1,35 +1,39 @@ --- -title: "Gradle Version Catalog Issue" -slug: "gradle-version-catalog-issue" # if :slug is in the permalinks configuration, use this to resolve URL conflict with other posts -date: 2024-05-12T14:53:10Z # if year month day in the permalinks configuration and other posts have the same date, modify this to resolve URL conflict with other posts -lastmod: 2024-05-12T14:53:10Z # no longer needed if enableGitInfo = true -draft: true # remember to change it back to false before opening the PR for publishing -authors: [] # no quotes -featuredImage: "" -description: "" +title: "从预编译脚本插件访问Gradle版本目录的漂亮解决方案" +# slug: "" # 如果在永久链接配置中有:slug,使用这个来解决与其他帖子的URL冲突 +date: 2024-05-12T14:52:55Z # 如果永久链接配置中有年月日,并且其他帖子有相同的日期,修改这个来解决URL冲突 +lastmod: 2024-05-12T14:52:55Z # 如果启用了enableGitInfo,则不再需要 +draft: false # 发布前记得将其改为false +authors: [CXwudi] # 没有引号 +featuredImage: "img/featured image.webp" +description: "使用gradle-buildconfig-plugin或BuildKonfig插件从预编译脚本插件访问Gradle版本目录" # license: 'CC BY 4.0' -# need quotes for all three -tags: [] -categories: [] +# 所有三个都需要引号 +tags: [devops] +categories: [tech] series: [] series_weight: -# you can copy any config from [params.page] to here to override global default +# 你可以从[params.page]复制任何配置到这里来覆盖全局默认 -# outdatedArticleReminder: # uncomment to enable, default is false in config +# outdatedArticleReminder: # 取消注释以启用,默认在配置中为false # enable: true # reminder: 180 # warning: 365 -# sponsor: # uncomment to disable, default is false in config - # enable: false -# table: # uncomment to disable, default is true +sponsor: + enable: true + bio: "写完这篇文章后,我感到非常疲惫 😫。我需要一杯咖啡 ☕ 来提神。如果你喜欢我的Gradle 🐘 解决方案,你介意请我喝一杯咖啡吗?感谢你的支持!🤗" + custom: "
在ko-fi.com给我买咖啡
" +# table: # 取消注释以禁用,默认为true # sort: false -# comment: # uncomment to disable comment system +# comment: # 取消注释以禁用评论系统 # enable = false -# lightgallery: true # uncomment if using the better image shortcode +lightgallery: true # 如果使用更好的图片shortcode则取消注释 +code: + maxShownLines: 50 seo: - images: [] # same as featuredImage + images: ["img/featured image.webp"] # 与featuredImage相同 --- 太长不看:在 `buildSrc/build.gradle.kts` 中应用 [gradle-buildconfig-plugin](https://github.com/gmazzo/gradle-buildconfig-plugin) 或 [BuildKonfig](https://github.com/yshrsmz/BuildKonfig) 插件。 @@ -39,7 +43,7 @@ seo: **来源**: [Medium](https://medium.com/@gopalsays108/android-gradle-version-catalog-by-gopal-cf459e90fb92) {{< /admonition >}} {{< admonition type=info title="注意" open=true >}} -此文章是从本站英文版原文通过[什么工具]翻译并适当修改而来,可能会出现语言不自然等Bug,请予以谅解。 +此文章是从本站英文版原文通过GPT-4 Turbo翻译并适当修改而来,可能会出现语言不自然等Bug,请予以谅解。 {{< /admonition >}} ## 简介 @@ -129,3 +133,150 @@ Gradle官方文档推荐两种[构建多模块项目的结构](https://docs.grad 它看起来就像一个普通的Gradle项目,对吧? 所以,接下来就是precompiled script plugin的真相了。 + +### precompiled script plugin的真相 + +你是否曾经好奇为什么precompiled script plugin会被放在`src/main/kotlin`文件夹中?🤔 就像上面的`shared-build-conventions.gradle.kts`示例。它能否就留在构建项目的第一层,比如`build-logic/shared-build-conventions.gradle.kts`? + +这里,我想邀请你观看{{}}的一个视频,标题为[Understanding Gradle #25 – Using Java to configure builds](https://youtu.be/XnVZdMROVG8?list=PLWQK2ZdV4Yl2k2OmC_gsjDpdIBTN0qqkE&t=263)。基本上,每个Gradle脚本都是[`Plugin`](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html)接口的一个实现。构建脚本`build.gradle.kts`对应`Plugin`,设置脚本`settings.gradle.kts`对应`Plugin`。 + +{{}} + +因此,放在`src/main/`文件夹中的precompiled script plugin也是业务逻辑代码,但这里的业务逻辑是你主项目的构建逻辑。这种代码通常从你的`class MyPlugin implements Plugin`实现中的`apply(Project project)`方法开始。 + +### 在`src/main/kotlin`中使用version catalog?🤔 + +现在,让我们回顾一下[gradle/gradle#15383](https://github.com/gradle/gradle/issues/15383)中的声明: + +> We want to make the version catalogs accessible to precompiled script plugins + +翻译过来: + +> 我们希望使version catalog对precompiled script plugin可见 + +利用我们上面回顾的知识来翻译这句话,它就变成了: + +“一个名为`libs.versions.toml`的文件本意是用在`build.gradle.kts`文件中。现在我们想在`src/main/kotlin`文件夹中的业务代码中使用它。” + +听起来很奇怪,不是吗?🤔 + +这就是为什么这个问题很难解决的原因,因为这是一个设计问题,违反了关注点分离原则。 + +## 现有的workaround + +一些聪明的人提出了几个绝妙的workaround。最著名的一个来自{{}}的[评论](https://github.com/gradle/gradle/issues/15383#issuecomment-779893192),其中你需要添加一个隐秘的Gradle内部文件到`dependencies {}`块中。这个方法虽然有效,但非常hacky,不能保证在任何Gradle项目中都有效(至少我没弄成功过 😕),[在`plugins {}`块中不起作用](https://github.com/gradle/gradle/issues/15383#issuecomment-900569305),而且这个workaround依赖于一个可能随时变更的Gradle内部API。 + +还有其他不需要hack的workaround吗? + +幸运的是,有一个针对`plugins {}`块的workaround,如[此评论](https://github.com/gradle/gradle/issues/15383#issuecomment-1855984127)中提到的。由于将外部插件应用到precompiled script plugin需要将外部插件的相应依赖添加到`build.gradle.kts`文件中,你可以将哪个依赖放进version catalog中。这种方法假设构建项目中的`settings.gradle.kts`文件[导入了与主项目中使用的相同的version catalog](https://docs.gradle.org/current/userguide/platforms.html#sec:importing-catalog-from-file)。我还发现这种方法适用于设置插件,正如我在[此论坛帖子](https://discuss.gradle.org/t/how-to-use-version-catalog-in-the-root-settings-gradle-kts-file/44603/5)中描述的那样。 + +{{}} + +我还找到了一个不需要hack的`dependencies {}`块的workaround,并在[此评论](https://github.com/gradle/gradle/issues/15383#issuecomment-1858465843)中进行了描述。这种workaround利用了[Gradle platform](https://docs.gradle.org/current/userguide/platforms.html#sub:using-platform-to-control-transitive-deps)和[Gradle的构建阶段](https://docs.gradle.org/current/userguide/build_lifecycle.html#sec:build_phases)来实现目的。然而,这种方法非常麻烦,因为添加/移除依赖需要在你的项目中进行三处修改。 + +总的来说,非hacky的workaround可以覆盖`plugins {}`块和`dependencies {}`块。在大多数情况下,这已经足够。但是对于[extensions](https://docs.gradle.org/current/userguide/implementing_gradle_plugins_precompiled.html#sec:getting_input_from_the_build)怎么办呢?例如,如果precompiled script plugin应用了[Micronaut Gradle插件](https://micronaut-projects.github.io/micronaut-gradle-plugin/latest/index.html),如何使用version catalog在`micronaut {}`扩展中设置Micronaut框架的版本呢?🤷‍♂️ + +{{}} + +有一天,我在研究Micronaut框架时,意识到我无法应用{{}}在[Understanding Gradle #09 – Centralizing Dependency Versions](https://www.youtube.com/watch?v=8044F5gc1dE&list=PLWQK2ZdV4Yl2k2OmC_gsjDpdIBTN0qqkE&index=9)中提到的任何集中版本管理解决方案。 + +所以我向Micronaut团队提出了一个[功能请求](https://github.com/micronaut-projects/micronaut-gradle-plugin/issues/681)。我甚至深入研究了插件的源代码,并指出了阻止使用version catalog或Gradle platform的[代码](https://github.com/micronaut-projects/micronaut-gradle-plugin/blob/cc84332f5635e3da7c71e81460659f41fd36ae2b/minimal-plugin/src/main/java/io/micronaut/gradle/PluginsHelper.java#L48-L60)。 + +创建了gradle/gradle#15383问题的那个人,{{< person name="Cédric Champeau" nick="melix" text="Micronaut团队和GraalVM团队成员,为多个Micronaut工具和GraalVM Native Build Tools做出了贡献" picture="https://avatars.githubusercontent.com/u/316357?v=4" >}}回应了我。😲 + +他很快提出了[一个PR](https://github.com/micronaut-projects/micronaut-gradle-plugin/pull/701),在Micronaut Gradle插件中增加了一个新选项`importMicronautPlatform`,可以设置为`false`以允许你使用Gradle platform实现集中版本管理。一旦我能够使用Gradle platform,我就能够使用我上面提到的workaround来使用version catalog。 + +今天,你可以在Micronaut Gradle插件的[文档](https://micronaut-projects.github.io/micronaut-gradle-plugin/latest/index.html#_micronaut_library_plugin)中看到这个选项。 + +{{< /admonition >}} + +## 现在开始介绍全新漂亮的workaround 🤩 + +上面提到的所有workaround或多或少都在做一件事:“发送”原本应该用在Gradle构建脚本中的变量到`src/main/kotlin`文件夹中的业务代码里。 + +有一天,我无意中发现了两个Gradle插件:[gradle-buildconfig-plugin](https://github.com/gmazzo/gradle-buildconfig-plugin)和[BuildKonfig](https://github.com/yshrsmz/BuildKonfig)插件。 + +这两个插件通常用于Kotlin跨平台项目(通常是跨平台Compose应用),用来生成一个包含你在`build.gradle.kts`脚本中定义的配置变量的Kotlin文件。 + +例如,如果你有: + +```kotlin {title="build.gradle.kts"} +plugins { + // ... + id("com.github.gmazzo.buildconfig") version +} + +buildConfig { + className("MyConfig") // 强制类名。默认为'BuildConfig' + packageName("com.foo") // 强制包名。默认为'${project.group}' + + buildConfigField(String::class.java, 'APP_NAME', "my-project") +} +//... +``` + +你会得到: + +```kotlin {title="com.foo.MyConfig.kt"} +package com.foo + +object MyConfig { + const val APP_NAME: String = "my-project" +} +``` + +突然间,我意识到如果我将这个插件应用到构建项目中的`build.gradle.kts`会怎样?😲 + +```toml {title="gradle/libs.versions.toml"} +[versions] +java = "21" +slf4j = "2.0.13" + +[libraries] +slf4j-api = {module = "org.slf4j:slf4j-api", version.ref = "slf4j"} +``` + +```kotlin {title="build-logic/conventions/build.gradle.kts"} +plugins { + // ... + id("com.github.gmazzo.buildconfig") version +} + +buildConfig { + className("VersionCatalog") // 强制类名。默认为'BuildConfig' + packageName("my.util") // 强制包名。默认为'${project.group}' + + buildConfigField(Int::class.java, "JAVA_VERSION", libs.versions.java.get().toInt()) + buildConfigField(String::class.java, "SLF4J_API", libs.dep.slf4j.get().toString()) +} +``` + +那么,我的precompiled script plugin能访问`JAVA_VERSION`和`SLF4J_API`变量吗? + +你擦怎么着?它居然真的有效!😱 + +```kotlin {title="build-logic/conventions/src/main/kotlin/shared-build-conventions.gradle.kts"} +import my.util.VersionCatalog + +// 其他配置 + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(VersionCatalog.JAVA_VERSION)) + } +} + +dependencies { + implementation(VersionCatalog.SLF4J_API) +} + +// 其他配置 +``` + +{{
}} + +## 结论 + +利用gradle-buildconfig-plugin或BuildKonfig插件的方法是从precompiled script plugin访问Gradle的version catalog是一个漂亮的workaround。它不是太hacky,能确保在任何Gradle项目中都行得通,而且没有仅在`plugins {}`块或`dependencies {}`块可访问的限制。这是一个在你的Gradle项目中集中版本管理的绝佳解决方案。 + +我希望这个workaround能帮助到你的Gradle项目。如果你有任何问题或建议,欢迎在下面留言。😊