Bosh
Bosh

Reputation: 8738

Gradle: Can I compile code that depends on its own output?

This is a strange question, but it's not theoretical...

  1. I'd like to make a Gradle project that uses a buildSrc with a java project inside. That java project defines some classes used in the build process.

  2. The nasty trick is, the Gradle project produces a bunch of outputs, including modified classes that belong to the Java project of the buildSrc itself.

Is there any way to express this with Gradle?

The only solution I have in mind right now is: run the whole build script twice in a row. Is there any way to avoid that? For example, by generating the modified code for buildSrc, re-compiling the buildSrc and then generating the additional outputs of the main Gradle project?

Upvotes: 4

Views: 1157

Answers (2)

Bosh
Bosh

Reputation: 8738

Okay, building on Michael Easter's awesome example, I can have the main-level build call a GradleBuild on one of its own tasks:

task generateNewCode() << {
  println("tracer top build")

  // BuildTool is a Java class defined in `buildSrc`
  // ... and it has a cyclic dependency on its own 
  // output (eek -- but that's what I'm dealing with!)
  BuildTool.generateNewCode();
}

task generateDocs(type: GradleBuild) {
  buildFile='build.gradle'
  tasks = ['generateDocs_Real']
}

task generateDocs_Real << {
  BuildTool.outputDocumentation();
}

generateDocs.dependsOn generateNewCode

Then I can invoke gradle generateDocs to do:

  1. Codegen (creating new java classes in buildSrc
  2. Recompile buildSrc
  3. Generate documentation using helpers from the recompiled buidSrc

Upvotes: 2

Michael Easter
Michael Easter

Reputation: 24468

I believe that section 59.4 of the Gradle doc can help you.

Using Gradle 1.8, I tried to "run another Gradle build from a build", where the other Gradle build is buildSrc.

This does not involve codegen, but might be enough to help.

To replicate, I have a simple Java project in buildSrc, with a build.gradle that looks like this:

apply plugin: 'java'

build << { 
    println "TRACER: hello from buildSrc java build"
}

task compile2() << {
    println "TRACER: hello from buildSrc compile2"
}

The 'build' task is called automatically via the buildSrc mechanism. The goal is to call 'compile2' from the root. At the root, the build.gradle looks like this:

task build1() << {
    println "TRACER: top-level build1" 
}

task build2(type: GradleBuild) {
    buildFile = 'buildSrc/build.gradle'
    tasks = ['compile2']
}

build2.dependsOn build1

At the root level, the output is as follows:

$ gradle build2

:buildSrc:compileJava etc etc
TRACER: hello from buildSrc java build
TRACER: top-level build1
TRACER: hello from buildSrc compile2

This shows that:

  • the Java project in buildSrc is compiled
  • the root 'build1' is called (compile your main project here)
  • the buildSrc 'compile2' is called

The classpath and codegen is nasty but may be straight-forward.

Upvotes: 1

Related Questions