Use zipTree as source set for building and static compiling

Better to tell gradle that the sources.jar is part of the main.java.srcDirs.

Your code doesn’t create that connection. If somebody deleted the generated-src directory without the clean task, gradle wouldn’t recreate it.

plugins {
    id 'idea'
}

configurations {
  sourcesJar
}
// Create Sync task which first deletes the content of the target directory
// and then extracts the jar file every time it is run.
def unzipFilesTask = tasks.register('unzipFiles', Sync) {
    from(zipTree(sourcesJar.singleFile))
    into(layout.projectDirectory.dir("generated-src")
    fileMode = 444
}
// Tell gradle that 1) it should add the output of the `unzipFiles` task as java files and
// 2) that the task is a dependency of the java.srcDir
sourceSets {
    main {
        java.srcDir(unzipFilesTask)
    }
}
// Inform idea, that this is generated code.
idea {
    module {
        generatedSourceDirs += file('${buildDir}/generated-src/')
    }
}
// finally tell gradle to run the `unzipFiles` task after a `clean` task:
tasks.clean.configure {
    finalizedBy(unzipFilesTask)
}

Advantages: when built with gradle the jar source files should always be correct
Disadvantages: before opening the project the idea task should be run. I don’t know if you can avoid that.

Much appreciated. Not sure why, but the sourcesJar isn’t being added as a property:

Could not create task ':project.module:unzipFiles'.
Could not get unknown property 'sourcesJar' for task ':project.module:unzipFiles' of type org.gradle.api.tasks.Sync.

Also, I think the following might not be the correct syntax (the IDE applies inconsistent indenting):

    fileMode = 444

Some examples appear to create tasks without using = for assigning the mode:

task stageZip(type: Sync) {
    from zipTree('data/data.zip')
    into 'staging'
    fileMode 0644
}

Ah, it was a missing ) after into and sourcesJar needs the configurations. prefix:

def unzipFilesTask = tasks.register('unzipFiles', Sync) {
    from(zipTree(configurations.sourcesJar.singleFile))
    into(layout.projectDirectory.dir("generated-src"))
    fileMode = 444
}

Better to tell gradle that the sources.jar is part of the main.java.srcDirs .

Good point. The following is satisfactory:

apply plugin: 'idea'

final GENERATED_JAR = "${projectDir}/sources.jar"
final GENERATED_DIR = "${buildDir}/generated/sources"

task extractSources(type: Sync) {
  from zipTree(GENERATED_JAR)
  into GENERATED_DIR
}

sourceSets.main.java.srcDir extractSources
clean.finalizedBy extractSources

idea.module.generatedSourceDirs += file(GENERATED_DIR)

This feels reasonably clean.

Even though you seem satisfied already and also picked a solution, I’d like to chime in to note two things.

  1. This:

    task genSources {
        copy {
            from zipTree("${projectDir}/sources.jar")
            into "${buildDir}/src"
         }
    
        sourceSets.main.java.srcDirs += "${buildDir}/src"
    }
    

    does not what you think it does.
    The task genSources is never executed, because it has no actions to do.
    The whole content you defined is done at configuration time.
    And as you don’t use task configuration avoidance, the task is always configured.
    If you left out the first and last line, it would behave exactly the same.

  2. I wouldn’t use any task for extracting the sources at all.
    In all the examples you have "${projectDir}/sources.jar", but you said the source jar is coming from a maven repository.
    So I think the proper way would be to create a configuration, add the coordinates of your sources jar to that configuration, and use an artifact transform to unpack the jar. Then you can simply use the result to configure the source set the idea plugin and it should also work when having a fresh worktree and just opening IntelliJ without manually triggering the extraction task first.

1 Like