How to create a custom task in gradle to pack java and kotlin code to a jar? - gradle

We have a multi modular setup and we are sharing some tests classes between the modules (mainly Fakes implementations). Our current solution (that you can find below) works just for classes written in Java, but we are looking at supporting also shared kotlin classes.
if (isAndroidLibrary()) {
task compileTestCommonJar(type: JavaCompile) {
classpath = compileDebugUnitTestJavaWithJavac.classpath
source sourceSets.testShared.java.srcDirs
destinationDir = file('build/testCommon')
}
taskToDependOn = compileDebugUnitTestSources
} else {
task compileTestCommonJar(type: JavaCompile) {
classpath = compileTestJava.classpath
source sourceSets.testShared.java.srcDirs
destinationDir = file('build/testCommon')
}
taskToDependOn = testClasses
}
task testJar(type: Jar, dependsOn: taskToDependOn) {
classifier = 'tests'
from compileTestCommonJar.outputs
}
How can I modify the compileTestCommonJar so it supports kotlin?

Here is what we do:
In the module with shared test classes, pack the test source set output into a jar
configurations { tests }
...
task testJar(type: Jar, dependsOn: testClasses) {
baseName = "test-${project.archivesBaseName}"
from sourceSets.test.output
}
artifacts { tests testJar }
In a module that depends on the shared classes
dependencies {
testCompile project(path: ":my-project-with-shared-test-classes", configuration: "tests")
}
PS: Honestly, I would prefer to have a separate Gradle module with common test classes as it's more explicit solution.

The task compileTestCommonJar(type: JavaCompile) compiles .java files only because its the task of JavaCompile type.
There's KotlinCompile task aswell, so you would need to merge it, it basically works similary to JavaCompile but compiles .kt files only.
Said that i wouldn't use task system to share the dependencies across, i would use separate module and work with default compileTestKotlin and compileTestJava task's outputs

Related

gradle - Add custom configuration to installDist for a specific task

I'm trying to make a custom runtime dependency configuration, so that the specified dependencies will only be installed for a specific task. The dependencies are installed using the installDist task. So it seems like I need the configuration to be added to the runtimeClasspath for one task and not the other. I'm thinking I need a custom distribution, but I'm not sure how to set that to have a different runtimeClasspath.
In the example below, I want the run2 task to have the myRuntimeDep dependencies installed, but for the run1 task I do not.
I've be struggling to figure this out all day, does someone know what I'm missing?
Example build.gradle:
configurations {
myRuntimeDep.extendsFrom runtimeOnly
}
dependencies {
...
myRuntimeDep 'the:dependency:1.0'
}
task run1(type: JavaExec, dependsOn: installDist) {
// does not need myRuntimeDep dependencies
}
task run2(type: JavaExec, dependsOn: installDist) {
// needs myRuntimeDep dependencies
}
So after a long weekend, I sort of got something working. Maybe someone can tell me if there's a better way? Also, it doesn't fully work because it doesn't follow transitive dependencies with the configuration (which is kind of a pain because all sub-dependencies need to be manually added).
Solution:
top-level build.gradle
...
subprojects {
configurations {
fooRuntime.extendsFrom runtimeOnly
fooClasspath.extendsFrom runtimeClasspath, fooRuntime
}
distributions {
foo {
contents {
from installDist.destinationDir
from(configurations.fooClasspath) {
into 'lib'
}
}
}
}
installFooDist.dependsOn installDist
}
project A build.gradle
dependencies {
fooRuntime project(':projectB')
fooRuntime project(':projectC') // only need this because transitive dependencies won't work
}
task run(type: JavaExec, dependsOn: installFooDist) {
classpath = fileTree("$installFooDist.destinationDir/lib")
}
project B build.gradle
dependencies {
fooRuntime project(':projectC')
}
task run(type: JavaExec, dependsOn: installFooDist) {
classpath = fileTree("$installFooDist.destinationDir/lib")
}

Not able to extend a Gradle task

I'm new with Gradle (we're switching from SBT) and using it to build apps made with Play Framework.
I need to add some filtering on the resources before Gradle processes them (I would like to inject some build properties into the configuration to make them available from the code).
I've managed to "extend" the java processResources task, but, for some reason, I cannot do the same with play processPlayBinaryPlayResources.
processPlayBinaryPlayResources {
filter ReplaceTokens, tokens: [
"applicationVersion": version
]
}
Even this doesn't work :
def playVersion = "2.6.20"
def scalaVersion = "2.12"
def javaVersion = "1.8"
apply plugin: "java"
apply plugin: "idea"
apply plugin: "play"
model {
components {
play {
platform play: playVersion, scala: scalaVersion, java: javaVersion
injectedRoutesGenerator = true
}
}
}
processPlayBinaryPlayResources {
doLast {
println("ok")
}
}
dependencies {
compile "io.vavr:vavr:0.9.2"
compile "org.imgscalr:imgscalr-lib:4.2"
compile "com.typesafe.play:play-guice_${scalaVersion}:2.6.20"
compile "com.typesafe.akka:akka-http_${scalaVersion}:10.1.5"
compile "com.typesafe.play:filters-helpers_${scalaVersion}:2.6.20"
compile "ch.qos.logback:logback-classic:1.2.3"
}
It yields :
> Could not find method processPlayBinaryPlayResources() for arguments [build_6grwx7eowye82rdqpu4qlinur$_run_closure2#582d9dbd] on root project 'myproject' of type org.gradle.api.Project.
Any idea why ?
Your assumption of finding a task processPlayBinaryPlayResources is based on that, that the java plugin automatically adds a processResources task for all source set as process<sourceset_name>Resources . This happens only when a source set is added using java pugins sourceSets method which, in this case PlayBinaryPlay is not. The play plugin uses its own DSL to configure source sets.
Therefore when you try extending processPlayBinaryPlayResources it does not happen as no such tasks by that name exists in the first place and hence while delegating it to Project, you end up with this
Could not find method processPlayBinaryPlayResources() for arguments [build_6grwx7eowye82rdqpu4qlinur$_run_closure2#582d9dbd] on root project 'myproject' of type org.gradle.api.Project.
Lastly, I would like to add that the processPlayBinaryPlayResources task is not added by the play plugin.

Using testCompile output from other subproject (Gradle Kotlin DSL)

I have some utility files in the test sources in one of my gradle subproject and would like to use them in an other subproject.
My "source" subproject is called core, while the one uses it is called tem.
I try to migrate and integrate the following example:
In your Server project:
configurations {
testArtifacts.extendsFrom testCompile
}
task testJar(type: Jar) {
classifier "test"
from sourceSets.test.output
}
artifacts {
testArtifacts testJar
}
In your ServerWeb project:
testCompile project(path: ":Server", configuration: 'testArtifacts')
As far as I get is making the conversation. I added the following to my core.gradle.kts:
val testConfig = configurations.create("testArtifacts") {
extendsFrom(configurations["testCompile"])
}
tasks.register("testJar", Jar::class.java) {
classifier += "test"
from(sourceSets["test"].output)
}
artifacts {
add("testArtifacts", tasks.named<Jar>("testJar") )
}
And tried to refer to it in tem.gradle.kts:
testImplementation(project(":core", "testArtifacts"))
It compiles, but I still can't access the classes from core.
Where did I miss something?
Most of your code should be OK
But you must define classesDirs for jar
tasks.register<Jar>("testJar") {
dependsOn("testClasses")
archiveBaseName.set("${project.name}-test")
from(sourceSets["test"].output.classesDirs)
}
I also added depends on testClasses to be sure that classes are compiled.
You can test that jar is OK by executing testJar task. Then verify that generated jar contains your classes. If you make mistake with from method call then you get empty jar.
The following configuration worked for me to include both the test classes and test resources:
core build.gradle.kts
val testConfig = configurations.create("testArtifacts") {
extendsFrom(configurations["testCompile"])
}
tasks.register("testJar", Jar::class.java) {
dependsOn("testClasses")
classifier += "test"
from(sourceSets["test"].output)
}
artifacts {
add("testArtifacts", tasks.named<Jar>("testJar") )
}
tem build.gradle.kts
testImplementation(project(":core", "testArtifacts"))

Gradle compileKotlin includeRuntime not adding runtime to jar

I have a Kotlin Gradle project, and I would like to include Kotlin's runtime and stdlib in the jar file. I'm currently using this, but it's not including the runtime or stdlib when I build the project using the build.gradle configuration.
compileKotlin {
kotlinOptions {
includeRuntime = true
noStdlib = false
}
}
This is the Gradle code I'm using to include the runtime/stdlib in the jar, but it isn't working like I expect it to. Here's the full build.gradle file for some context:
https://github.com/BenWoodworth/CrossPlatformGreeter/blob/bd1da79f36e70e3d88ed871bc35502ecc3a852fb/build.gradle#L35-L43
Kotlin's Gradle documentation seems to indicate that setting kotlinOptions.includeRuntime to true should include the Kotlin runtime in the resulting .jar.
https://kotlinlang.org/docs/reference/using-gradle.html#attributes-specific-for-kotlin
Edit:
This might be related. When I run compileKotlin, I'm getting a couple of warnings related to the runtime:
:compileKotlin
w: Classpath entry points to a non-existent location: <no_path>\lib\kotlin-runtime.jar
BUILD SUCCESSFUL
Here's an alternative I came up with. It'll add the Kotlin runtime and stdlib to the jar using the jar task.
jar {
from {
String[] include = [
"kotlin-runtime-${version_kotlin}.jar",
"kotlin-stdlib-${version_kotlin}.jar"
]
configurations.compile
.findAll { include.contains(it.name) }
.collect { it.isDirectory() ? it : zipTree(it) }
}
}
Gradle Kotlin DSL:
tasks.withType<Jar> {
val include = setOf("kotlin-stdlib-1.4.0.jar")
configurations.runtimeClasspath.get()
.filter { it.name in include }
.map { zipTree(it) }
.also { from(it) }
}
Try this:
Build script
Unpack jar
Add kotlin runtime and rapack it
type gradle packJar to create jar with kotlin runtime in it
or
type gradle runJar to create and run the jar file
build Script

How to generate multiple jar files with gradle's java plugin

I have a multi-project gradle build using the java plugin setup as follows:
myProj/
settings.gradle
build.gradle
util/
build.gradle
In my util project, I would like to generate 2 jars... one for packageA and one for packageB. I'm a noob with gradle so any help here would be much appreciated. Here are my settings and gradle files:
myProj/settings.gradle
include 'util'
myProj/build.gradle
subprojects {
apply plugin: 'java'
repositories {
maven {
url "http://mymavenurl"
}
}
sourceSets {
main {
java {
srcDir 'src/java'
}
}
}
}
myProj/util/build.gradle
dependencies {
.
.
.
}
jar {
baseName = 'packageA'
includes = ['com/mycomp/packageA']
}
task packageBJar(type: Jar) {
dependsOn classes
includes = ['com/mycomp/packageB']
baseName = 'packageB'
}
When I try to build my project here is the output:
:util:compileJava
:util:processResources UP-TO-DATE
:util:classes
:util:jar
:util:assemble
:util:compileTestJava UP-TO-DATE
:util:processTestResources UP-TO-DATE
:util:testClasses UP-TO-DATE
:util:test
:util:check
:util:build
I would hope to see :util:packageBJar after classes, but I'm not having any luck.
One way is to declare packageBJar as an artifact of, say, the archives configuration:
artifacts {
archives packageBJar
}
Now gradle assemble, and therefore also gradle build, will produce packageBJar.

Resources