When including a submodule, is it possible to override its dependencies?

Hey,

I’m developing a project (A Minecraft fabric mod) that has multiple submodules, each with their own implementation of the mod and each using different version of the Minecraft api (basically a library, this should also apply to project that has multiple implementations for each version of another library, I think you can ignore the Minecraft-part here).

Currently I have multiple files that are identical between the implementations, because the underlying Minecraft api interfaces haven’t changed between the versions. I have tried to move this logic into its own submodule called “common”, and the idea was that the common code would use one version of the Minecraft api (1.19, in compile time I guess?) to get autocompletion and satisfy the compiler, but then when it’s required as a dependency in each mod implementation, it would use the implementation’s version instead. The code would still work since the files are identical in each version anyways.

So the question is: Is it possible to somehow copy the common code to each implementation when building the project, without modifying the implementation classpath, so that for example import net.minecraft.item.Item; would resolve into the corresponding Minecraft api used in the current implementation. I know this would be unsafe since the compiler would only check against 1.19 api when developing the common code, but to me this still seems better than copying the changes between each implementation by hand.

In case the explanation is as confusing as I assume, here is an example of how the project currently looks: https://github.com/aleksilassila/litematica-printer/tree/subprojects-example.

An example of an identical file between the implementations would be the Printer.java file:
implementation/v1_19/src/main/java/me/aleksilassila/litematica/printer/v1_19/printer/Printer.java

Currently I tried to just include the common code in the 1.19 implementation’s build.gradle like this:

implementation(project(":common")) {
	transitive = false // Not sure what is this for but it runs into runtime error instead of compile error
}

And then call the common code here:
me/aleksilassila/litematica/printer/v1_19/LitematicaMixinMod.java:60

System.out.println(CommonCode.ping("PING"));
				MinecraftClient c = MinecraftClient.getInstance();
		System.out.println("GAME VERSION FROM COMMON CODE: " + CommonCode.getVersion(c));

CommonCode looks like this:
me/aleksilassila/litematica/printer/common/CommonCode.java:5

    public static String getVersion(MinecraftClient client) {
        return client.getGameVersion();
    }

    public static String ping(String string) {
        return string;
    }

However, calling the getVersion(MinecraftClient) produces the following error:

Caused by: java.lang.NoSuchMethodError: 'java.lang.String me.aleksilassila.litematica.printer.common.CommonCode.getVersion(net.minecraft.client.MinecraftClient)'

Because the ping(String) can be called just fine, the runtime issues arise only when using the Minecraft api.

So: can i somehow configure the CommonCode to use the same Minecraft api version as the implementation it is being used in? Or just copy the CommonCode over to each implementation? Or is there a better way to do this?

Any help is greatly appreciated.

I didn’t look at your example code actually, but from what I understood from your text, that is just normal situation in almost any project. You should practically never use transitive = false except for really special edge cases. If you have common which depends on MC 1.19 and then the downstream project depends on common and MC 1.20, then by default anyway the higher version is used. If you want to downgrade, there are also options, but this is often not the best idea.

Thank you for the answer. I tried removing the transitive = false part, and also downgraded the common to use version 1.17, which is the lowest version I’m using, but I ran into some fabric-related issues. This seems to be the solution though, now its just up to me to figure out how I can make it work with the fabric gradle plugin that handles injecting the code into the game.