Gabriel
Gabriel

Reputation: 764

Gradle: How to create multiple jar?

I'm new to Gradle and Groovy, and I'd hope there would be something to solve my problem.

I have several packages, each of which needs to be compiled into one jar.
One solution I've found is to create multiple tasks that are of Jar type, but I'd like to avoid copy/paste and end up with a giant gradle file whenever I add a new package to my project.

My current implementation is to have multiple jar tasks, like this one :

task jarFoo(type: Jar) {
    baseName = "foo"
    version = "1.0.0"
    String className = baseName.capitalize()

    from(sourceSets.main.output) {
        include "$baseName/**"
    }

    from {
        configurations.compile.collect {
            it.isDirectory() ? it : zipTree(it)
        }
    }

    manifest {
        attributes "Implementation-Title": "$className",
                "Implementation-Version": "$version",
                "Main-Class": "$baseName.$className"
    }
}

It works like a charm, however I add packages very often and I will end up with a lot of packages, therefore a lot of tasks and a lot of copied/pasted code.

After fiddling with build.gradle file, I've found that I needed to extend from Jar in order to get a jar created.

So here's the code for the class so far :

class JarTask extends Jar {
    String jarName = "default"
    String jarVersion = "1.0.0"

    @TaskAction
    def jar() {
        baseName = jarName
        version = jarVersion
        String className = baseName.capitalize()

        // Thanks Opal for reminding that sourceSets
        // is defined in project.
        from(project.sourceSets.main.output) {
            include "$baseName/**"
        }

        from {
            configurations.compile.collect {
                it.isDirectory() ? it : zipTree(it)
            }
        }

        manifest {
            attributes "Implementation-Title": "$className",
                    "Implementation-Version": "$version",
                    "Main-Class": "$baseName.$className"
        }
    }
}

task jarFoo(type: JarTask) {
    jarName = "foo"
}

task jarBar(type: JarTask) {
    jarName = "bar"
    jarVersion = "1.2.42"
}

The problem is that the jar that is created ignores basically everything in the method: it contains only a MANIFEST.MF containing one line with the manifest version and is given the name of the project, not the name given in task. Nothing else.

If needed, you can find the code online in my GitHub repo (mainly in French).

Any idea would be truly appreciated!

Upvotes: 2

Views: 5963

Answers (2)

Gabriel
Gabriel

Reputation: 764

Here is another easier option that allows to pass parameters. I found the inspiration on this topic : https://discuss.gradle.org/t/passing-arguments-to-a-task/8427/20, which sounds exactly like what I was trying to do.

Here we basically define a method that returns a task, given some parameters. The rest is just testing if a version is given, or the code already given in question and adapted with @Opal great help.

It is sufficient to include the new builds in the artifacts block to make tasks available. Then, just run gradle jarqcm to build a single package or gradle assemble to compile everything.

apply plugin: "idea"
apply plugin: "java"

repositories {
    mavenCentral()
}

dependencies {
    compile "com.intellij:forms_rt:7.0.3"
    runtime "com.intellij:forms_rt:7.0.3"
}

def jarPackage(artifactName, artifactVersion) {
    if (artifactVersion == "" || artifactVersion == null) {
        artifactVersion = "1.0.0"
    }
    return tasks.create("jar${artifactName}", Jar) {
        baseName = artifactName
        version = artifactVersion
        String className = baseName.capitalize()

        from(sourceSets.main.output) {
            include "$baseName/**"
        }

        from {
            configurations.compile.collect {
                it.isDirectory() ? it : zipTree(it)
            }
        }

        manifest {
            attributes "Implementation-Title": "$className",
                    "Implementation-Version": "$version",
                    "Main-Class": "$baseName.$className"
        }
    }
}

artifacts {
    archives jarPackage("aleatoire", ""), jarPackage("calculatrice", "1.2.3"), jarPackage("copier", ""),
            jarPackage("qcm", "1.0.0")
}

Upvotes: 2

Opal
Opal

Reputation: 84776

After you edited the question is easy. There's no property sourceSets for the given task (Jar in this case). sourceSets are defined on Project and every task that extends DefaultTask inherits project field from it's parent. So you just need:

from(project.sourceSets.main.output) {
   include "$baseName/**"
}

ANSWER

I hope you understand the difference between task configuration and execution phase. This is the problem that occurs here. Basically you extended Jar task which as all tasks of type Copy is not designed to be extended - see here. In task action you configure the artifacts to be copied but.. there's too late for configuration - it's execution phase. To solve the problem task rules may be used. I've modified the script and it's:

apply plugin: "idea"
apply plugin: "java"

repositories {
    mavenCentral()
}

dependencies {
    compile "com.intellij:forms_rt:7.0.3"
    runtime "com.intellij:forms_rt:7.0.3"
}

tasks.addRule('Pattern: build<ID>') { String taskName ->
    if (taskName.startsWith('build')) {
        task(taskName, type: Jar) {
            baseName = taskName - 'build'
            version = '1.0.0'
            String className = baseName.capitalize()            

            from(sourceSets.main.output) {
                include "$baseName/**"
            }

            from {
                configurations.compile.collect {
                    it.isDirectory() ? it : zipTree(it)
                }
            }

            manifest {
                attributes "Implementation-Title": "$className",
                        "Implementation-Version": "$version",
                        "Main-Class": "$baseName.$className"
            }
        }
    }
}

artifacts {
   archives project.tasks['buildqcm'], project.tasks['buildlistage'] //etc
}

and should invoked simply with gradle buildlistage buildqcm. You can make additional validation to check if <ID> passed is on the list of packages e.g. Hope that helps and sorry for having to wait so long :/

Upvotes: 1

Related Questions