Is it possible to use #Grab inside a Gradle build.gradle file?
My understanding is that Gradle build scripts are written in Groovy and Grape/#Grab is built into Groovy.
But if i attempt to add a #Grab annotation into a build.gradle file it doesn't work.
e.g. adding:
#Grab(group='org.springframework', module='spring-orm', version='3.2.5.RELEASE')
import org.springframework.jdbc.core.JdbcTemplate
it gives me the error
org.gradle.groovy.scripts.ScriptCompilationException: Could not compile build file.
Is this possible?
According to this post, the answer is no, we can't use #Grab.
However, there is an equivalent way to declare a dependency and add it to the classpath, as shown below (usage for the example: gradle go):
buildscript {
repositories {
jcenter()
}
dependencies {
classpath group: 'org.springframework', name: 'spring-orm', version: '3.2.5.RELEASE'
}
}
import org.springframework.jdbc.core.JdbcTemplate
task go() {
doLast {
println 'TRACER : ' + JdbcTemplate.class.getSimpleName()
}
}
Related
I made a decision to migrate from Dependency Management Plugin to Gradle built-in BOM import support. Since Gradle built-in BOM import support has better performance But
I run into the issue:
I cannot find alternatives for dependency and dependencySet in native Gradle:
dependencyManagement {
dependencies {
dependency("org.springframework:spring-core:4.0.3.RELEASE")
}
}
//or
dependencyManagement {
dependencies {
dependencySet(group:'org.slf4j', version: '1.7.7') {
entry 'slf4j-api'
entry 'slf4j-simple'
}
}
}
and then I could use dependency without version
dependencies {
compile 'org.springframework:spring-core'
}
How can I get the same behavior in naive Gradle? I mean: I'd like to define a version once as I did it when using Dependency Management Plugin
Solution below helps to avoid versions copy-paste. However it isn't the same with Dependency Management plugin.
For Gradle Kotlin Dsl:
You can create buildSrc with you own code, when you can place any constants.
Algorithm:
Create folder buildSrc/src/main/kotlin
Create file buildSrc/src/main/kotlin/Versions.kt with content:
object Versions {
const val junitVersion = "5.5.5" // just example
}
Create file buildSrc/build.gradle.kts with content:
plugins {
`kotlin-dsl`
}
Use the following syntax in your gradle.kts files:
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:${Versions.junitVersion}")
}
For Gradle Groovy:
Create file gradle.properties
Put versions there with syntax like okhttp_version=4.2.0
Use the following syntax in your gradle files:
dependencies {
compile group: 'com.squareup.okhttp3', name: 'okhttp', version: okhttp_version
}
You can do so on the gradle.properties file. I.e.:
# APPLICATION PROPERTIES
name=projectName
group=com.domain
version=1.0.0
description=A brief description
gradleScripts=https://raw.githubusercontent.com/hexagonkt/hexagon/1.2.0/gradle
# DEPENDENCIES VERSIONS
kotlinVersion=1.3.61
kotlinCoroutinesVersion=1.3.2
Or in settings.gradle if you don't want to create another file:
rootProject.name = "hexagon-contact-application"
gradle.rootProject {
allprojects {
version = "1.0.0"
group = "org.hexagonkt"
description = "Contact application backend api"
}
extensions.gradleScripts = "https://raw.githubusercontent.com/hexagonkt/hexagon/1.0.18/gradle"
extensions.kotlinVersion = "1.3.50"
extensions.kotlinCoroutinesVersion = "1.3.2"
extensions.hexagonVersion = "1.0.21"
extensions.logbackVersion = "1.2.3"
extensions.bcryptVersion="0.8.0"
extensions.javaJwtVersion="3.8.2"
}
And if you want to avoid adding the version variable to all related dependencies, you can create a method in the build file:
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.50'
}
apply from: "$gradleScripts/kotlin.gradle"
apply from: "$gradleScripts/service.gradle"
apply from: "$gradleScripts/junit.gradle"
defaultTasks("test")
mainClassName = 'com.hexagonkt.contact.ApplicationKt'
applicationDefaultJvmArgs = ["-Xms64M", "-Xmx2G", "-XX:+UseNUMA", "-XX:+UseParallelGC"]
dependencies {
httpkt(it, "http_server_jetty")
httpkt(it, "store_mongodb")
httpkt(it, "hexagon_web")
implementation("at.favre.lib:bcrypt:$bcryptVersion")
implementation("com.auth0:java-jwt:$javaJwtVersion")
testImplementation("com.hexagonkt:port_http_client:$hexagonVersion")
}
private void httpkt(final def dependencies, final String artifact) {
dependencies.implementation("com.hexagonkt:$artifact:$hexagonVersion")
}
I'm fairly new to Groovy and I'm trying to wrap my head around Gradle. If I import the org.jvnet.hudson.plugins through Grapes it works perfectly and the dependency is resolved. But if I try to retrieve the dependency using Gradle the dependency is not resolved.
The package org.eclipse.hudson:hudson-core:3.2.1 works with both Gradle and Grape.
A dependency that is not resolved using Gradle
compile 'org.jvnet.hudson.plugins:checkstyle:3.42'
A dependency which is resolved using Grape
#Grab('org.jvnet.hudson.plugins:checkstyle:3.42')
A dependency which is resolved using Gradle
compile 'org.eclipse.hudson:hudson-core:3.2.1'
Error during Gradle build
line 3, column 1.
import hudson.plugins.checkstyle.CheckStyleResultAction;
^
The build.gradle
apply plugin: 'groovy'
repositories {
mavenCentral()
maven {
url "http://repo.jenkins-ci.org/releases/"
}
}
configurations {
ivy
}
sourceSets {
main {
groovy {
srcDirs = ['src/']
}
}
test {
groovy {
srcDirs = ['test/']
}
}
}
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.4.11'
compile "org.apache.ivy:ivy:2.4.0"
ivy "org.apache.ivy:ivy:2.3.0"
// Works
compile 'org.eclipse.hudson:hudson-core:3.2.1'
// Does not work
compile 'org.jvnet.hudson.plugins:checkstyle:3.42'
}
tasks.withType(GroovyCompile) {
groovyClasspath += configurations.ivy
}
You're probably not actually downloading the jar you think you are. Looks like the default artifact that comes back from the org.jvnet.hudson.plugins:checkstyle:3.42 dependency is actually a file named checkstyle-3.42.hpi.
To get the jar which contains the classes instead, use:
compile group: 'org.jvnet.hudson.plugins', name: 'checkstyle', version:'3.42', ext: 'jar'
Then that class will be found on your classpath (and you'll be on to locating the next missing dependency).
I am trying to execute my Kotlin class using the command:
./gradlew -q run < src/main/kotlin/samples/input.txt
Here is my HelloWorld.kt class:
package samples
fun main(args: Array<String>) {
println("Hello, world!")
val lineRead = readLine()
println(lineRead)
}
Here is my build.gradle.kts:
plugins {
kotlin("jvm")
application
}
application {
mainClassName = "samples.HelloWorldKt"
}
dependencies {
compile(kotlin("stdlib"))
}
repositories {
jcenter()
}
The code executes, but the data contained inside the input.txt file is not displayed. Here is the output I get:
Hello, world!
null
I want to be able to execute the gradlew command above and the input.txt stream be redirected to stdio. I can easily do that in C++. Once I compile my .cpp file, I can run:
./my_code < input.txt
and it executes as expected.
How can I achieve the same thing with Kotlin and Gradle?
Update: Based on this answer, I've tried adding this to build.gradle.kts but it is not a valid syntax:
AjahnCharles suggestion about run { standardInput = System.in } is correct, but to port it to kotlin-dsl you need a different syntax.
run in this case is the task name and you configure existing task of application plugin.
To configure existing task in kotlin-dsl you should use one of this ways:
val run by tasks.getting(JavaExec::class) {
standardInput = System.`in`
}
or
val run: JavaExec by tasks
run.standardInput = System.`in`
The upcoming version of Gradle 4.3 should provide API for plugin writers to read user input.
The reason of difference between of Groovy and Kotlin in this case because Groovy uses dynamic types, but in Kotlin you must specify task type to have autocompletion and just to compile config script
I finally settled on this (Gradle 7.1.1):
plugins {
application
}
tasks.getByName("run", JavaExec::class) {
standardInput = System.`in`
}
I don't know enough Kotlin yet to judge whether this is equivalent to https://stackoverflow.com/a/46662535/253921.
Almost, but this doesn't work :'(
In Theory
My understanding: < input.txt sets the standard input for the gradlew process, but by default this is not forwarded to your program.
You want to add this to your build.gradle.kts:
run {
standardInput = System.`in`
}
Sources:
https://discuss.gradle.org/t/why-doesnt-system-in-read-block-when-im-using-gradle/3308/2
https://discuss.gradle.org/t/how-can-i-execute-a-java-application-that-asks-for-user-input/3264
In Practice
These build configs look about the same to me, yet Groovy works and Kotlin doesn't. I'm starting to think that the Gradle Kotlin DSL doesn't support the standardInput term yet :/
Here's my working Groovy version if that's any help:
apply plugin: 'kotlin'
apply plugin: 'application'
buildscript {
ext.kotlin_version = '1.1.4'
repositories {
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
repositories {
jcenter()
}
dependencies {
// api => exported to consumers (found on their compile classpath)
// implementation => used internally (not exposed to consumers)
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
mainClassName = "samples.HelloWorldKt"
run {
standardInput = System.in
}
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
I'm facing behavior that I can't explain, using gradle 1.10 I have:
settings.gradle:
include('lib1', 'lib2', 'web')
build.gradle:
subprojects {
apply plugin: 'java'
}
project(':web') {
apply plugin: 'war'
dependencies {
compile project(':lib1')
}
task myTask(type: JavaExec, dependsOn: 'compileJava') {
main = "some.thirdparty.Class"
args "--searchPath", configurations.runtime.asPath
}
}
project(':lib1') {
dependencies {
compile project(':lib2')
}
}
project(':lib2') {
}
When I run gradle clean war I only have lib1.jar in war/build/libs/web.war/WEB-INF/lib.
To make WEB-INF/lib contain both lib1.jar and lib2.jar I have to:
move project('web') block to the end of the file
update configurations.runtime.asPath to configurations.runtime (but I need to provide class path as a path, so it is not a solution)
I read the build lifecycle description, tried to compare --debug outputs but that didn't help.
Why is this happening? And what would be a good solution to provide the module runtime class path as a path in JavaExec task please?
asPath resolves the configuration, but resolution will only work correctly if it happens at execution time rather than configuration time (in particular in the presence of project dependencies). Try to wrap the args line with doFirst { ... }.