Proper strategies for generating sources and compiling generated and non-generated code together?

I’m stumbling through converting a Maven build to Gradle. The build has to generate code with XJC and compile and package the generated and custom source together. I believe I’m generating the source correctly now, but I’m still trying to figure out the proper way to compile the generated source along with the custom source. After this, I’ll have to package up a WAR and EAR, but I’ll deal with those one step at a time.

I am using the user guide as a resource, but I’m finding the user guide does a good job of “listing” things (and formats them badly in the PDF sometimes), but it seems to fall short in describing how to actually do things. I also found references in the forum, but some of those references are quite old, and I’m not sure if they’re still valid.

This is an excerpt of my current build script. This currently barfs on my futile attempt to add to the compile classpath (the line is marked).

Don’t hesitate to give general advice about other things that you see here.

apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'war'
  sourceCompatibility = 1.7
targetCompatibility = 1.7
  repositories {
     maven { url "http://repo1.maven.org/maven2/" }
}
  configurations {
    xjcref
    xjcrun
}
  dependencies {
    xjcref 'com.sun.xml.bind:jaxb-xjc:2.2.7'
    xjcref 'com.sun.xml.bind:jaxb-core:2.2.7'
    xjcref 'javax.xml.bind:jaxb-api:2.2.7'
      xjcrun "com.github.jaxb-xew-plugin:jaxb-xew-plugin:1.4"
    xjcrun "net.java.dev.jaxb2-commons:jaxb-fluent-api:2.1.8"
      ... various compile dependencies ...
}
  sourceSets {
    gen {
        java {
            srcDir 'src/gen/java'
        }
    }
   }
  sourceSets.main.compileClasspath += sourceSets.gen.output.classesDir // compile error
  def processXSD(schemaName, pkgName) {
    ant.xjc(destdir: sourceSets.gen.java.srcDirs[0], package: pkgName, extension: true) {
        classpath { pathelement(path: configurations.xjcrun.asPath) }
        schema(dir: "src/main/resources/schema", includes: schemaName)
        arg(value: "-Xxew")
        arg(value: "-Xfluent-api")
    }
}
  task processXSDs() << {
    file(sourceSets.gen.java.srcDirs[0]).mkdirs()
          ant.taskdef(name: 'xjc', classname: 'com.sun.tools.xjc.XJCTask',
                classpath: configurations.xjcref.asPath)
      processXSD("serviceCallResults.xsd", "com.att.sunlight.service.domain.serviceCallResults")
    processXSD("dataProblems.xsd", "com.att.sunlight.service.domain.dataProblems")
    processXSD("serviceAutoRateCallResults.xsd", "com.att.sunlight.service.domain.autorate")
    processXSD("sunlightConfig.xsd", "com.att.sunlight.admin.domain.config")
    processXSD("uverseAutomationDailyStatusResults.xsd", "com.att.sunlight.service.domain.uverse.automation")
    processXSD("mobilityAutomationUnlockOrdersResults.xsd", "com.att.sunlight.service.domain.mobility.automation.unlockorders")
    processXSD("queryInfo.xsd", "com.att.sunlight.service.domain.queryInfo")
    processXSD("commonErrorMessages.xsd", "com.att.sunlight.service.domain.commonErrorMessages")
}
  compileGenJava.dependsOn processXSDs
  compileJava.dependsOn compileGenJava // Another apparently misguided attempt

Is the end result for the production code (src/main) to be combined with the JAXB generated code and both be included in the final JAR?

Thanks for the quick reply. Yes, the generated classes and the custom classes should be packaged together in the final jar.

Understood. Rather than defining a new source set (which in turn defines a compile task), I’d suggest simply adding the generated source to the existing ‘compileJava’ task. This has the benefit of putting all the class output in the same location (which is then used by the ‘jar’ task), as well as inheriting the production source classpath. I’d also suggest having your XJC task place your generated source somewhere in your build directory. Makes cleaning your project easier and doesn’t pollute your version controlled folders.

compileJava {

dependsOn processXSDs

source “${buildDir}/generated-src”

}

Ok.

I’d prefer to “softcode” that directory in a single place. I attempted to do this like this:

def genDir = "${buildDir}/gen"
  def processXSD(schemaName, pkgName) {
 ant.xjc(destdir: genDir, package: pkgName, extension: true) { // fails on this line
  classpath { pathelement(path: configurations.xjcrun.asPath) }
  schema(dir: "src/main/resources/schema", includes: schemaName)
  arg(value: "-Xxew")
  arg(value: "-Xfluent-api")
 }
}
  task processXSDs() << {
 file(genDir).mkdirs()
...
compileJava {
 dependsOn processXSDs
 source genDir
}

This fails on the “genDir” reference in the “processXSD” method, saying “Could not find property ‘genDir’ on root project …”.

The recommended convention for custom project properties is to use the extra properties extension.

ext {

genDir = “${buildDir}/gen”

}

Hmm. Seeing something I don’t understand yet. My generated classes declare nested static classes, often 2-3 classes deep, and my custom code imports some of those classes, using the fully qualified name, like “…DatasourceStatus.Datasources.Datasource.Results.Result”. When I inspect the classes compiled by Eclipse and my Maven build, these classes look fine. However, when I inspect the classes built by Gradle, and from manually constructed “javac” lines (based on the debug info from Gradle), I only see the following classes:

DatasourceStatus

DatasourceStatus$Datasource

DatasourceStatus$Datasource$Results

DatasourceStatus$Datasource$Results$Result

The classes generated by Maven and Eclipse are these:

DatasourceStatus

DatasourceStatus$Datasources

DatasourceStatus$Datasources$Datasource

DatasourceStatus$Datasources$Datasource$Results

DatasourceStatus$Datasources$Datasource$Results$Result

This is correct.

I see nothing special in the generated class with how these nested classes are declared. They are all “public static class”.

I’ve tried this with both 1.6 and 1.7 for the “sourceCompatibility” and “targetCompatibility” properties.

You might want to try using a diff tool to compare the Gradle generated sources versus the Maven ones to see if there are any strange subtle differences. I can’t think of anything off hand that would case the behavior you’re seeing.

Duh. Should have thought of that. I see differences that explain this. I guess my upgrades of JAXB libraries and extensions have resulted in changes in the resulting output. Time to dig that up again.