25

I'm developing a multi-module project with gradle/intellij-idea, and here is the structure of my project home:

project/
  sub-project-1
    /main/resources
  sub-project-2
    /main/resources
  data
    /main/resources
    /test/resources

As you can see I have multiple sub-projects (all java), how can I make them depend on some common resources (the "data" project, which contains no code but only resources), as well as their own separate resources?

Also it's best that the intellij-idea can pick up these dependencies with JetGradle automatically (JetGradle do just fine picking up the default gradle java project dependencies within each sub project).

Thanks a lot!

Eric Wu
  • 342
  • 1
  • 3
  • 11

8 Answers8

21

The approach I took was to use project reference

sourceSets {
    main {
        resources {
            srcDirs += [
                project(':data').sourceSets.main.resources
            ]
        }
    }
}

UPDATE: Tested with Gradle7 and it still works. Tested with java-library plugin.

Tono Wiedermann
  • 608
  • 6
  • 13
  • 1
    dude you are such a damn life saver. I was trying for hours to get a file to load across two modules without duplicating it but no matter where I put it, what I named it, or how I loaded it from the classpath whenever execution switched from one module to another gradle module I always got `FileNotFoundException`. This fixed the issue! – fIwJlxSzApHEZIl Nov 02 '17 at 22:27
  • 1
    glad it helped someone :) – Tono Wiedermann Nov 05 '17 at 14:39
  • 1
    I get a stack overflow when trying this. Bounces between at org.gradle.api.internal.file.DefaultSourceDirectorySet.getSrcDirTrees(DefaultSourceDirectorySet.java:128) at org.gradle.api.internal.file.DefaultSourceDirectorySet.doGetSrcDirTrees(DefaultSourceDirectorySet.java:141) – Toddius Zho Jan 09 '18 at 16:30
  • 1
    @ToddiusZho have you created a cyclical import loop? Where project 1 imports project 2 and project 2 imports project 1? Hierarchy is extremely important in gradle it's easy to create cyclical loops. – fIwJlxSzApHEZIl Jan 09 '18 at 22:06
  • No, something about the way subprojects are declared, each subproject has the right name and projectDir, yet all their sourceSets are exactly the same as the parent project! So I had to use the subprojects Closure to dynamically reconstruct their srcDirs back to src/main/resources and src/test/resources, relative to their projectDir. – Toddius Zho Jan 11 '18 at 04:37
  • 1
    I think this answer is now outdated. As per the latest gradle version, `sourceSets` doesn't exist on the `Project` type. – kapad Apr 15 '19 at 08:56
  • 1
    @kapad it's not outdated, still working on latest Gradle – Tono Wiedermann Jul 30 '21 at 07:27
  • 1
    Yes, this is still working! I would due expect Gradle can support such a behavior in the dependency section ‍♂️ – Roee Gavirel Feb 15 '22 at 06:27
14

One solution is to apply the Java plugin also to the data project, and then use regular project dependencies (e.g. dependencies { runtime project(":data") }). However, this would require a bit of effort to prevent shipping the test resources.

Another solution is not to make data a Gradle project but literally include its resource directories in the other two projects (sourceSets.main.resources.srcDir "../data/main/resources"; sourceSets.test.resources.srcDir "../data/test/resources").

elect
  • 6,765
  • 10
  • 53
  • 119
Peter Niederwieser
  • 121,412
  • 21
  • 324
  • 259
6

You need to choose the project which will hold your resources. All the other projects that require those resources, will add them to the resources component of the sourceSets.

sourceSets {
    data {
        resources {
            srcDir "${project(':data').projectDir}/src/main/resources"
            include "your_pattern_here**"
        }
    }
    main {
        resources {
            srcDirs += [ data.resources ]
        }
    }
}
nucatus
  • 2,196
  • 2
  • 21
  • 18
  • is the "data" under "sourceSets" the same or differnent ":data" ? GAAAAA. My kingdom for questions that don't use KEYWORDS. "sub-project-1", "sub-project-2", and "myshared" would have been ambiguity free. – granadaCoder Oct 26 '20 at 21:19
  • I see it now. For future readers, I think it can be anything. Here is use "peanutbutter" to show how it can be anything. sourceSets { peanutbutter { resources {. and srcDirs += [ peanutbutter.resources ] – granadaCoder Oct 27 '20 at 12:41
4

Ok, I figured it out. It's actually pretty simple. Just treat the "data" folder as another project and add dependency declarations to sub projects will do the trick. For example:

dependencies {
    compile project (':data')
    testCompile project (':data')
}
Eric Wu
  • 342
  • 1
  • 3
  • 11
  • 3
    Hi Eric, I want to do pretty much the same thing. did you actually create the :data project as a gradle project? if so ,could you share your build.gradle please? – Dave00Galloway Mar 22 '16 at 23:06
3

And here is version for Kotlin DSL - to sync all resources from :data module to root and all sub-modules build/resources folders:

// Synchronizing resources from :data module to project root: build/resources
synchronizeSharedResources()

subprojects {
    // Synchronizing resources from :data module to all submodules: build/resources
    synchronizeSharedResources()
}

fun Project.synchronizeSharedResources() {
    sourceSets {
        main {
            resources.srcDir(project(":data").sourceSets["main"].resources.srcDirs)
        }
        test {
            resources.srcDir(project(":data").sourceSets["test"].resources.srcDirs)
        }
    }
}
  • 1
    Can you elaborate where you will put synchronizeSharedResources function and subprojects block? – Harsh May 01 '20 at 07:33
0
sourceSets {
    main {
        resources {
            srcDirs += [
                '../sub-project-1/src/main/resources',
                '../sub-project-2/src/main/resources',
                '../data/src/main/resources'
            ]
        }
    }

    test {
        resources {
            srcDir '../data/src/test/resources'
        }
    }    
}
Nelson
  • 233
  • 1
  • 4
  • 6
0

I had another problem with resources in multi-module project, may be it will helpful for somebody.

I have two independent modules:

- database (contains migrations for whole project)
- main (contains business logic(controllers, services and etc))

Also I use Spring-Boot 2 in my project.

I have integration tests in main and before test is started I need to create schema and tables in DB. But all my migrations (use Flyway for migration control) in database module. Path for migration is src/main/resources/db/migrations.

As a result I need to have all migrations in /test/resources in order to start tests. Of course, I don't want to duplicate migrations in main module and I found this solution. I need to add these lines in files in main module.

builde.gradle

dependencies { 
   testImplementation "com.example.module:database:$dbMigrationVersion"
}

application-test.yml

spring:
  flyway:
     locations: classpath:/BOOT-INF/classes/db/migration

After these lines all your migrations from  database module will be in main module classpath (in our case for test profile). And flyway can execute them before starting tests.

Vladimir
  • 525
  • 5
  • 15
0

If all your projects are java projects and you apply java plugin to them you can just do the following. Assuming your directory structure you could do :

def dataMainResourcesPath = rootProject.projectDir.path + '/data/main/resources'
def dataTestResourcesPath = rootProject.projectDir.path + '/data/test/resources'

subprojects.each {
    it.sourceSets.main.resources.srcDirs += dataMainResourcesPath
    it.sourceSets.test.resources.srcDirs += dataTestResourcesPath
}

in your root build.gradle.

Michał Krzywański
  • 15,659
  • 4
  • 36
  • 63
  • 1
    This works (as well as the other variants of this approach), but will cause issues in IDEs - for example IntelliJ starts complaining about "duplicate content roots detected". See https://youtrack.jetbrains.com/issue/IDEA-210311/Multiple-modules-with-shared-content-root-error-after-upgrade-20183-20191 and https://youtrack.jetbrains.com/issue/IDEABKL-6745/Cannot-define-two-identical-content-roots-in-different-module-within-a-single-project – tftd Nov 02 '22 at 10:26