Case study: Gradle & IntelliJ IDEA integration in building Terasology modules: resource paths

Terasology is a project with history dating back to the time of Gradle 1.0. I’ve been working on modernizing its build configuration and streamlining its use with IntelliJ IDEA. These are some of my recent notes.

Time for another Terasology Build Tune-Up Tuesday, with more fun facts about Gradle and IntelliJ!

As we covered earlier, gradle puts jars on the runtime classpath, not classes directories.

Except for the current subproject under test. The difference is probably because main and test are source sets within the same project, not a dependency between projects.

This becomes especially relevant when the build has things that are only triggered when building the jar, as is the case here: Terasology/terasology-module.gradle.kts at 220c52dcb9d5a7b68f9d23bff2f80c85daf94649 · MovingBlocks/Terasology · GitHub

That’s one reason why your tests might not be finding module resources when invoked by gradle.

As for IntelliJ IDEA:

When IntelliJ IDEA imports a gradle project, it makes an IdeaModule (not to be confused with a Terasology module!) for the project, and sub-modules for the main and test source sets.

  • :open_file_folder: Foo - Foo/
    • :file_folder: Foo.main - Foo/src/main/
    • :file_folder: Foo.test - Foo/src/test/

The complication for us comes in when we consider resources. IntelliJ IDEA won’t run a gradle processResources task by default, but it will at least pick up a few things from the source set.

The assets of a Terasology module are stored in Foo/assets.

We can add Foo/assets as a resource directory to Foo.main. That seems to work, but a file assets/textures/foo.png ends up under the resource path textures/foo.png, not the intended assets/textures/foo.png. Its resource name is relative to the resource directory; the name of the resource directory itself is not included.

If we were using IntelliJ alone, we would solve this by setting the relative output path for assets as a resource root. But IntelliJ doesn’t use gradle’s output paths, so it doesn’t pick up on that from the SourceSet’s output properties.

In gradle, we can set Foo/ as the source with an include pattern to match assets (and deltas and overrides) without everything else. But that’s too sophisticated for the gradle-IntelliJ translation. When IntelliJ sees that Foo.main is trying to claim Foo/ as a content root, it cries foul, complaining that Foo/ is already taken as a content root by its parent IdeaModule.

Long story short: Avoid build configuration headaches by keeping your resources in a resources directory.