Unable to replace "jar" task in multi-module project using Gradle 3.5 or newer

(Garrett L. Ward) #1

In Gradle 3.3 and 3.4, I am able to use a plugin replace the ‘jar’ task with a custom subclass of “Jar” that does additional work (signing and staging to a library folder) after the Jar copy task executes. This works both with Project.tasks().remove() as well as Project.tasks.replace(JavaPlugin.JAR_TASK_NAME, Module).

Starting with Gradle 3.5 (and in 4.0.1 as well), this override seems to be somewhat broken. While building a single project jar works as expected (e.g, “:project:jar”), trying to build all jars (with gradlew jar) or a jar with project dependencies causes all dependent projects to be built with a default Jar task instead of my custom task.

I’ve confirmed that subprojects have the correct set of tasks after my plugin is applied:

:web-rt:jar -> com.tridium.gradle.plugins.module.task.Module_Decorated
:web-rt:javadocJar -> org.gradle.api.tasks.bundling.Jar_Decorated
:web-rt:testModule -> com.tridium.gradle.plugins.module.task.Module_Decorated
:net-rt:jar -> com.tridium.gradle.plugins.module.task.Module_Decorated
:net-rt:javadocJar -> org.gradle.api.tasks.bundling.Jar_Decorated
:net-rt:testModule -> com.tridium.gradle.plugins.module.task.Module_Decorated

However, using a TaskExecutionListener, I can see that the wrong Jar task is being executed for dependent projects, but not the requested project:

  public static class Listener implements TaskExecutionListener {
    public void beforeExecute(Task task)
      if (task instanceof Jar)
        System.out.printf(":%s:%s -> %s (%s)%n", task.getProject().getName(), task.getName(), task.getClass().getCanonicalName(), task.toString());
        for (Jar t : task.getProject().getTasks().withType(Jar.class)) 
          System.out.printf("  %s -> %s (%s)%n", t.getName(), t.getClass().getCanonicalName(), t.toString());
    public void afterExecute(Task task, TaskState state)  {  }

Output of “./gradlew :web-rt:jar” (truncated):

    :net-rt:jar -> org.gradle.api.tasks.bundling.Jar_Decorated (task ':net-rt:jar')
      jar -> com.tridium.gradle.plugins.module.task.Module_Decorated (task ':net-rt:jar')
      javadocJar -> org.gradle.api.tasks.bundling.Jar_Decorated (task ':net-rt:javadocJar')
      testModule -> com.tridium.gradle.plugins.module.task.Module_Decorated (task ':net-rt:testModule')
    :web-rt:jar -> com.tridium.gradle.plugins.module.task.Module_Decorated (task ':web-rt:jar')
      jar -> com.tridium.gradle.plugins.module.task.Module_Decorated (task ':web-rt:jar')
      javadocJar -> org.gradle.api.tasks.bundling.Jar_Decorated (task ':web-rt:javadocJar')
      testModule -> com.tridium.gradle.plugins.module.task.Module_Decorated (task ':web-rt:testModule')

I’ve no idea where the Jar task being called for “net-rt” is coming from, as it does not appear to be a task on the project.

Is replacing a built-in task still supported in Gradle 3.5+? Is there anything special (beyond calling project.tasks.replace(…)) to ensure the “old” task is really gone?

(Garrett L. Ward) #2

Update: I was able to to fix this by also replacing the archives for the “apiElements” and “runtimeElements” configurations, in addition to the “runtime” configuration, with an archive created from my custom task:

 // Remove jar artifact and add module artifact
 def runtimeConfiguration = project.configurations.getByName(JavaPlugin.RUNTIME_CONFIGURATION_NAME)
 def moduleArtifact = project.artifacts.add(JavaPlugin.RUNTIME_CONFIGURATION_NAME, moduleTask)
 rewireVariants(runtimeConfiguration, moduleArtifact)
 rewireVariants(project.getConfigurations().getByName(JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME), moduleArtifact)
 rewireVariants(project.getConfigurations().getByName(JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME), moduleArtifact)

  private void rewireVariants(Configuration configuration, PublishArtifact artifact) {
    ConfigurationPublications publications = configuration.getOutgoing();