我怎样才能 Gradle 在 运行 我的任务之前建立依赖关系?

How can I get Gradle to build dependencies before running my task?

背景:我正在尝试将我自己的领域特定语言的编译器挂接到 Gradle。 DSL 被编译为 Java 源代码,因此我构建了一个在 Java 编译器之前运行的任务。编译器目前无法处理具有依赖关系的多个项目,所以我正在尝试添加它。

我的 DSL 有像 Java 这样的包映射到相同的 Java 包。项目也应如此。在这种情况下,对于每个项目,DSL 源被编译为 Java 源代码,以及元数据(每个编译 class 的 JSON 文件,包含来自 DSL 类型的信息无法映射到 Java 类型的系统)。当项目 A 依赖于 B 时,A 的 DSL 编译过程需要来自 B 的元数据文件。该元数据应与生成和编译的 Java 代码一起作为资源打包到 JAR 文件中,以及可能是手写和编译的 Java 代码。

FoobarPlugin.groovy:

class FoobarPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {

        // create the compileFoobar task
        CompileFoobarTask task = project.getTasks().create('compileFoobar', CompileFoobarTask.class);
        task.group = 'build';
        task.setDescription('Compiles Foobar to Java code.');
        task.sourceDirectory = new File(project.projectDir, "src/main/foobar");
        task.outputDirectory = new File(project.getBuildDir(), "foobar-java");

        // compileFoobar must run before compiling Java code
        project.tasks.compileJava.dependsOn(task);

        // add the task's output folders as Java source folders
        project.sourceSets.main.java.srcDirs += task.outputDirectory;
        project.sourceSets.main.resources.srcDirs += task.outputDirectory;
        project.sourceSets.test.java.srcDirs += task.outputDirectory;
        project.sourceSets.test.resources.srcDirs += task.outputDirectory;

        // Turn project dependencies into task dependencies. We have to delay this until the end of the configuration
        // phase because project dependencies are not fully known until then.
        project.gradle.addBuildListener(new BuildAdapter() {
            @Override
            void projectsEvaluated(Gradle gradle) {
                project.configurations.compile.each {
                    task.dependencyOutputs += it
                }
            }
        });

    }

}

CompileFoobarTask.groovy:

class CompileFoobarTask extends DefaultTask {

    @InputDirectory
    File sourceDirectory;

    @InputFiles
    List<File> dependencyOutputs = new ArrayList<>();

    @OutputDirectory
    File outputDirectory;

    @TaskAction
    void run() {
        FileUtils.write(new File(outputDirectory, "timestamp"), "" + System.currentTimeMillis(), StandardCharsets.UTF_8);
    }

}

build.gradle 来自项目 A:

apply plugin: 'java'
apply plugin: foobar.gradle.FoobarPlugin

repositories {
    mavenCentral()
}

dependencies {
    compile project(':b')
}

build.gradle 来自项目 B:

apply plugin: 'java'
apply plugin: foobar.gradle.FoobarPlugin

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.apache.commons:commons-lang3:3.0'
}

测试运行和输出:

martin@xyz:~/git-repos/gradle-test$ ./gradlew clean a:compileFoobar
adding dependency /home/martin/git-repos/gradle-test/b/build/libs/b.jar to task task ':a:compileFoobar'

> Task :a:compileFoobar
running task ':a:compileFoobar'

BUILD SUCCESSFUL in 1s
4 actionable tasks: 2 executed, 2 up-to-date


martin@xyz:~/git-repos/gradle-test$ ./gradlew clean b:compileFoobar
adding dependency /home/martin/git-repos/gradle-test/b/build/libs/b.jar to task task ':a:compileFoobar'

> Task :b:compileFoobar
running task ':b:compileFoobar'

BUILD SUCCESSFUL in 469ms
4 actionable tasks: 2 executed, 2 up-to-date


martin@xyz:~/git-repos/gradle-test$ ./gradlew clean a:compileJava
adding dependency /home/martin/git-repos/gradle-test/b/build/libs/b.jar to task task ':a:compileFoobar'

> Task :a:compileFoobar
running task ':a:compileFoobar'

> Task :b:compileFoobar
running task ':b:compileFoobar'

BUILD SUCCESSFUL in 487ms
7 actionable tasks: 5 executed, 2 up-to-date


martin@xyz:~/git-repos/gradle-test$ ./gradlew clean b:compileJava
adding dependency /home/martin/git-repos/gradle-test/b/build/libs/b.jar to task task ':a:compileFoobar'

> Task :b:compileFoobar
running task ':b:compileFoobar'

BUILD SUCCESSFUL in 471ms
4 actionable tasks: 3 executed, 1 up-to-date

如您所见,即使我将 b.jar 添加为 a:compileFoobar 的依赖项,Gradle 也不会在 运行 [=33= 之前构建该 JAR ]. Java 插件似乎做了一些不同的事情,因为 运行 a:compileJava 将首先构建 b.jar。我需要做什么才能完成我的任务?

您需要做的是在消费者项目的 compileFoobar 任务和生产者项目的 jar 任务之间显式创建 任务依赖关系(在您的示例中其中 project a 依赖于 project b,您需要创建任务依赖性 a:compileFoobar -> b.jar)

您可以在自定义插件中实现此目的,方法是检查当前项目是否具有 ProjectDependency 类型的依赖项:如果是,则相应地创建任务依赖项。

代码示例(在您的插件 apply() 方法中):

        // Turn project dependencies into task dependencies. We have to delay this until the end of the configuration
        // phase because project dependencies are not fully known until then.
        project.gradle.addBuildListener(new BuildAdapter() {
            @Override
            void projectsEvaluated(Gradle gradle) {
                project.configurations.each { config ->
                    config.dependencies.each { dep ->
                        if (dep instanceof ProjectDependency) {
                            def producerProject = ((ProjectDependency) dep).dependencyProject
                            def producerJarTask = producerProject.tasks.jar
                            println " **** Project $project.name depends on $producerProject.name"
                            println "      => create dependency between $task to $producerJarTask"
                            task.dependsOn(producerJarTask)
                        }
                    }
                }
            }
        })

构建执行:

$  ./gradlew clean a:compileFoobar
 **** Project a depends on b
      => create dependency between task ':a:compileFoobar' to task ':b:jar'
> Task :a:clean
> Task :b:clean
> Task :b:compileFoobar
> Task :b:compileJava NO-SOURCE
> Task :b:processResources NO-SOURCE
> Task :b:classes UP-TO-DATE
> Task :b:jar
> Task :a:compileFoobar