How to configure [implementation] configuration not to list jar from a dependent project which has disabled jar task

I have a big gradle multi-project and few of the projects do not produce a jar file deliberately - tasks.withType(Jar) { enabled = false }

We have been using the java-library plugin for these projects in order for their consumer’s to consume the java classes (instead of jar) as explained here: The Java Library Plugin

This works fine and all projects compile successfully.

The implementation configuration however of the consumer’ projects always shows a reference to the jar file, which does not exist.
This pollutes the classpath entries and confuses.

Is there a proper way to solve this problem - either somehow remove the jar file reference or use the plugins in some other (better) way?

Producer project that does not produce a jar file:

apply plugin: 'java-library'
sourceSets {
    main {
        java {
            srcDirs = ['src']
        }
    }
}
tasks.withType(Jar) { enabled = false }
println("$project.name configurations.compileClasspath.asPath entries: " + configurations.compileClasspath.asPath)

Consumer project that consumes the classes of the producer project:

apply plugin: 'java'
sourceSets {
    main {
        java {
            srcDirs = ['src']
        }
    }
}
configurations {
    implementation {
        canBeResolved(true)
    }
}
dependencies {
    implementation project(":vehicle")
}
println("===========================================")
//why I get vehicle-0.0.0-v0001.jar in implementation entries since jar is disabled
//pollutes the classpath entries and confuses. 
//Can I remove it somehow?
println("implementation entries: " + configurations.implementation.asPath) 
println("sourceSets compileClasspath entries: " + sourceSets.main.compileClasspath.asPath)
println("===========================================")

Attaching a repro in:
sample.zip (77.8 KB)

Some points:

  • your “repro” is a bit unlucky, it uses a custom distribution that requires some unclear ARM variables to be set
  • your repro even though being quite short is over and over full of discouraged bad practices, if this resembles your real projects, you should really overhaul them a bit. :slight_smile:
  • why do you disable the jar in the producer project, obviously you need it. Not talking about the implementation thing you printed, but for example the runtimeClasspath will need the jar. Just that the compileClasspath can work with just the compiled classes in a directory does not mean all other consumers are happy with that, for example you miss resources otherwise.
  • that you even can call the asPath on implementation is one of the highly discouraged bad practices. You should never change the role of a configuration. implementation is a dependencyScope configuration which has the role of declaring dependencies on them, but not to resolve dependencies or consume them from other projects.
  • but besides that, if you for example would use runtimeClasspath you would get the same jar in the path printed, as that is what you request. The consuming project cannot know whether the jar task is disabled or not and it also must not have to know. The producing project declares that it publishes a jar, the consuming project requests a jar, so you get the jar. That the producing project says it publishes a jar but then does not produce it is a problem of the producer, not the consumer. The consuming project could everywhere set the according LibraryElements attribute on resolvable configurations to prefer the classes in a directory for example. But I still question that you at all try to disable the jar task. If you really want to do that, you should probably change the java component to suppress the publication of the main feature variant and instead model the outgoing configurations yourself without the jar or something like that, but I never tried that as it usually really makes very little sense.

Thanks for the quick reaction.

  • apologies for the unlucky repro, will correct it.
  • yes, it is true that in the real project we do have few (if not many) antipatterns, trying to eliminate some of these.
  • jar is disabled as I cannot change the build output (in terms of jars), nor I can change the project layout (policies, legacy product)
  • calling asPath was added just to better showcase what we have - I am not actually using it.
  • you say “The producing project declares that it publishes a jar, the consuming project requests a jar, so you get the jar” - apologies if I am not getting it right, but how can I declare that I am not publishing a jar and still be a java project with sources?
  • If you really want to do that, you should probably change the java component to suppress the publication of the main feature variant and instead model the outgoing configurations yourself without the jar or something like that, but I never tried that as it usually really makes very little sense.” → any chance to point me to some example/snippet?

@Vampire,

Do you mean something like this:

configurations {
    [apiElements, runtimeELements].each {
        it.outgoing.artifacts.removeIf { it.buildDependencies.getDependencies(null).contains(jar) }
    }
}

The ‘design’ problem on my end is that these non-jar projects are packaged in another project jar (out of all 8 projects with separate source sets, only а few publish jar files) and I cannot change this.

  • jar is disabled as I cannot change the build output (in terms of jars), nor I can change the project layout (policies, legacy product)

I did not understand either reasoning how that would mandate for disabling the jar task.

  • calling asPath was added just to better showcase what we have - I am not actually using it.

asPath per-se is fine.
But not on implementation any call that does resolution on implementation is bad which you can see by the need to set canBeResolved to true manually, changing the role of implementation. (Unless of course you also just did that for the show-casing, but you could as well just have used the runtimeClasspath configuration which is meant for resolution and also consumes the jar :slight_smile: )

how can I declare that I am not publishing a jar and still be a java project with sources

As I said, I don’t see where that ever would make any sense at all, so never tried and thus cannot tell you more concrete how to do it.
Actually, changing the java component will probably not work for intra-build-dependencies, but only for publishing.
So you probably need to just remove the artifacts with something like

[
      configurations.apiElements,
      configurations.runtimeElements
].each {
    it.artifacts.removeAll { true }
}

But as I said, that will not just make it “magically work”, it will then fail when you try to resolve implementation or runtimeClasspath (and others) because you still request to get a jar, but now the producer will not state it provides one anymore and thus fail resolution unless you configure it them to request the class files instead like the compileClasspath is doing. But again, this will most probabyl latest fail when you add some resources to the producer, as you only get the class files, not the resources in the consumer.

The ‘design’ problem on my end is that these non-jar projects are packaged in another project jar (out of all 8 projects with separate source sets, only а few publish jar files) and I cannot change this.

Well, that is actually quite bad and ugly.
If you separate the code into multiple projects or source sets, you should also publish them as separate jars.
But even then, you should not disable the jar task, but then use the contents of the jar file and repack them in the consumer, as ugly as it is (the repacking at all).