Yet more build hell, why is the IDEA plugin naming my .iml file differently for just one subproject?

plugins

(Trejkaz (pen name)) #1

I’m trying to track down a problem where resources are mysteriously missing from the classpath when running the tests, but this is only occurring for one module. At the moment I haven’t got the foggiest idea how to figure out why this is happening, so I thought I would attack Gradle for one other stupid thing it is doing - the naming of the .iml files. This inconsistency could indirectly be the cause of our problems, so maybe if it’s fixed, the other problem will evaporate?

So here’s what I mean. Take this example top-level project (the project name is also ‘example’, so that is the directory it is in):

group 'org.example'
version '1.0-SNAPSHOT'

apply plugin: 'java'
apply plugin: 'idea'

sourceCompatibility = 1.5

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.11'
}

subprojects {
  apply plugin: 'java'
  apply plugin: 'idea'
}

Subproject in ‘common’:

group 'org.example'
version '1.0-SNAPSHOT'

apply plugin: 'java'

sourceCompatibility = 1.5

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.11'
}

Subproject in ‘example’:

group 'org.example'
version '1.0-SNAPSHOT'

apply plugin: 'java'

sourceCompatibility = 1.5

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.11'
}

When I run gradle idea, it creates three .iml files:

$ find . -name '*.iml'
./common/common.iml
./example/example-example.iml
./example.iml

Why is the one in example not just called example.iml?

In our real project, the file in example/ is called example.iml. When we run gradle idea, we now have both example/example.iml and example/example-example.iml. Perhaps this is the cause for our classpath issues when running the tests, but I really have no idea.

This was occurring under 2.7 and is still occurring under 2.8. I initially thought the plugin in IDEA was doing this, but it turns out the command-line tool does it as well, ignoring IDEA itself entirely.


(Trejkaz (pen name)) #2

Irrespective of why it’s doing it, the workaround is to tell Gradle what name to use:

idea {
  module {
    name = 'example'
  }
}

This then generates the right .iml files.

I also made the same change for our real project, and it fixed not only the name of the .iml file, but also the bizarre classpath problems we had when running tests. So suspicions confirmed, I guess.


(René Groeschke) #3

Hey Trejkaz,

when generating the Idea metadata gradle applies some deduplication logic to avoid having multiple projects with exactly the same name in your IDE. The workaround you mentioned here is the way to go I think.
Would you mind elaborating about the classpath issues you encountered that you think are related to the different idea module name?

cheers,
René


(Trejkaz (pen name)) #4

Basically we found that while running the tests, ServiceLoader wasn’t finding the META-INF/services files from that one particular module. It was finding the classes from that same module - and we store the resources and Java sources in the same directory, so it wasn’t really clear why it was able to find one and not the other. It was as if, when compiling, it didn’t copy the resources to the classes dir for some reason.

When I looked in the .iml file, I saw that Gradle had inserted additional paths to a nonexistent …/src/ directory, but extra paths wouldn’t explain the problem.

Interestingly, with the workaround, we don’t appear to be having any problems with the IDE having two of the same module filename. It seems to treat them as different modules with no problem.


(René Groeschke) #5

thanks for providing further details. A common mistake when reconfiguring
sourceSets folders is to do

sourceSets {
    main {
        java {
            srcDir 'src'
        }
        resources {
            srcDir 'src'
        }
    }
}

that just adds the folders instead of replacing them via

sourceSets {
main {
java {
srcDirs = [‘src’]
}
resources {
srcDirs = [‘src’]
}
}
}

what you describe sounds a bit like you might run into this?

cheers,
rene


(Trejkaz (pen name)) #6

What we appear to have is:

  sourceSets.main.resources {
    srcDirs = ['src/java', 'src/resources']
    includes = [
      'META-INF/services/*', '**/*.properties', '**/*.vm', '**/*.txt', '**/*.wav', '**/*.dat',
      '**/*.mdb', '**/*.ntf', '**/*.ttf', '**/*.woff', '**/*.woff2', '**/*.tld',
      '**/*.gif', '**/*.png', '**/*.jpeg', '**/*.jpg', '**/*.svg', '**/*.ico',
      '**/*.xml', '**/*.xsd', '**/*.xsl', '**/*.dtd',
      '**/*.json', '**/*.html', '**/*.js', '**/*.css',
    ]
  }

Since apparently some modules did have a separate resources dir…


(Trejkaz (pen name)) #7

Bumping for a newer workaround. In settings.gradle we now use:

rootProject.name = 'root'

This way you’re safe until someone creates a submodule called “root”.

Really, I consider this to be a Gradle bug - it should not name the root project after the name of the directory, because that makes the build change in stability depending on what directory the project is checked out to. But in this case, if someone happens to check the project out to a directory which is also the name of one of the subprojects (which is the case for the default checkout of our project!), the build fails because the subproject’s name gets unnecessarily prefixed by itself.