我怎样才能 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
背景:我正在尝试将我自己的领域特定语言的编译器挂接到 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