gradle - 使用Gradle Kotlin DSL在Gradle中进行样板项目配置

标签 gradle kotlin gradle-kotlin-dsl

我目前正在尝试改善我们的项目共享其配置的方式。我们所有的库和微服务都有很多不同的多模块gradle项目(即许多git repos)。

我的主要目标是:

  • 不要在每个项目中都复制我的Nexus存储库配置(而且,我可以放心地认为URL不会更改)
  • 使我的自定义Gradle插件(发布到Nexus)可用于每个项目,且模板/重复项最少(它们应可用于每个项目,并且该项目唯一关心的是它使用的版本)
  • 没魔力-开发人员应该清楚如何配置

  • 我当前的解决方案是使用以下脚本的自定义gradle分发:
  • mavenLocal()和我们的Nexus存储库添加到项目存储库中(非常类似于Gradle init script documentation example,不同之处在于它添加了存储库并对其进行了验证)
  • 配置了一个扩展,该扩展允许将gradle插件添加到buildscript类路径中(使用this workaround)。它还将我们的Nexus存储库添加为构建脚本存储库,因为这是托管插件的位置。我们有很多用于各种样板的插件(基于Netflix出色的nebula plugins构建):标准项目设置(kotlin设置,测试设置等),发布,发布,文档等,这意味着我们的项目build.gradle文件几乎完全是用于依赖项的。

  • 这是初始化脚本(已清除):
    /**
     * Gradle extension applied to all projects to allow automatic configuration of Corporate plugins.
     */
    class CorporatePlugins {
    
        public static final String NEXUS_URL = "https://example.com/repository/maven-public"
        public static final String CORPORATE_PLUGINS = "com.example:corporate-gradle-plugins"
    
        def buildscript
    
        CorporatePlugins(buildscript) {
            this.buildscript = buildscript
        }
    
        void version(String corporatePluginsVersion) {
            buildscript.repositories {
                maven {
                    url NEXUS_URL
                }
            }
            buildscript.dependencies {
                classpath "$CORPORATE_PLUGINS:$corporatePluginsVersion"
            }
        }
    
    }
    
    allprojects {
        extensions.create('corporatePlugins', CorporatePlugins, buildscript)
    }
    
    apply plugin: CorporateInitPlugin
    
    class CorporateInitPlugin implements Plugin<Gradle> {
    
        void apply(Gradle gradle) {
    
            gradle.allprojects { project ->
    
                project.repositories {
                    all { ArtifactRepository repo ->
                        if (!(repo instanceof MavenArtifactRepository)) {
                            project.logger.warn "Non-maven repository ${repo.name} detected in project ${project.name}. What are you doing???"
                        } else if(repo.url.toString() == CorporatePlugins.NEXUS_URL || repo.name == "MavenLocal") {
                            // Nexus and local maven are good!
                        } else if (repo.name.startsWith("MavenLocal") && repo.url.toString().startsWith("file:")){
                            // Duplicate local maven - remove it!
                            project.logger.warn("Duplicate mavenLocal() repo detected in project ${project.name} - the corporate gradle distribution has already configured it, so you should remove this!")
                            remove repo
                        } else {
                            project.logger.warn "External repository ${repo.url} detected in project ${project.name}. You should only be using Nexus!"
                        }
                    }
    
                    mavenLocal()
    
                    // define Nexus repo for downloads
                    maven {
                        name "CorporateNexus"
                        url CorporatePlugins.NEXUS_URL
                    }
                }
            }
    
        }
    
    }
    

    然后,通过将以下内容添加到root build.gradle文件中,配置每个新项目:
    buildscript {
        // makes our plugins (and any others in Nexus) available to all build scripts in the project
        allprojects {
            corporatePlugins.version "1.2.3"
        }
    }
    
    allprojects  {
        // apply plugins relevant to all projects (other plugins are applied where required)
        apply plugin: 'corporate.project'
    
        group = 'com.example'
    
        // allows quickly updating the wrapper for our custom distribution
        task wrapper(type: Wrapper) {
            distributionUrl = 'https://com.example/repository/maven-public/com/example/corporate-gradle/3.5/corporate-gradle-3.5.zip'
        }
    }
    

    虽然这种方法行之有效,但允许可复制的构建(不同于我们以前的应用程序通过URL应用构建脚本-当时是不可缓存的),并且允许脱机工作,这确实使它有些不可思议,我想知道我是否可以做得更好。

    这全部是由Gradle开发者Stefan Oehme阅读a comment on Github触发的,该声明表示构建应该可以在不依赖初始化脚本的情况下工作,即,初始化脚本应该只是装饰性的,并且可以像已记录的示例那样进行操作-防止未经授权的回购等。

    我的想法是编写一些扩展功能,使我可以将Nexus repo和插件添加到内部版本中,就像它们内置在gradle中一样(类似于Gradle Kotlin DSL提供的扩展功能 gradleScriptKotlin() kotlin-dsl() )。

    因此,我在kotlin gradle项目中创建了扩展功能:
    package com.example
    
    import org.gradle.api.artifacts.dsl.DependencyHandler
    import org.gradle.api.artifacts.dsl.RepositoryHandler
    import org.gradle.api.artifacts.repositories.MavenArtifactRepository
    
    fun RepositoryHandler.corporateNexus(): MavenArtifactRepository {
        return maven {
            with(it) {
                name = "Nexus"
                setUrl("https://example.com/repository/maven-public")
            }
        }
    }
    
    fun DependencyHandler.corporatePlugins(version: String) : Any {
        return "com.example:corporate-gradle-plugins:$version"
    }
    

    计划在我的项目的build.gradle.kts中使用它们,如下所示:
    import com.example.corporateNexus
    import com.example.corporatePlugins
    
    buildscript {
    
        repositories {
            corporateNexus()
        }
    
        dependencies {
            classpath(corporatePlugins(version = "1.2.3"))
        }
    }
    

    但是,在buildscript块中使用时,Gradle无法看到我的函数(无法编译脚本)。尽管在正常的项目存储库/依赖项中使用它们仍然可以正常工作(它们可见且按预期运行)。

    如果可行,我希望将jar bundle 到我的自定义发行版中,这意味着我的init脚本可以执行简单的验证,而不是隐藏神奇的插件和回购配置。扩展功能不需要更改,因此在插件更改时不需要发布新的Gradle发行版。

    我试过的
  • 将我的jar添加到测试项目的buildscript类路径(即buildscript.dependencies)中-不起作用(也许这在设计上不起作用,因为在同一块中引用的buildscript中添加依赖项似乎不正确)
  • 将函数放在buildSrc中(该功能适用​​于常规项目deps / repos,但不适用于buildscript,但不是真正的解决方案,因为它只是移动样板)
  • 将 jar 放到发行版
  • lib文件夹中

    所以我的问题实际上可以归结为:
  • 我要实现的目标是否可能(是否可以使buildScript块可见自定义类/函数)?
  • 是否有更好的方法来配置公司Nexus回购协议(protocol),并以最少的样板配置在许多单独的项目(即完全不同的代码库)中提供自定义插件(已发布到Nexus)?
  • 最佳答案

    如果您想从Gradle Kotlin DSL的所有优点中受益,您应该努力使用plugins {}块来应用所有插件。参见https://github.com/gradle/kotlin-dsl/blob/master/doc/getting-started/Configuring-Plugins.md

    您可以在设置文件中管理插件存储库和解析策略(例如其版本)。从Gradle 4.4开始,可以使用Kotlin DSL aka settings.gradle.kts编写此文件。参见https://docs.gradle.org/4.4-rc-1/release-notes.html

    考虑到这一点,您便可以使用一个集中的Settings脚本插件来进行设置并将其应用到您的生成settings.gradle.kts文件中:

    // corporate-settings.gradle.kts
    pluginManagement {
        repositories {
            maven {
                name = "Corporate Nexus"
                url = uri("https://example.com/repository/maven-public")
            }
            gradlePluginPortal()
        }
    }
    

    和:
    // settings.gradle.kts
    apply(from = "https://url.to/corporate-settings.gradle.kts")
    

    然后,在项目构建脚本中,您可以简单地从公司资源库中请求插件:
    // build.gradle.kts
    plugins {
        id("my-corporate-plugin") version "1.2.3"
    }
    

    如果您希望多项目构建中的项目构建脚本不重复插件版本,则可以通过在根项目中声明版本来使用Gradle 4.3。请注意,如果所有版本都使用相同的插件版本,则也可以使用settings.gradle.ktspluginManagement.resolutionStrategy中设置版本。

    另请注意,要使所有这些工作正常进行,您的插件必须使用其plugin marker artifact发布。使用java-gradle-plugin插件可以轻松完成此操作。

    关于gradle - 使用Gradle Kotlin DSL在Gradle中进行样板项目配置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43657807/

    相关文章:

    android - 与 Gradle 的条件依赖关系,这可能吗?

    android - 如何使用 gradle 1.12 生成 html 报告?

    Java 与 Kotlin 的兼容性

    kotlin - Jacoco不生成xml文件

    gradle - 在以下任何来源中都找不到插件 [id : 'org.jetbrains.kotlin.jvm' , 版本: '1.2.71']

    java - Firebase先连接到应用程序,然后出现Internet错误并且无法第二次连接

    java - 具有多个 JDK 的 Gradle Maven 发布插件

    java - Kotlin 方法接受内联类作为参数 - 如何从 Java 访问?

    android-studio - 在Android Studio中使用导航框架时,如何知道保留的类名或关键字?