org.gradle.plugins.ide.idea.model.Module destroys content

The Module destroys existing content. In one of our projects I have added some “content” tags in the “NewModuleRootManager” component. now if I re-run ideaModule task, it taskes the first one unconditionally (“findNewModuleRootManager().content[0]”) and modifies it, instead of searching for the one with the correct url or creating it if it is not present. This destroys the consistency of the IML file. It does the same for “output” and “output-test”, but I guess there it is not a problem, as I think it wouldn’t make sense to have multiple of those present.

I have a problem to follow what modification you are doing and what happens then. Would it be possible to create a sample demonstrating your problem. Perhaps there is a better way how to do what you need.

Generally Gradle will generate .iml file and you can perform your own customization using withXml hooks as described in and in You can restore your modifications this way.

Sure, that is exactly what I am doing. Here the stripped-down example:

apply plugin: 'idea'
  idea.module {
    iml.withXml {
        def newModuleRootManager = it.asNode().component.find { it.@name == 'NewModuleRootManager' }
        def contentRoots = newModuleRootManager.content
        def contentRootURL = 'file://$MODULE_DIR$/foo'
        def contentRoot = contentRoots.find { it.@url == contentRootURL }
        if (!contentRoot) {
            // This line
            contentRoots.each { newModuleRootManager.remove it }
            contentRoot = newModuleRootManager.appendNode 'content', [url: contentRootURL]
            // together with this line just simulate that IDEA changes the order, so that the main
            // content root is not the first one in the list which is important for this bug.
            // This happens to me in real-life.
            contentRoots.each { newModuleRootManager.append it }
        def sourceFolder = contentRoot.sourceFolder.find { it.@url == "$contentRootURL/bar" }
        if (!sourceFolder) {
            contentRoot.appendNode 'sourceFolder', [url: "$contentRootURL/bar", isTestSource: false]

If you now invoke ideaModule, then copy the generated IML file and invoke ideaModule again, you can compare the first with the second version and will see what I mean.

What happens is that the org.gradle.plugins.ide.idea.model.Module class unconditionally takes the first content root it finds and modifies it which is simply wrong. It should search for the content root with the url that it wants to modify and if not present create a new one, similar to how I do it in my code and also in the show-case.

If you then load the module in idea, it will consolidate the two content roots with the same url attribute, but it will not allow you to do any modifications to your settings, as on Apply or OK it will complain that file://$MODULE_DIR$/foo/bar is used twice in the module.