Gradle dependency management based on build variant for java project - gradle

I am using gradle to build a java spring project. What I am trying to do is:
If build target is dev or not specified, use dependency
compile("org.hsqldb:hsqldb")
If build target is prod or qa, use dependency
compile("mysql:mysql-connector-java:5.1.31")
I am not sure how to do this in gradle. First thing is how to pass the build type variable to the build script. The second thing is how to write the conditional dependency code based on the variable? Thanks in advance.

build.gradle is a Groovy file, so you can use if statements to dynamically change your dependencies, like this:
dependencies {
// other dependencies
if (someFlag) {
compile("org.hsqldb:hsqldb")
} else {
compile("mysql:mysql-connector-java:5.1.31")
}
}
To configure something like this one may use project properties:
dependencies {
// other dependencies
if (!project.hasProperty("targetEnv") || targetEnv == "dev") {
compile("org.hsqldb:hsqldb")
} else if (targetEnv == "prod" || targetEnv == "qa") {
compile("mysql:mysql-connector-java:5.1.31")
} else {
throw new InvalidUserDataException("Unknown target environment '$targetEnv'")
}
}
And now mysql dependency will be used when gradle is run with -PtargetEnv=qa or -PtargetEnv=prod, hsqldb — for cases when targetEnv is not specified or set to dev, and finally exception will be thrown if some unknown environment name is passed.

Related

How to set gradle subproject artifact as task input?

My gradle build has a subproject with a task that produces a file
$ ./gradlew :strings:tokenizeStrings # creates strings/string_tokens.csv
then in my root project I have a task which consumes that file
tasks.generateLocalizationFiles {
inputTokensCsvFile.set(layout.projectDirectory.file("strings/string_tokens.csv"))
}
this works, but since gradle doesn't know about the dependency, it only works if I run the two tasks manually in the right order
$ ./gradlew :strings:tokenizeStrings
$ ./gradlew :generateLocalizationFiles
I want to add the proper dependency to gradle so that I can run just :generateLocalizationFiles and it will go into the subproject and do whatever it needs to. But I can't figure out the right way to do it.
What I've tried:
Following Simple sharing of artifacts between projects, I tried adding a consumable configuration to the suproject build script
val localizationData by configurations.creating {
isCanBeConsumed = true
isCanBeResolved = false
}
tasks.tokenizeStrings {
artifacts {
add("localizationData", outputTokensCsvFile) {
builtBy(this)
}
}
}
and then a resolvable configuration plus the dependency to the root project build script
val localizedStringData by configurations.creating {
isCanBeConsumed = false
isCanBeResolved = true
}
// hook up our resolvable configuration to the strings' consumable configuration
dependencies {
localizedStringData(project(mapOf(
"path" to ":strings",
"configuration" to "localizationData")
))
}
tasks.generateLocalizationFiles {
dependsOn(localizedStringData)
inputTokensCsvFile.set(localizedStringData.singleFile)
}
but that fails, seemingly because the consumable configuration is not populated?
Caused by: java.lang.IllegalStateException: Expected configuration ':localizedStringData' to contain exactly one file, however, it contains no files.
You need to add the outgoing artifact directly in the subproject build script, not inside the task configuration (which is only run lazily). You also don't need builtBy if you're using a RegularFileProperty for the artifact.
val localizationData by configurations.creating {
isCanBeConsumed = true
isCanBeResolved = false
}
artifacts {
add("localizationData", tasks.tokenizeStrings.flatMap { it.outputTokensCsvFile })
}
The trick is to use flatMap to lazily access the task. You should similarly use map when passing it to the task resolving the data. That allows for lazy task creation and implicitly tells gradle about the dependency between the two:
tasks.generateLocalizationFiles {
inputTokensCsvFile.set(localizedStringData.elements.map { it.first().asFile })
}
This still feels somewhat hacky, since it would be very clumsy if you wanted to repeat this for many artifacts, but it does seem to be the idiomatic way of doing it in gradle since it doesn't require any explicit dependency creation via builtBy/dependsOn.

gradle: how to create 2 jars with different java versions using Springboot bootJar task and java toolchain

springboot gradle plugin version: org.springframework.boot:spring-boot-gradle-plugin:2.7.2
I have defined different versions of Java for compilation and test as mentioned here but thats just for compile and test.
tasks.withType(JavaCompile).configureEach {
javaCompiler = javaToolchains.compilerFor {
languageVersion = JavaLanguageVersion.of(8)
}
}
task('testsOn14', type: Test) {
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(14)
}
}
Ref: https://docs.gradle.org/current/samples/sample_jvm_multi_project_with_toolchains.html
The jar task is defined simply as jar.dependsOn(bootJar)
I want this task to create 2 jars: myproject-j8.jar and myproject-j14.jar meaning one build with Java-8 and other with Java-14. No idea how to do that. Thanks for taking a look.

Can Gradle produce multiple Kotlin Native binaries (for one OS)?

Can I convince Gradle to produce multiple binaries? I have several Kotlin packages with files that have a proper "fun main(...)" but the default IntelliJ build.gradle file only allows me to specifiy one "compilations.main.entryPoint".
I could put the main functions into Kotlin classes or objects if that would help.
Changing the entryPoint argument to an array did not work :)
If it's not currently possible, is it a general limitation of Gradle or only of the "kotlin-multiplatform" plugin?
plugins {
id 'kotlin-multiplatform' version '1.3.11'
}
repositories {
mavenCentral()
}
kotlin {
targets {
// For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64
// For Linux, preset should be changed to e.g. presets.linuxX64
// For MacOS, preset should be changed to e.g. presets.macosX64
fromPreset(presets.mingwX64, 'mingw')
configure([mingw]) {
// Comment to generate Kotlin/Native library (KLIB) instead of executable file:
compilations.main.outputKinds('executable')
// Change to specify fully qualified name of your application's entry point:
compilations.main.entryPoint = 'hello.main'
}
}
sourceSets {
// Note: To enable common source sets please comment out 'kotlin.import.noCommonSourceSets' property
// in gradle.properties file and re-import your project in IDE.
mingwMain {
}
mingwTest {
}
}
}
task runProgram {
def buildType = 'debug' // 'release' - Change to 'debug' to run application with debug symbols.
dependsOn "link${buildType.capitalize()}ExecutableMingw"
doLast {
def programFile = kotlin.targets.mingw.compilations.main.getBinary('EXECUTABLE', buildType)
exec {
executable programFile
args ''
}
}
}
In https://github.com/JetBrains/kotlin-native/issues/2505 I've just got the answer that this will be possible with Kotlin Native 1.3.20!

How to set Eclipse's output directory from Gradle

I am using Gradle's eclipse plugin. For cross-project reference reasons, I need Eclipse's output directory to not be the default bin, rather ecbuild.
Everytime I run ./gradlew eclipse, it overwrites this output directory setting.
How to make sure it doesn't, or how to set it within gradle build script ?
Add this to the build.gradle script:
eclipse {
classpath { defaultOutputDir = file('ecbuild') }
}
This might require you to upgrade the version of your gradle wrapper.
If so, run :
./gradlew wrapper --gradle-version 3.3
In my case, seting defaultOutputDir was not enough. So I did the following:
eclipse {
classpath {
defaultOutputDir = file("build")
file.whenMerged {
entries.each { entry ->
if (entry.kind == 'src' && entry.hasProperty('output')) {
entry.output = entry.output.replace('bin/', "build/")
}
}
}
}
}

Why my dependency is printed twice?

I have the following fairly simple build.gradle build script:
repositories {
mavenCentral()
}
configurations { libs }
dependencies {
libs 'org.hibernate:hibernate-core:4.3.5.Final'
}
configurations.libs.files { println it }
When I run it with gradlew build (I'm using Gradle 1.12, latest at the moment) I get the following:
DefaultExternalModuleDependency{group='org.hibernate', name='hibernate-core', version='4.3.5.Final', configuration='default'}
DefaultExternalModuleDependency{group='org.hibernate', name='hibernate-core', version='4.3.5.Final', configuration='default'}
These seem to be the same dependencies, but I don't get it why there are 2 of them when I have added just a single one.
Does anyone know why? Did I do something wrong? Or is there something I don't understand?
The Configuration#files method expects a predicate. (For API details, see Configuration in the Gradle Build Language Reference.) If you instead pass it a closure containing a println statement, (seemingly) strange things will happen. You probably want something like the following instead:
// configurations should only be resolved in
// the execution phase, so let's declare a task
task printLibs {
doLast {
configurations.libs.each { println it }
}
}

Resources