Replace default artifact from Jar task - Need new solution for Gradle 5+

I asked this long ago and there is an archived answer from the Old Forum, but that solution doesn’t work with Gradle 5.

I want to replace the default jar with an obfuscated one. Ideally this would be conditional, so I could do an unobfuscated build vs and obfuscated build.

I am using this:

jar {
	classifier 'unobfuscated'
}

task obfuscate(dependsOn: jar) {
	// configure obfuscator here
	...
}

configurations.runtime.artifacts.removeAll { 
	def removeIt = (it.classifier == 'unobfuscated' && it.extension == 'jar')
	if(removeIt) {
		println "#### Removing runtime artifact ${it.file}"
	}
	removeIt
}

archives {
	runtime file: file("$buildDir/obfuscated/${project.name}.jar"), builtBy: obfuscate
}

but a downstream project in my multi-project build sees both files, the removed unobfuscated jar and the obfuscated jar.

1 Like

I’d be investigating removing the unobfiscated jar from the “archives” configuration (or preventing it from being added in the first place).

See the java plugin docs here which states

The jar task creates a JAR file containing the class files and resources of the project. The JAR file is declared as an artifact in the archives dependency configuration. This means that the JAR is available in the classpath of a dependent project. If you upload your project into a repository, this JAR is declared as part of the dependency descriptor. You can learn more about how to work with archives in Archive creation in depth and artifact configurations in Legacy Publishing.

Tried that as well (removing the file from both ‘runtime’ and ‘archives’ configurations. It doesn’t work. When I list the files that come from project A (where I do the stuff shown above, even including the archives configuration) in sibling project B. I still see the output of A:jar listed.

e.g. in B/build.gradle:

configurations {
    stuffFromA
}
dependencies {
    stuffFromA project(path:":A")
}

task showMeAFiles() {
    doLast {
        configurations.stuffFromA.findAll {
            println "stuffFromA config has ${it.file}"
        }
    }
}

The showMeAFiles task prints the main jar file as well as the obfuscated jar file.

Please show the bit where you remove the unobfiscated jar from project A’s “archives” configuration

I tried this:

configurations.runtime.artifacts.removeAll {
    def removeIt = (it.classifier == 'unobfuscated' && it.extension == 'jar')
    if (removeIt) {
        println "#### Removing runtime artifact ${it.file}"
    }
    removeIt
}
configurations.archives.artifacts.removeAll {
    def removeIt = (it.classifier == 'unobfuscated' && it.extension == 'jar')
    if (removeIt) {
        println "#### Removing archives artifact ${it.file}"
    }
    removeIt
}

I see the prints showing that it should be removed.

Perhaps something like

configurations.archives {
   exclude group: 'com.mygroup', module: 'mymodule' 
} 

Another option might be to either

  1. replace the jar task with obfuscated version
  2. disable the jar task (jar.enabled = false)

In both of these cases you would

  1. dependOn ‘compileJava’ instead of ‘jar’
  2. build your jar from sourceSets.main.output directly instead of transforming the unobfiscated jar
1 Like

I’m not sure how that works. Won’t that exclude the obfuscated jar as well?

I want to produce and publish the unobfuscated jar… I just don’t want it to be the default artifact.

Whoops, I meant

configurations.archives {
   exclude group: 'com.mygroup', module: 'mymodule', classifier: 'unobfiscated' 
} 

I put that in project A and it had no effect. Project B still sees the unobfuscated jar as part of project A’s default artifacts.

I think this will fix it!

Note that:

dependencies {
   compile project(':a')
} 

Is equivalent to

dependencies {
   compile project(path: ':a', configuration: 'default')
} 

You should investigate changing the “default” configuration to remove the unobfiscated jar.

See https://stackoverflow.com/questions/19936915/gradle-what-is-the-default-configuration-and-how-do-i-change-it

When I try to remove the unobfuscated jar using:
configurations.default.artifacts.removeAll { …some condition… }
there are NO artifacts present to remove. I tried this in a doLast for the obfuscate task which runs after the jar task has produced the unobfuscated jar. The default configuration seems to be “special”.

I am currently using a workaround similar to what you proposed (in your deleted comment) by putting the jar I want in a separate configuration ‘outputJar’ - this allows me to pick the unobfuscated or obfuscated jar as output of project A without doing anything in the downstream project B… but it is not ideal as anyone depending on A must remember to not depend on the default configuration.

I wish Gradle had a more straightforward way of handling a task that wants to replace an artifact produced by another task that runs earlier. There are many possible reasons to do this for the default artifact… e.g. if you want to sign it with a specific tool, or transform it in any way… like inject another file into an archive, etc.

Yeah they know how to make life hard for users. I needed the same some time ago. Ended up writing plugin in Kotlin. Used this extension function to remove the clear jar from all configurations. Somewhere around Gradle 4.8-10 they started using LazyPublishArtifact that complicated things even further!

fun Project.removeArtifacts(task: Task, excludeDefaultConfiguration: Boolean = true) {
configurations.stream()
    .filter { if (excludeDefaultConfiguration) it.name != Dependency.DEFAULT_CONFIGURATION else true }
    .forEach {
        it.artifacts.removeIf {
            // it is ArchivePublishArtifact && it.archiveTask == task
            // FIXME: LazyPublishArtifact doesn't provide information about archiveTask
            it.file == task.outputs.files.files.first()
        }
    }
}

After that I’m adding the the obfuscated artifact to configurations with:

artifacts.add(JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME, proguardPublishArtifact)
artifacts.add(JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME, proguardPublishArtifact)
artifacts.add(Dependency.ARCHIVES_CONFIGURATION, proguardPublishArtifact)

In any case good luck.

This worked for me :

configurations.archives.with {
artifacts.remove artifacts.find { it.toString().contains(“jar”) }
}

Just wanted to thank you for your code! In my case I had disabled the default jar task (jar.enabled = false) and added a bunch of new jar tasks producing archives. That worked fine on the command line, but when using Eclipse Buildship dependent projects still kept looking for the non-existent default JAR when the project was closed. Your code, slightly modified to only remove the default JAR, was the solution:

// Remove the default jar archive which is added by the 'java' plugin.
// This is necessary so that Eclipse pulls in the schema JARs properly to dependent projects
// when this project is closed.
configurations.each { cfg ->
	cfg.artifacts.with { archives ->
		def artifacts = []
		archives.each {
			if (it.file =~ jar.archiveName) {
				// We can't just call `archives.remove(it)` here because it triggers
				// a `ConcurrentModificationException`, so we add matching artifacts
				// to another list, then remove those elements outside of this iteration.
				artifacts.add(it)
			}
		}
		artifacts.each {
			archives.remove(it)
		}
	}
}

Note that it only worked for me if I used the java plugin – using the java-library plugin caused my artifact JARs to not be picked up by dependent projects when built using the CLI.

Many thanks for this thread.
I tried a whole day to solve this problem for me.
Finally I created this generic script file:

fixArtifactConfiguration.gradle

configurations.each { configuration ->
    configuration.artifacts.with { artifactSet ->
        def toBeRemovedList = []
        artifactSet.each {
            def artifactName = it.file.name
            def jarName = jar.archiveFileName.get()
            if (artifactName.equals(jarName)) {
                logger.info("artifact '${artifactName}' will be removed from configuration '${configuration.name}'")
                toBeRemovedList.add(it)
            } else {
                logger.info("artifact '${artifactName}' stays in configuration '${configuration.name}'")
            }
        }
        toBeRemovedList.each {
            artifactSet.remove(it)
        }
    }
}

tasks.each { task ->
    if (task.name.equals('war')) {
        // for War Plugin
        logger.info("add ${war.archiveFileName.get()} to runtime elements configuration")
        artifacts.add(JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME, war)
    } else if (task.name.equals('ear')) {
        // for Ear Plugin
        logger.info("add ${ear.archiveFileName.get()} to runtime elements configuration")
        artifacts.add(JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME, ear)
    } else if (task.name.equals('distZip')) {
        // for Distribution Plugin
        logger.info("add ${distZip.archiveFileName.get()} to runtime elements configuration")
        artifacts.add(JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME, distZip)
    }
}

if (logger.isInfoEnabled()) {
    logger.info("artifacts in configurations after fix:")
    configurations.each { configuration ->
        def artifacts = []
        configuration.artifacts.each { artifact ->
            artifacts += artifact.file.name
        }
        logger.info("${artifacts.size()} artifacts in ${configuration.name} [${artifacts.join(', ')}]")
    }
}

Which I simply include in zip-, war- or ear-creating projects:

apply from: "../fixArtifactConfiguration.gradle"

Yeah. :partying_face:

1 Like

In my case, I just wanted to replace the default jar with an obfuscated jar…

This worked for me:

def archivePath = jar.getArchivePath().absolutePath
ext.obfuscatedJar = archivePath.replace(".jar", "_obf.jar")

// replace the default jar archive with the obfuscated jar
configurations.archives.with {
    artifacts.clear()
    artifacts {
        archives file(obfuscatedJar)
    }
}

I have another task that does the obfuscation and creates the obfuscatedJar.

Hope this helps someone.