Gradle sourceCompatibility has no effect to subprojects
Asked Answered
L

4

70

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.

Leath answered 9/1, 2014 at 18:38 Comment(13)
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?Limousine
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.Limousine
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.Limousine
@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.Leath
@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.Leath
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.Limousine
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?Leath
@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.Leath
@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.Leath
You still need to set the bootstrap classpath so it compiles against the Java 6 class library (see e.g. this answer) 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.Bocage
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.Grumous
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?Grumous
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.Leath
G
114

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).

Grumous answered 21/7, 2014 at 10:0 Comment(5)
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.Limousine
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.Grumous
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".Limousine
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'".Leath
It may be obvious, but this also works when used in a subprojects block (after the "java" or "groovy" plugin application).Coliseum
L
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.

Limousine answered 10/1, 2014 at 18:17 Comment(3)
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.Grumous
With or without the parenthesis? ""Linalinacre
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.Ciracirca
G
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.

Gregory answered 2/5, 2016 at 18:48 Comment(0)
M
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
    }
}
Mayotte answered 16/10, 2021 at 17:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.