I'm using (or trying to use) Gradle to build a plain Java (not Android) multi-module project, which contains a CLI and several micro-services. I have a simple, single-configuration build working.
I'd like to be able to build it two different ways: a "development" build with one group of settings and dependencies, and a "deployment" build with different settings and dependencies. Some settings and dependencies will overlap between the two.
In other build tools, this would correspond to "Debug" and "Release" build configurations. But for Gradle, I've seen build types, variants, flavors, and capabilities, and combinations of all of those—some of which seem to be Android specific, some depending on plugins that seem to have fallen out of date. But I can't seem to locate a straightforward example of a "traditional" debug/release build setup.
I have a simple approach working using manually created buildDebug, buildRelease, assembleDebug, assembleRelease, etc. tasks, but it feels like I'm working around Gradle rather than with it.
Does anyone have such an example who would be willing to share their work? Many thanks!
It looks like my early searches (i.e. "gradle debug and release builds") and my expectation of something built into Gradle sent me down the wrong rabbit hole. I finally stumbled across this question only after it occurred to me to search on "gradle equivalent of maven build profiles".
It's possible I'm missing a Gradle feature (i.e. variants) I could be taking advantage of, but it appears the correct solution may be:
ext {
env = findProperty('env') ?: 'debug'
}
dependencies {
// shared dependencies
if (env == 'debug') {
// debug build dependencies
}
if (env == 'release') {
// release build dependencies
}
}
The build is selected by setting the env property on the command line:
# debug build; can use either
$ gradle build
$ gradle build -Penv=debug
# release build
$ gradle build -Penv=release
Hope that helps a fellow Gradle newbie.
Related
I use gitflow-incremental-builder with Maven in a monorepo model. It allows me to:
Build only these modules in feature branch that differ from main branch.
Build only these modules that changed from last successful build tag.
When a library changes, build all the modules that use it.
Build a library if needed for modules, but if nothing changed, skip tests (skipTestsForUpstreamModules)
Force build all.
Changes are resolved using git log and then it affects the reactor config.
I am looking for a similar tool that will do it for Gradle.
Gradle Build Cache will automatically track the inputs and outputs of tasks and will skip any that have not changed.
Enabling Gradle Build Cache
It can be enabled locally by adding in gradle.properties
org.gradle.caching=true
or by adding a flag to the command line
./gradlew tests --build-cache
Sharing the build cache
The build cache for a project can be shared across multiple machines over HTTP. Sharing the build cache remotely isn't necessary - it can still work, even if the cache is stored locally.
Register task inputs
Gradle needs to know about the all inputs and outputs of tasks, otherwise tasks might be skipped, so make sure they are correctly registered.
For example, if some integration tests depend on an environment variable, then register the environment variable as a test-task input.
// build.gradle.kts
tasks.named("integrationTest") {
// TEST_TASK_QUALITY is used in integration tests to change <blah blah blah>
// register it as an input so Gradle knows when to re-run the tests
inputs.property("TEST_TASK_QUALITY", providers.environmentVariable("TEST_TASK_QUALITY"))
}
Stable task outputs
Gradle will use the outputs of some tasks as the inputs of other tasks. If the outputs aren't stable, then Gradle will always re-run the dependent tasks.
For that reason, it's worth enabling reproducible builds in all projects.
// build.gradle.kts
tasks.withType<AbstractArchiveTask>().configureEach {
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
}
Also, consider input normalization for any custom files your project has.
The solution which needs bootstrapping is supplied as java code. Absolutely sure that this is necessary.
Receivers of the solution are guaranteed to have a suitable JDK
However, receivers of the solution are unable to install Maven (they don't know how to and cannot be taught)
My idea is to include some sort of Maven with the project, such that can be set up in a script like so:
set up maven repo as a folder under the solution folder (using relative reference)
set up anything else maven needs (don't know what, exactly)
call /path/to/maven/mvn compile -f /path/to/oneAndOnly/pom.xml
java /target/MySolutionClas
I am aware of: https://dzone.com/articles/embedding-maven but it gets confusing when he talks about configuring the portable maven into the pom.xml - wait, how is that pom.xml going to mean anything if maven is not configured yet?
(PS: I mean no disrespect to the author. I probably got it all wrong)
One could include a shell script that would setup maven if it is not already present.
The same for building and packaging encapsulating the complexities of the setup to just runing a couple of scripts.
Maven Wrapper aims to do just that, similar to the gradle wrapper seen in many gradle projects.
Running the wrapper goal of the maven wrapper plugin will generate a mvnw script in your project that can be run in place of a globally installed mvn command.
It's part of the maven 3.7.0 release, and documented more fully here: https://maven.apache.org/plugins/maven-wrapper-plugin/index.html
See https://github.com/takari/maven-wrapper for maven < 3.7.0
I use Kotlin DSL script (.kts) for building. There the structure of my project is:
Root project 'demo'
+--- Project ':backend'
\--- Project ':frontend'
I need to build project frontend first, than backend. I tried
include(":frontend")
include(":backend)
and
include(":frontend", ":backend")
with and without : in settings.gradle.kts of root project, but still the order of build is alphabetical - backend, than frontend.
View source code on GitHub
Do you have any ideas what is wrong?
There is nothing wrong. If you don't specify any inter-project dependencies, Gradle will execute them in alphabetical order. This should be fine if the two projects are unrelated, as they are now.
But let's say you like to build the frontend (using node) and then include those resources in the backend (using Spring Boot). Then you will need to make the backend depend on frontend project. Then Gradle will honor the dependency graph and build the frontend first.
There are many ways to do that. One is to use the java plugin in the frontend to build a jar file of your frontend resources. You can then make a normal project dependency to it. You could also make a dependency directly into the frontend project's "internal" build tasks, but that is a bit frowned upon. Or you could declare your own artifact, or do a it in a bunch of other different ways.
For the first approach, you can build a jar file of your frontend resources like this:
plugins {
// ...
id("java")
}
java {
// Required to make the jar artifact compatible with your backend, which is configured for Java 1.8
targetCompatibility = JavaVersion.VERSION_1_8
}
tasks.named("jar", Jar::class) {
dependsOn("assembleFrontend")
from("$buildDir/dist")
into("static")
}
Then in the backend, depend on it like this:
dependencies {
// ...
runtimeOnly(project(":frontend"))
}
There are a few other things wrong with your build script as well.
The runtime configuration is deprecated; use runtimeOnly instead (for your spring-boot-devtools dependency).
A multi-project should only have a single settings.gradle file, but you have one in each project. Delete them except for the one in the root folder.
You have declared the org.siouan.frontend plugin twice: once using the recommended way and once using the "old" way. Remove the latter (everything in the buildscript block and the apply statement.
Also, while I am not familiar with the org.siouan.frontend plugin, it appears it does not declare inputs and outputs for you - probably because it is very generic. So to avoid running npm each time you build your backend (as you now have a dependency to the frontend), you should declare proper inputs and outputs for the frontend tasks like installFrontend and assembleFrontend.
I've recently returned to working on a Scala project, after having spent some time working with the nodejs ecosystem. After getting used to package managers like npm and yarn, I feel like I need to rethink the tools/processes I've been using for Java/Scala. With that in mind, there are several problems which appear to exist in the JVM world for which I'd like to know if there's some automated solution:
Given some list of dependencies without versions (but with group, module), is there some automated way to detect what the valid combinations (if any exist) of versions for each dependency are? Specifically, ensuring that there are no conflicting transitive dependencies?
I believe Java Modules should reduce/eliminate this issue, but I'm currently limited to Java 8 so can't use them.
Aside from manually changing version numbers in my build.gradle, is there any automated way to update a dependency from cli?
For example, I can do yarn install <package>#<version> to record the new version of a nodejs library I depend on and install it in one step - does anything similar exist for JVM projects?
Are there any tools similar to updtr for Java/Scala projects? Basically; a tool that will automatically try to update my dependencies and run tests with the new versions, and rollback if anything fails.
In case it matters, I'm currently using gradle as my build tool in a Scala 2.11 project; but I'm curious to know about any answers that would apply to any mixed language project using any build tool. Ultimately I just want to avoid manually checking every one of my dependencies against every other dependency manually - anything else is an extra nicety.
I can answer only point 3 of your question, and even this - only partially.
You might be interested in Gradle Versions Plugin by Ben Manes.
This plugin does not update your dependencies (so all the more it does not have the test-running + rollback functionality).
However, it will list all the dependencies that can be upgraded, like that (it's only one of the possible formats):
The following dependencies are using the latest integration
version:
- backport-util-concurrent:backport-util-concurrent:3.1
- backport-util-concurrent:backport-util-concurrent-java12:3.1
The following dependencies exceed the version found at the integration
revision level:
- com.google.guava:guava [99.0-SNAPSHOT 3.0]
http://code.google.com/p/google-guice/
- com.google.inject.extensions:guice-multibindings [2.0 -> 3.0]
http://code.google.com/p/google-guice/
Gradle updates:
- Gradle: [4.6 -> 4.7 -> 4.8-rc-2]
Source: Report format
Moreover, the plugin can be configured as to what is not considered to be an upgradeable version:
boolean rejected = ['alpha', 'beta', 'rc', 'cr', 'm'].any { qualifier ->
selection.candidate.version ==~ /(?i).*[.-]${qualifier}[.\d-]*/
}
Source: Revisions
As example, i have program with version 0.0.1. Maven must create separate folder for it - "target/0.0.1/" instead of "target/". It must be done for version "0.0.2", "0.0.3", etc.
I use Eclipse & it's Maven:
Version: Oxygen.3a Release (4.7.3a)
Build id: 20180405-1200
JDK 1.8.0_172
Maven doesn't work that way, and trying to do something like that will lead to a path of suffering. Options I see include
Creating a separate assembly (and output Jar) for each version (see Maven Assembly Plugin)
Create a multi-project reactor with a separate output configuration for every project. Keep common code in one project that you link as dependency from the others. Possibly use the maven-shade-plugin to re-link the packages in your common project into the individual output projects
As you can see, both of these approaches are pretty hacky and require advanced Maven skills. It would be much easier to have parameterized builds where you pass in the output version. But that would make sense on a CI server like Jenkins.