2

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!

Gabriel
  • 734
  • 11
  • 26

2 Answers2

2

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")
}
Gabriel
  • 734
  • 11
  • 26
1

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 :/

Community
  • 1
  • 1
Opal
  • 81,889
  • 28
  • 189
  • 210
  • Thanks for your answer ! It solves the `Could not find property 'sourceSets'` error. But the jar is still nearly empty (just a MANIFEST file) and with the project name, not the name given in task. Any idea ? – Gabriel May 03 '15 at 10:05
  • Still not yet. I would have posted an answer ;) But if you have something to suggest, I'd be happy to test it ! – Gabriel May 06 '15 at 13:40
  • @Gabriel, what's the current status? Sorry but the original questions has so many edits that I can't find out what's going on right now? – Opal May 06 '15 at 16:20
  • I edited the question. Thanks for telling me that it was going wild ! I sincerely hope it's more clear now, tell me if anything is wrong. – Gabriel May 06 '15 at 21:24
  • Ok, now it looks much better. Do you have this project hosted online maybe? – Opal May 07 '15 at 19:45
  • Thanks ! I edited the question to include a link to the online project. – Gabriel May 07 '15 at 21:39
  • Thanks for your answer ! It works like a charm :D However, I don't quite understand the usefulness of the last lines. It doesn't do anything when running `gradle assemble`. Also, is it possible to "customize" the task (change version for one jar only for exemple) ? – Gabriel May 09 '15 at 09:07
  • You mean `artifacts`? I see that it won't work. It requires additional handling unfortunately. To change version for a particular task you can pass it via project property: `-P` and apply similar convention as in task rule - e.g. `qcmVersion` or whatever will work for you. If the answer was useful, please accept it. – Opal May 09 '15 at 16:48
  • Thanks for your answer, I'll test later the part for customizing but you solved my problem ! – Gabriel May 09 '15 at 20:35
  • @Gabriel, I've updated the answer to make `artifacts` block to work. I'd recommend to define all the variants names in some list in the script. – Opal May 10 '15 at 06:04
  • I've found an easier way, see my answer. Sorry for the time you took to answer this question :/ But I really liked the help you provided ! I wasn't aware of task rules or things like that, you made me discover these. Thank you again ! BTW, with your anwser, `project.tasks['buildqcm']` is not needed ; just `buildqcm`. – Gabriel May 10 '15 at 07:58
  • Sure, you're welcome :) In gradle there are many different ways to achieve to goal :) In the example you provided I just wasn't sure it task will already exist, hence I did it via `getAt`. – Opal May 18 '15 at 08:29