Skip to content

Creating a source jar reflecting the "uber jar" #41

Closed

Description

@raphw

Is it possible to use the Shadow plugin to create a source code and maybe even a javadoc jar that reflects the state after "merging"?

I need to import ASM in my project, i.e. the original packaging cannot be used for anything since this would violate compatibility. However, if someone added a dependency to my project, they could not navigate the dependency or read any documentation since the uber jar does not come with such information.

If this is not yet possible, is it consider to implement such functionality? I really need it and I want to move a first release to Maven Central soon, so I would be more than greatful. Great plugin, by the way, even without that feature!

Activity

  1. johnrengelman commented on Apr 3, 2014

    @johnrengelman
    Collaborator

    It's on the list of things to do. It's a native feature of Maven Shade, so I'd like to do it as well...Not sure when I'll have the time to do it though.

  2. raphw commented on Apr 3, 2014

    @raphw
    Author

    Thanks for your quick reply. Are you sure that this is possible using the Maven Shade plugin? I asked a question about this on the Gradle forum where the existence of such a tool was questioned. I also looked for documentations on this regarding Maven's shadowing plugin but I did not find any documentation on this. Could you provide a link or tell me where within the plugin this is implemented?

  3. johnrengelman commented on Apr 3, 2014

    @johnrengelman
    Collaborator
  4. raphw commented on Apr 3, 2014

    @raphw
    Author

    I just tried it and it works! Thanks so much for the link, I must have read to quickly through the descriptions because I did not see it when I came looking.

    I really hope you are integrating the source code shifting feature one day. I just had to migrate my build from Gradle to Maven for this feature... Too bad. Thanks for considering to do it some day! I'll keep my Gradle build as a secondary option until this happens.

  5. johnou commented on Sep 12, 2016

    @johnou

    @johnrengelman how's this coming?

  6. juddgaddie commented on Sep 30, 2016

    @juddgaddie

    +1 for this feature, migrating to Maven isn't really an option.

  7. added this to the 2.0.0 milestone on Nov 3, 2016
  8. felixbarny commented on May 11, 2018

    @felixbarny

    Any news on this? Are there other workarounds?
    @johnrengelman what would be the approach for implementing support for this?

  9. removed this from the 5.0.0 milestone on Jan 20, 2019
  10. Petikoch commented on Jan 31, 2019

    @Petikoch

    Up to the current shadow version 4.0.4, this kind of "all-sources.jar" feature is still missing, afaik.

    In one of my internal Gradle-MultiProject's I create and publish a single "...-all.jar" for my internal clients. To produce an accompanying "...-all-sources.jar" I collect all the sources from the dependent projects ("modules") with the following groovy snippet in my build.gradle file

    ...
    task sourcesJar(type: Jar, dependsOn: classes) {
    	group = 'build'
    	classifier = 'sources'
    	from { collectSourceSetsIncludingSubmodules(project) } // we return here a closure to do this lazy, after all projects are configured by gradle
    }
    
    // workaround for missing gradle shadow plugin feature, see https://github.com/johnrengelman/shadow/issues/41
    private Set<SourceDirectorySet> collectSourceSetsIncludingSubmodules(Project project){
    	Set<SourceDirectorySet> result = [].toSet()
    	recursiveCollectSourceSets(project, [].toSet(), result)
    	return result
    }
    
    private void recursiveCollectSourceSets(Project visitingProject,
    					Set<Project> visitedProjects,
    					Set<SourceDirectorySet> collectedSourceSets) {
    	if (!visitedProjects.contains(visitingProject)) {
    		visitedProjects.add(visitingProject)
    		collectedSourceSets.add(visitingProject.sourceSets.main.allSource)
    		visitingProject.configurations.implementation.getAllDependencies().withType(ProjectDependency).each { ProjectDependency pd ->
    			recursiveCollectSourceSets(pd.getDependencyProject(), visitedProjects, collectedSourceSets)
    		}
    	}
    }
    
    artifacts {
    	archives sourcesJar
    }
    
    shadowJar.dependsOn sourcesJar
    
    shadowJar {
       ...
    }
    ...
    

    It's not perfect (I'm not a gradle expert), but works for me. Maybe this helps someone else looking for a solution.

    (I'm using Gradle 4.10.x)

  11. Danilo-Araujo-Silva commented on Jun 12, 2020

    @Danilo-Araujo-Silva

    What is the gradle.kts version for this workaround?

  12. Danilo-Araujo-Silva commented on Jun 12, 2020

    @Danilo-Araujo-Silva

    I tried the following with gradle.kts but it didn't work for me =/, at least not yet:

    val sourcesJar by tasks.creating(Jar::class) {
    	dependsOn("classes")
    	group = "build"
    	archiveClassifier.value("sources")
    	from(collectSourceSetsIncludingSubmodules(project))
    }
    
    fun collectSourceSetsIncludingSubmodules(project: Project): MutableSet<SourceDirectorySet> {
    	val result: MutableSet<SourceDirectorySet> = mutableSetOf()
    	recursiveCollectSourceSets(project, mutableSetOf(), result)
    	return result
    }
    
    fun recursiveCollectSourceSets(
    	visitingProject: Project,
    	visitedProjects: MutableSet<Project>,
    	collectedSourceSets: MutableSet<SourceDirectorySet>
    ) {
    	if (!visitedProjects.contains(visitingProject)) {
    		visitedProjects.add(visitingProject)
    		collectedSourceSets.add(visitingProject.sourceSets.getByName("main").allSource)
    		visitingProject.configurations.implementation.get().allDependencies.withType<ProjectDependency>().forEach {
    				pd: ProjectDependency ->
    			recursiveCollectSourceSets(pd.getDependencyProject(), visitedProjects, collectedSourceSets)
    		}
    	}
    }
    
    artifacts {
    	archives(sourcesJar)
    }
    
  13. DasBabyPixel commented on Aug 2, 2023

    @DasBabyPixel

    A question because this was reopened:
    Does this mean you are planning to work (or already working) on this issue?
    If not, would you accept a PR?
    I'd love to see the shadow plugin being able to create the correct source jar.
    This would also make life a whole lot easier for publishing an artifact with shaded dependencies.

  14. KinComet commented on Aug 7, 2025

    @KinComet

    Now I have this

    val sourcesFatJar = tasks.register("sourcesFatJar", Jar::class) {
        dependsOn(tasks.classes)
        group = "build"
        archiveClassifier.value("sources-all")
    
        val result = dependencies.createArtifactResolutionQuery()
            .forComponents(
                configurations.runtimeClasspath.get().incoming.resolutionResult
                    .allDependencies
                    .map { it.from.id }
            )
            .withArtifacts(JvmLibrary::class.java, SourcesArtifact::class.java)
            .execute()
        for (component in result.resolvedComponents) {
            component.getArtifacts(SourcesArtifact::class.java).forEach {
                if (it is ResolvedArtifactResult) {
                    zipTree(it.file.absolutePath).visit {
                        if (path.startsWith("META-INF") || file.isDirectory)
                            return@visit
                        from(file) {
                            into(path.substringBeforeLast("/"))
                        }
                    }
                }
            }
        }
    }
    

    that creates sourcesJar.
    How to I apply relocate to this?

  15. Goooler commented on Aug 7, 2025

    @Goooler
    Member

    Requires replacing steps for source files.

  16. KinComet commented on Aug 7, 2025

    @KinComet

    Requires replacing steps for source files.

    Sorry, is there any doc or source code for this?

  17. Goooler commented on Aug 7, 2025

    @Goooler
    Member

    If you configured relocate("x.y.z", "prefix.x.y.z"), you need to replace all x.y.z with prefix.x.y.z in sources, which are plain texts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions