Compilation fails if the archive name is changed


(Sergii Vozniuk) #1

Hello,

I have noticed “strange” behavior during project compilation under the following conditions. (1) I have a multi project setup with two subprojects

top-level
    |---subproject1
    |
   |---src
    |
   |---subproject1.gradle
    |---subproject2
    |
   |---src
    |
   |---subproject2.gradle
    |---build.gradle
    |---settings.gradle

(2) The main source set of subproject1 contains production code and some testing utils that are needed for testing of subproject2 (used only in the test source set). So what I’m doing is excluding some part of the sources from production archive. Additionally I declare a configuration and a jar that contains both production and testing code. I use this configuration as a testCompile dependency of subproject2.

(3) The build files are the following:

subproject1.gradle

apply plugin: 'java'
  ext.productionArchiveName = 'demo-production.jar'
ext.testsArchiveName = 'demo-testing.jar'
  configurations {
    tests
}
  dependencies {
    testCompile 'junit:junit:4.11'
}
  jar {
//
  archiveName = productionArchiveName
    exclude(['com/vozniuk/testing/**'])
}
  task(testingJar, type: Jar) {
//
  archiveName = testsArchiveName
    from sourceSets.main.output
//
  exclude(['com/vozniuk/demo/**'])
}
  artifacts {
    tests testingJar
}

subproject2.gradle

apply plugin: 'java'
  dependencies {
    compile project(':subproject1')
    testCompile 'junit:junit:4.11'
    testCompile project(path: ':subproject1', configuration: 'tests')
}

(4) With the provided code the project succeeds to compile. The “strange” behavior I’m talking about manifests itself if you uncomment the archiveName = … in the jar task or in the testingJar task or in both of them then the compilation fails during compileTestJava with missing test utilities classes. Moreover if you uncomment exclude …in testingJar task the compilation fails during compileTestJava but the missing production classes from subproject1. My expectation was that testCompile extends the compile configuration and everything visible in compile should be visible in testCompile. It seems there is some colision between the published artifacts. The only situation when such a setup works is when the published archives names are identical, which I’m tryin to avoid.

To summarize: declaring a several configurations with a published artifacts and then trying to use them for different denedency configurations brings compilation errors due to classes not found. My expectation was that naming of the artifacts should not influence how the classes are resolved in the dependent projects.


(Peter Niederwieser) #2

If you don’t give the test Jar a different name (e.g. by setting ‘appendix = “test”’), it will overwrite the main Jar on disk. Another thing that’s suspicious is that you configure the test Jar to contain ‘sourceSets.main.output’ rather than ‘sourceSets.test.output’.


(Sergii Vozniuk) #3

Thanks for the quick response Peter

If you don’t give the test Jar a different name (e.g. by setting appendix = “test”).

Exactly this is what bothers me: if I do give the test Jar a different name the project does not compile anymore. With a different name for some reasons the classes contained in the second test jar are not resolved anymore.

test Jar to contain sourceSets.main.output rather than sourceSets.test.output.

And again exactly because I need some of the test helpers to be available in production code for our internal tools I cannot put them into the test sourceSet (limitation of the IDE that the test classes of one subproject are not visible for production code in another subproject)

If you wish I can provide the minimal project where the problem can be easily reproduced.


(Peter Niederwieser) #4

if I do give the test Jar a different name the project does not compile anymore

This just means that fixing one problem (the name collision) exposed another, likely unrelated, problem.

And again exactly because I need some of the test helpers to be available in production code

The way the two Jar tasks are configured right now, the test Jar is a superset of the main Jar. That doesn’t seem right to me.


(Sergii Vozniuk) #5

Thaks for your reply Peter,

I wouldn’t claim that I like current layout of the project either. But I have to handle the limitations of the tools I use and limitation in current project structure which requires weeks of refactoring to fix it. Before posting here I tried of course lots of different layouts and the one described in the first post is the only one that worked.

At first to avoid the situation when “test Jar is a superset of the main Jar” I tried to split the code in two separate jars with no overlap. Prod jar has prod classes e.g. everything from com.vozniuk.demo.** and test jar has only test helper classes from com.vozniuk.testing.**

task(testingJar, type: Jar) {
//
  archiveName = testsArchiveName
    from sourceSets.main.output
//
  exclude(['com/vozniuk/demo/**'])
}

Uncommenting the exclusion line makes the build produce 2 disjoint jars. Unfortunately this setup doesn’t work.

Can I then put the question this way:

Without changing the project structure how can one produce two different jars from one subproject such that: 1. The jars have different names 2. One of the jars is used in compile configuration and another is used in testCompile configuration in a different subproject 3. It doesn’t matter if the jars overlap or they are disjoint (although I would prefer disjoint) 4. The project actually compiles and all classes are resolved both in compile and testCompile

Thanks for help!


(Peter Niederwieser) #6

1-3 should be clear by now. (The way you publish and reference the test Jar looks fine.) There is no generic answer to 4. The problem is likely specific to your build, and to help further I’d need a reproducible example.


(Sergii Vozniuk) #7

Sure. Here is a reproducible example: Download Thanks!


(Peter Niederwieser) #8

Thanks for the example. Your code doesn’t configure the test Jar’s name, which is a must (otherwise the test Jar will overwrite the main Jar.) However, there is also a Gradle bug involved here. If the test Jar’s name is reconfigured by setting ‘testJar.archiveName’, the project dependency (‘testCompile project(path: ‘:subproject1’, configuration: ‘tests’)’) doesn’t work and the test Jar doesn’t end up on subproject2’s ‘testCompile’ class path. If the test Jar’s name is reconfigured by setting one of the name components (e.g. ‘baseName’ or ‘classifier’), things work as expected. I recommend to do the following:

task testJar(type: Jar) {
    classifier = 'test'
    from sourceSets.main.output
    exclude('com/vozniuk/demo/**')
}

This works fine for me. Raised GRADLE-3044 for the Gradle bug.


(Sergii Vozniuk) #9

Thanks for help. Your solution workerd. Indeed the bug doesn’t manifest itself if other archive name components are changed. Only if the the sole changed property is archiveName then it shows up (i.e changing archiveName AND classifier works fine).