Hi folks. I absolutely ran out of ideas how to fix...
# community-support
e
Hi folks. I absolutely ran out of ideas how to fix it (wasted 5 hrs debugging this already) so I hope you can help me. I have following task
Copy code
open class FatJarTask: Jar() {

    init {

        val configuration = project.configurations.getByName("runtimeClasspath")

        manifest.apply {
            attributes["Timestamp"] = System.currentTimeMillis()
        }

        project.plugins.withType(JavaPlugin::class.java) {
            val sourceSets = project.extensions.getByType(SourceSetContainer::class.java)
            from(sourceSets.getByName("main").output)
        }

        from(configuration.map { if (it.isDirectory) it else project.zipTree(it) }, {
            duplicatesStrategy = DuplicatesStrategy.EXCLUDE
        })

        isZip64 = true
    }
}
implemented in my custom gradle plugin (which is published to maven repo). When it's being used like this
Copy code
tasks {
    register<FatJarTask>("fatJar")
}
I get error
Copy code
FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':project:fatJar'.
> Entry META-INF/LICENSE is a duplicate but no duplicate handling strategy has been set. Please refer to <https://docs.gradle.org/8.10.1/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:duplicatesStrategy> for details.
If I move implementation of this task as is to build.gradle.kts of the project that uses it there will be no error. So, for some reason duplicatesStrategy is not set when task implementation is coming from external gradle plugin. What am I missing ?
m
Is this a case of resolving the wrong scope? Try this:
Copy code
from(configuration.map { if (it.isDirectory) it else project.zipTree(it) }, {
            it.duplicatesStrategy = DuplicatesStrategy.EXCLUDE
        })
(with additional
it.
)
PS: configuring your task from its constructor feels a bit dangerous, I'd also try to move that out of the constructor
e
@Martin Goddammit Martin, that was it ! Thank you so much. I love this community already 🙂 I send you linkedin request if that's ok. Where would you recommend to move initialization logic to ? I used to have it overridden copy() method but it didn't work properly.
m
To your task instantiation:
Copy code
tasks.register("fatJar") {
  // configure here
}
👍 1
v
Besides that, building such a bad-practice fat jar is a very problematic operation and can have many problems, especially if you simple exclude any duplicates. You should consider instead using the GradleUp
shadow
plugin which at least sails around some of the cliffs you will hit sooner or later. And you shouldn't use
project.plugins
(see its JavaDoc)
e
@Vampire Thank you for the note, Björn. I used Shadow initially but I couldn't configure it to skip compileOnly dependencies and certain dependencies. So after long fight with it I gave up and switched to vanilla Jar task.
And you shouldn't use
project.plugins
(see its JavaDoc)
Could you please elaborate what you mean by this and perhaps demonstrate it with snippet of code?
v
but I couldn't configure it to skip compileOnly dependencies
That's probably because you configured it to include them somewhere. 😄 By default they are not included as that makes not sense. By default it uses
runtimeClasspath
which usually exactly is what you need.
So after long fight with it I gave up and switched to vanilla Jar task.
Do whatever works for you. Just saying that with that you have more quirks than you have anyway by using such bad-practice fat jars.
Could you please elaborate what you mean by this and perhaps demonstrate it with snippet of code?
I'm not sure what you are asking for. You showed that you use
project.plugins
. And if you look at its JavaDoc it tells you you shouldn't use it and what to use instead. So did you read its JavaDoc as I suggested? If so, then I really don't understand the question, can you maybe reformulate it?