在多模块项目中,Gradle能否将插件构建为一个模块,然后在同一个构建中使用该插件?

在多模块项目中,Gradle能否将插件构建为一个模块,然后在同一个构建中使用该插件?,gradle,plugins,multi-module,Gradle,Plugins,Multi Module,我们有一个Gradle项目,有很多模块。其中一个模块是一个自定义代码生成器,作为Gradle插件编写。我们希望稍后在同一总体多模块构建中的另一个模块中运行该代码生成器插件,以便测试代码生成器 我们知道如何动态地创建一个单独的项目并在其中运行代码生成器,但是我们需要在主项目中运行代码生成器,而不是在临时测试项目中 我们尝试过的东西都不管用,Gradle文档似乎也没有解决这个问题。这似乎是Gradle设计的基础,因为构建中使用的整个插件集基本上是一个单独的程序,一开始就组装好了。试图添加一个刚刚构建

我们有一个Gradle项目,有很多模块。其中一个模块是一个自定义代码生成器,作为Gradle插件编写。我们希望稍后在同一总体多模块构建中的另一个模块中运行该代码生成器插件,以便测试代码生成器

我们知道如何动态地创建一个单独的项目并在其中运行代码生成器,但是我们需要在主项目中运行代码生成器,而不是在临时测试项目中

我们尝试过的东西都不管用,Gradle文档似乎也没有解决这个问题。这似乎是Gradle设计的基础,因为构建中使用的整个插件集基本上是一个单独的程序,一开始就组装好了。试图添加一个刚刚构建的插件后,事实似乎不受支持,或者我们错过了一些东西

到目前为止,我们所能想到的最好的方法是用Java实现插件(Kotlin也可以工作),因此Gradle插件只是实现上的一层薄薄的Gradle外壳,在另一个模块中运行代码生成器时直接调用Java实现。这是可行的,但这意味着我们实际上并没有测试代码生成器的Gradle部分


Maven(、和)本机支持这一点,这并不奇怪,因为Maven中的每个插件都在单独的类装入器中运行。如果在Gradle中不可能,这将是Gradle没有功能奇偶校验的少数情况之一。

一种黑客方法是通过Gradle的测试工具包运行程序运行新编译的插件

一种更简洁的方法是将插件编写为编写到Gradle API的薄壳代码,将实际工作委托给普通的旧Java(或Kotlin)实用程序方法。这有许多优点:

  • 您可以对实用程序方法进行单元测试
  • 您可以将实用程序方法用于与插件无关的其他目的
  • 您可以直接从项目中的其他模块调用实用程序方法,从而完成插件的功能,如果您可以构建它,然后在同一构建中调用它

  • 来扩展上述答案

    不要像调用插件一样调用插件,而是添加一个main方法,该方法接受Gradle插件配置传递给插件的相同参数

    然后使用Gradle的Java exec任务调用插件的main:

    task(generateFoo, type: JavaExec) {
        main = 'com.bar.Foo'
        classpath = configurations.runtimeClasspath
        args = ["arg1", "${projectDir}/src/generated/java"]
    }
    
    注意args:这些信息与以前通过Gradle配置传入的信息相同:

    apply plugin: 'foo-plugin'
    generateFoo {
        theArg "arg1"
        outputDir "${projectDir}/src/generated/java"
    }
    
    因为Java exec使用的运行时类路径是调用模块的运行时类路径,所以您可能会遇到运行时类加载器问题

    如果发生这种情况,很容易修复。只需将重写的插件更改为胖罐子:

    task fatJar(type: Jar) {
        manifest {
            attributes 'Implementation-Title': 'Foo Fat JAR', 'Main-Class': 'com.bar.Foo'
        }
        baseName = project.name + '-exec'
        from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
        with jar
    }
    artifacts {
        archives fatJar
    }
    
    然后用Java exec执行fat jar:

      def fooGenerate = task(generateFoo, type: JavaExec) {
          main = 'com.bar.Foo'
          classpath = files("${projectDir}/../foo-plugin-module/build/libs/foo-plugin-module-exec.jar")
          args = ["arg1", "${projectDir}/src/generated/java"]
      }
    
    最后,使依赖模块的编译任务依赖于代码生成:

    compileJava.mustRunAfter fooGenerate
    
    如果使用fatJar方法,您甚至不需要在依赖模块中声明实现项目(“:foo”)

    也可以为此()使用Gradle的复合构建