69

I have Java 6 and 7 installed on my machine. Gradle uses 1.7 (checked using gradle -v). But I need to compile my code to be compatible with Java 1.6. As far as I understand the documentation I can use the sourceCompatibility property to do so (and indirectly the targetCompatibility which defaults to the sourceCompatibility).

So I added the following line to my build file (on the root level, not in any closure):

sourceCompatibility = 1.6

(to be sure I also added the targetCompatibility = 1.6 in some trials, but that should not make a difference)

To check whether the result was actually compatible with 1.6 I unzipped the resulting jar, cd into the WEB-INF/classes folder and used javap -verbose on the first .class file I encountered. But no matter whether I set the target compatibility or whether I used 1.5 instead of 1.6 or whether I specified it as string ('1.6'), each time the result of javap was

minor version: 0
major version: 51

Afaik this means it is Java 1.7 Bytecode, which is wrong.

Any ideas why the sourceCompatibility-setting doesn't work? Or is javap not the correct way to check the compatibility?

UPDATE: Yes, this is actually a multi-project build but I only checked one of the subprojects' build results. In this subproject's build file I made the mentioned changes to be sure they are actually applied. In addition, I added the following in the root project's build file (as @Vidya proposed as well):

allprojects {
    sourceCompatibility = 1.6
    targetCompatibility = 1.6
}

But this didn't help either.

UPDATE 2: I checked the setting of sourceCompatibility with this snippet in the relevant build.gradle files:

compileJava.doFirst {
    println "source compatibility " + sourceCompatibility
}

It revealed that my sourceCompatibility is set to 1.7 although I tried to set it to 1.6. When I extracted the simplest subproject and built in on its own the sourceCompatibility is set correctly and the Java Byte code is compatible to 1.6. However, even this sub-project uses the wrong sourceCompatibility when used in the multi project build.

BTW: The plugins I use in some of the sub projects are: java, war, jetty, gwt

UPDATE 3: I changed the built scripts to just use the java plugin (and thus just construct some jars) and removed the usage of the war, jetty and gwt plugin. But still all the projects are set to sourceCompatibility 1.7 despite me setting it in the allprojects section and in some of the sub projects. All that is left now in the build scripts is the declaration of some decencies (maven, files and other sub projects), the declaration of the repositories to use, the declaration of some others tasks (that the build-task does not depend on, so it shouldn't be affected) and the configuration of the manifest file for the created jar files (I add a specification and an implementation version and title to the manifest file).

I don't see how any of that would affect the sourceCompatibility setting.

Marwin
  • 2,655
  • 1
  • 19
  • 17
Joachim Kurz
  • 2,875
  • 6
  • 23
  • 43
  • To satisfy your requirement, you need to set `targetCompatibility`. But I don't think you can have source compat > target compat, and you are right in that target compat defaults to source compat. Hence I'd expect this to just work. Is this a multi-project build? Are you sure you are setting `sourceCompatibility` for the right project? – Peter Niederwieser Jan 09 '14 at 20:18
  • An alternative is to run Gradle with Java 6. This will also catch cases where you inadvertently use some Java 7 API, and avoids an annoying javac warning that is issued whenever you use Java 7 compiler with source compat 6 and don't put the Java 6 standard library on the compiler's bootstrap class path. – Peter Niederwieser Jan 09 '14 at 20:21
  • 1
    I exactly repeated your steps (also running Gradle with JDK7), and it works just fine for me (`major version: 50`). At this point it's likely that it's a problem with your build. For example, some build script or third-party plugin might overwrite your configuration. But without a reproducible example, it's hard to help any further. One thing you can try is to check what `compileJava.doFirst { println sourceCompatibility }` prints. Also try with a clean build, although it shouldn't be necessary. – Peter Niederwieser Jan 09 '14 at 22:21
  • @PeterNiederwieser Is there a way to tell the gradle-wrapper to always use Java 6 to run gradle? Cause it's not enough if I just change the default SDK on my machine and it works, but it still fails on my colleagues' machines. – Joachim Kurz Jan 10 '14 at 10:47
  • @PeterNiederwieser thanks for the tip about `compileJava.doFirst { println sourceCompatibility }`. I added that and all of the sub-projects have sourceCompatibility set to 1.7. I'll check whether it's due to any of the plugins I use. – Joachim Kurz Jan 10 '14 at 10:49
  • 1
    It's printing `compileJava.sourceCompatibility`, which defaults to `project.sourceCompatibility` (which is what you've been setting so far). You can try to set the task-level properties (these are the ones that ultimately matter) directly with `tasks.withType(JavaCompile) { sourceCompatibility = "1.6"; targetCompatibility = "1.6" }`, although usually that wouldn't be necessary. – Peter Niederwieser Jan 10 '14 at 11:06
  • If I use the `tasks.withType ` snippet in the root build.gradle-file will this also affect the tasks defined by the sub projects' build files or do I have to do it in each of the sub-build-files? – Joachim Kurz Jan 10 '14 at 11:20
  • @PeterNiederwieser I removed the usage of all plugins but the java plugins and now there is just basic stuff (repository and dependency declarations and some basic other tasks left, details see update above) left. Still the sourceCompatibility is set to 1.7. Any other ideas what could lead to it being set wrongly? Next, I'll try setting the sourceCompatibility on the JavaCompile tasks directly. – Joachim Kurz Jan 10 '14 at 11:44
  • 1
    @PeterNiederwieser I added this `tasks.withType(JavaCompile) { sourceCompatibility = "1.6"; targetCompatibility = "1.6" }` to `allProjects{}` and it now works. Even with all the plugins in the original build script. If you add this as an answer I'll accept it. I'm still curious why it didn't work via the project attributes, though. – Joachim Kurz Jan 10 '14 at 11:57
  • You still need to set the bootstrap classpath so it compiles against the Java 6 class library (see e.g. [this answer](http://stackoverflow.com/a/16679733/592139)) otherwise you risk runtime errors if you accidentally call a method that was introduced by Java 7. Or keep it simple and just build on 6 if you need to be compatible with 6. – Ian Roberts May 04 '14 at 18:01
  • We ran into the same situation today. It works for the root project but not for a subproject. When I print everything out: both `project.sourceCompatibility` and `rootProject.sourceCompatibility` appears to be set correctly but the task's `sourceCompatibility` is different. I am still curious what causes the setting to be overwritten. – Marwin Jul 21 '14 at 09:34
  • Ok. Now I know what causes it. It's the `allprojects` (or `subprojects`) clause in the root project. It seems to misbehave: If I say `allprojects { sourceCompatibility = 1.6 }` in the root, the settings does not work correctly in subprojects, *not even* if I redefine it again in the subproject directly. If I remove the `allprojects` clause and *only* set the `sourceCompatibility` in the subproject, it works as expected. It seems like a some kind of bug. If I am able to describe it more specificaly, I am going to report to the developers. Joachim, I can you please verify this in your setup? – Marwin Jul 21 '14 at 09:49
  • Sounds similar to our setup. I'll try to have a look at it this week. Feel free to remind me if I don't. – Joachim Kurz Jul 22 '14 at 00:09

4 Answers4

111

It seems this behavior is caused by specifying the sourceCompatibility before apply plugin: 'java', which happens if you try to set the compatibility option inside allprojects.

In my setup, the situation can be solved by replacing:

allprojects {
    sourceCompatibility = 1.6
    targetCompatibility = 1.6
}

with:

allprojects {
    apply plugin: 'java'
    sourceCompatibility = 1.6
    targetCompatibility = 1.6
}

Will be glad if anyone else can verify this in a different setup.

I am still not sure whether this should be reported as a bug but I believe this solution is better than the work-around mentioned above (which has been very helpful however).

Marwin
  • 2,655
  • 1
  • 19
  • 17
  • 1
    Good catch. The first snippet is just wrong, as these properties are only introduced by the `java` plugin. In 1.x it will give deprecation warnings about assigning values to non-existing properties, in 2.0 it will fail outright. – Peter Niederwieser Jul 21 '14 at 10:35
  • We were just discussing this "warnings" topic with a coleague of mine. For some reason, the code did not issue a warning (Gradle 1.8 and 1.12). I am still not quite sure why. – Marwin Jul 21 '14 at 10:53
  • If `sourceCompatibility` is set before applying the `java` plugin, it will definitely issue the following deprecation warning: `Deprecated dynamic property: "sourceCompatibility" on "root project 'foo'", value: "1.6".` – Peter Niederwieser Jul 21 '14 at 10:57
  • Thanks a lot, that solved the problem for me as well! I think I didn't get a warning back then, either, but I did get one now (gradle 1.10), when I tried it again without the "apply plugin 'java'". – Joachim Kurz Aug 04 '14 at 15:35
  • It may be obvious, but this also works when used in a `subprojects` block (after the "java" or "groovy" plugin application). – cjstehno Apr 06 '15 at 20:29
53

Symptoms indicate that somewhere somebody is overwriting project.sourceCompatibility. But given that there are many ways to customize Gradle, I can't say from a distance who that is.

As a workaround, you can set the properties on the task level, which is what ultimately counts:

tasks.withType(JavaCompile) { 
    sourceCompatibility = "1.6"
    targetCompatibility = "1.6" 
}

Add this to allProjects { ... } block.

Display Name
  • 8,022
  • 3
  • 31
  • 66
Peter Niederwieser
  • 121,412
  • 21
  • 324
  • 259
  • We ran into the same situation today. This is a nice work-around by I am still curious what causes the sourceCompatibility setting to be overwritten. – Marwin Jul 21 '14 at 09:31
  • With or without the parenthesis? "" – El Mac Feb 12 '16 at 13:09
  • I actually don't think you want this in the allProjects block. Doesn't that make it evaluate the block for every project? If you put it at the bottom of your root gradle project, I think you'll only execute it once and it'll hit every configured javaCompile task. – Jazzepi Jun 28 '19 at 01:19
1

You need to define compileJava tasks in build.gradle file, if you are using sourceCompatibility or targetCompatibility. Without compileJava tasks, both compatibility variables are displayed as unused variables in Intellij. I am using Gradle version 2.10.

0

To solve this problem, just check the build Gradle (app), and put the below code inside it.

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

Also in the build Gradle (project), you have to have the below code.

allprojects {
    repositories {
        google()
        mavenCentral()
        jcenter()
        sourceCompatibility = 1.6
        targetCompatibility = 1.6
    }
}
hassan bazai
  • 424
  • 6
  • 9