How do I do one last bit of code generation and compilation after the main compilation?


(monty.zukowski) #1

So after I have done the compilation of my core subproject, I have a code generator (just compiled) that needs to run, and then that class compiled in as well. It’s probably easiest to do this as a separate subproject, but I would prefer to have it live in the same directory with all the core source.

So, question one is, can I have two subprojects that live in the same directory? (One is a subdirectory of the other.)

If not, this is close to what I want to do, but I haven’t got the dependencies right, and the classpath is wrong as well. I want it to be able to use the classes just compiled in the compileJava step. I thought classpath=configurations.runtime would do that, but it doesn’t have the newly compiled classes in there, just the libraries it needs.

So the coercionGenerate task is supposed to run the code generator just compiled in by compileJava, but my runtime isn’t correct, and the inputs and outputs aren’t triggering this task to run as I would have expected it to.

task coercionGenerate(type: JavaExec, dependsOn:compileJava) {
    inputs.file file('src/main/java/com/temboo/arcturus/util/CoercionGeneratorTask.java')
    outputs.file file('src/main/java/com/temboo/arcturus/coercion/TypeCoercion.java')
    main = 'com.temboo.arcturus.util.CoercionGeneratorTask'
    classpath = configurations.runtime
    args = [file('src/main/java/com/temboo/arcturus/coercion/TypeCoercion.java')]
}

This second task is supposed to depend on the above task, and compile the one file which it created, with the .class file going where all the other classfiles went during compileJava.

task coercionCompile(type:JavaCompile, dependsOn:coercionGenerate) {
    inputs.file file('src/main/java/com/temboo/arcturus/coercion/TypeCoercion.java')
    classpath = configurations.runtime
    source = file('src/main/java/com/temboo/arcturus/coercion/TypeCoercion.java')
}

Is there an easy way to make this work or do I really need to do surgery and make this a bonafide subproject?

Thanks,

Monty


(Scott Palmer) #2

I have this same need.

I want to configure it as:

  • compile the ‘main’ sourceset - exec a class that was just compiled which will generate new .java files into to another sourceset (e.g. ‘generated’) - compile the ‘generated’ source set - include all of the classes in the final output

I’m starting with something like this:

sourceSets {

generated {

compileClasspath += sourceSets.main.runtimeClasspath

java {

srcDir ‘src-generated/java’

}

}

}

And I have a task like this:

task generateCode(type: JavaExec, dependsOn: [‘classes’]) {

classpath sourceSets.main.runtimeClasspath

main = ‘my.code.generator.Main’

args ‘-genSrc’

}

Now the generateCode step works by itself… but the dependency really needs to be only the compilation of the ‘main’ source set, and then I have to compile the ‘generated’ source set only after this runs.

I’m not sure how to set that up.


(Perryn Fowler) #3

Hi swpalmer,

I’m not totally sure what you were asking but I think the following seems to do what you want

apply plugin: 'java'
  ext.generatedSourceDir = "src/generated/java"
  sourceSets {
      generated {
         java {
              compileClasspath += sourceSets.main.runtimeClasspath
              srcDir generatedSourceDir
          }
      }
  }
    task generateCode(type: JavaExec, dependsOn:['compileJava']) {
      classpath sourceSets.main.runtimeClasspath
      main = 'my.code.generator.Main'
      args '-genSrc'
      workingDir =
generatedSourceDir
}
  compileGeneratedJava.dependsOn(generateCode)
jar.dependsOn(generatedClasses)
jar.from sourceSets.generated.output

Personally I would consider moving the location of the generated sources to the build directory so that they can be cleaned easily.

Monty - Does this help you too?


(monty.zukowski) #4

I’m not sure what this means:

compileGeneratedJava.dependsOn(generateCode)

Is compileGeneratedJava a task defined somewhere else, or does it come into existence because of the

sourceSets{ generated ...}

code?

I tried a few things copying and pasting your code and modifying it for me, still nothing is triggering my two tasks, even though I’ve set up a dependency:

compileJava {
    dependsOn coercionGenerate
    source file("src/main/java/com/temboo/arcturus/coercion/TypeCoercion.java")
}

I also tried adding a sourceSet, not sure if that’s equivalent to the source statement above or not

sourceSets {
      main {
          java {
              srcDir 'com/temboo/arcturus/coercion/'
          }
      }
  }

Running in --debug mode, I see

09:58:44.712 [INFO] [org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter] Skipping task ‘:coercion:compileJava’ as it has no source files.

That would be because my dependency specified for compileJava isn’t firing, but I don’t understand why not. The same statement is working for me in the core subproject for generating antlr files. Something is amiss when trying to apply it here though.


(monty.zukowski) #5

Actually, now that I look deeper, it looks like my coercion.gradle file is not being read, and that gradle thinks the ‘coercion’ subproject is just an empty subproject.

I have this up in my root level settings.gradle:

include "coercion"
project(":coercion").buildFileName = "coercion.gradle"

How could I debug this to see that it is actually being loaded?


(Perryn Fowler) #6

Hi Monty

Yes ‘compileGeneratedJava’ comes into existence because of the ‘generated’ source set. You can read up about SourceSets in the Java Plugin documentation: http://www.gradle.org/docs/current/userguide/java_plugin.html

I can’t really tell from your snippets what you are trying to do or how you are expecting things to work, but I can see a few things that look wrong:

configurations are for managing dependencies ‘configurations.runtime’ represents the set of dependency libraries needed by your code at runtime, not your classes themselves. You are probably looking for ‘sourceSets.main.runtimeClasspath’

You should use the ‘source’ method on JavaCompile to add source to be compiled rather than trying to directly set the source property

you can see if any file is loaded by adding a println

Perryn


(monty.zukowski) #7

Thanks for the documentation pointer, now I have a much better idea of what is going on. You were right about the classpath too. In the end I was able to do almost everything I want using a new source set, and a little refactoring of the original source will let me do the code generation in a cleaner way.

Between this and the integrationTest example, I came up with the code below, which now lives in the core subproject instead of being its own subproject:

sourceSets {
    coercion {
        java.srcDir file('src/main/java/com/temboo/arcturus/coercion/')
    }
}
task coercionGenerate(type: JavaExec, dependsOn:jar) {
    outputs.file file('src/main/java/com/temboo/arcturus/coercion/TypeCoercion.java')
    main = 'com.temboo.arcturus.util.CoercionGeneratorTask'
    classpath = sourceSets.main.runtimeClasspath
    args = [file('src/main/java/com/temboo/arcturus/coercion/TypeCoercion.java')]
}
  compileCoercionJava {
    dependsOn coercionGenerate
}

This is awkward because the generated code is in a folder that has already been compiled by the main java task. I really do need to move it to its own directory so it can be a proper subproject.