path | title |
---|---|
/learnings/gradle |
Learnings: Gradle |
- About Gradle
- Using Gradle
- Files
- Dependencies
- Tasks
- in Kotlin mode
- Gradle Interacting with Java
- >
- Making your own Gradle plugins (and or buildSrc)
- Build Scan
- Plugins
- Gradle Object mode
- Lifecycle
- Build Cache
- Multi project builds with Gradle
- Misc workflow / developer experience tips for people using your Gradle build scripts
frequent releases: every 6-8 weeks
gradle init will create a basic project for you after prompting you to ask some questions.
Listing tasks gradle tasks
. BUT see Gradle_Tasks_And_The_CLI_Task_List
Listing dependency tree: gradle dependencies
(or gradle dep
)
gradlew
is the wrapper - recommended you use the wrapper (it makes sure everyone on the same project is using the same version of Gradle). It will automatically download the correct Gradle version if you don't have it.
gradlew(.bat), gradle
folders are both checked into source control (yes even the .jar) binary file
--console=plain
<-- assume a stupid TTY (ie like not clearing running task inventories via curses)
-p can use this to override properties specified in gradle.properties files on CLi
--warning-mode all
<-- turns on warnings for plugins etc for this task run (but potentially loud)
- build.grade(.kts) <-- delegate to org.gradle.api.Project. Is build script
- settings.gradle(.kts) <-- delegate to org.gradle.api.initializaton.Settings. slightly before the build.gradle script aka some things can only be modified here
- gradle.properties <— items defined in here can be accessed as properties by settings.gradle or build.gradle files. Can live in root of project or home dir
May vary based on what language plugin you're using Gradle with
Note: Gradle handles SNAPSHOT versions of libraries a different way than released libraries: they are marked as changed and only cached for a (defined or default) TTL.
Can also depend on other projects ie in a multi project build implementation project("mysubproject")
in the dependencies block
Can manage dependencies for JVM languages, plus Swift and C++ builds.
Can also use the project dependencies plugin and have it output an HTML version of gradle deps
default: highest version number wins
you can use rich version constraints to control that a bit
The dependency blocks are also closures too where you can exclude transitive deps if you need
- Gradle_Transitive_Deps_And_Implementation_Deps
Instead of declaring a dependency using a string notation, can use a variable name.
Really good when you have multiple projects in a build but they all - say - have common versions of common libraries.
Can declare in code, in dependencyResolutionMangament.versionCatalogs OR in libs.versions.toml file.
Example of syntax:
[versions]
groovy = "3.0.5"
[libraries]
groovy-core = { module = "org.codehaus.groovy:groovy", version.ref = "groovy" }
Use this like:
implementation(libraries.groovy-core)
Versions can use a simple string or a Gradle rich version
You can also import a TOML file from an external location ala file!!!!!!!!! (Practically this could let you reuse the catalog of the main build for buildSrc
but also wonder if this would be useful in a multi repo/one microservice per repo/enterprise or org wide standard catalog somehow???
For the later you could create a custom version catalog plugin and publish / use that in affected microservices.
If your alias name has -
, _
and .
these will be normalized to .
. For example foo-bar as an alias is converted to foo.bar automatically. Source
- [Gradle documentation on version catalogs
configurations are also a way to label (and group) dependencies. Configuration types may be provided by plugins.
single automic piece of work for a build, composed of actions.
have groups, and dependencies (semantic relationship: A produces something, B consumes it)
You can skip actions in a task by throwing a StopExecutionException
which will skip further executions.
You can also skip a Task - for example if you have tasks that normally depend on a list of tasks, you can skip some of those dependent task by doing taskObject.enabled = false
.
using -x
on the command line will also disable various tasks.
See skip several tasks in Gradle quickly
task hi {dependsOn: ‘someOtherTask’} {
// can config ie task desc and group here
// dependsOn can be an array or a closure too (which must return a string or an array of tasks. Ie you have a filter task or dynamically created tasks…)…
doLast {
// stuff for execution can go here. Can have many doLasts or doFirsts
}.doLast {
// can chain these things too!!
}
}
These get put into the project object so you can access it there
When declaring an ad-hoc task — one that doesn’t have an explicit type — you should use Task.doLast{} if you’re only declaring a single action.
<<Gradle_Tasks_And_The_CLI_Task_List>>
gradle tasks
by default only shows you tasks assigned to a task group.
TO SEE ALL TASKS: gradle tasks --all
. Source
TO SEE A CUSTOM TASK IN GRADLE TASKS CLI LIST:
set group
and you'll likely want to set description
task hi {dependsOn: ‘someOtherTask’} {
group 'MY_SUPER_BUILDING_GROUP'
description 'this is a human description of what this tasks does and will appear in gradle tasks list'
}
In Groovy could do
frontend {
nodeVersion = '6.11.3'
cleanScript = 'run clean'
}
This causes errors in Kotlin mode, like Type mismatch: inferred type is String but Property<String!>! was expected
You need to use the .set
method on those properties (no, it is not per JavaBean protocol)
frontend {
nodeVersion.set("6.11.3")
cleanScript.set("run clean")
}
<<Gradle_Interact_With_Java>>
Built in Java plugin for this
main and test conventions, jUnit and testNG, javaDocs.
Other JVM languages usually inherit from Java plugin
can set source and resources dir (uses the Maven defaults but you can customize this).
creating fat jars. You could also somewhat put this together yourself by setting the manifest in the jar task and copy in the dependencies files from the build folder.
<<Gradle_Configuration_Api_vs_Implementation>>, <<Gradle_Java_Dependency_Types>>
api / implementation <-- different labels for dependancies given by plugins
difference:
- api - does changing this version mean your CONSUMERS care (ie yes it's part of the API people need to care). ONLY supported when using the Java library plugin
- implementation - private API detail <—
<<Gradle_Transitive_Deps_And_Implementation_Deps>>
NOTE: using an implementation type this means transitive dependencies will NOT be shared if another project in a multi project build tries to include your project (they need to declare their own dependency on it!!!)
Unless you're writing a library for public consumption, probably everything will be an implementation
.
Most important / useful configurations:
configuration name | notes |
---|---|
implementation |
you probably want this |
compileOnly |
duh, but compile phase only. Deprecated a bit see below. |
testImplementation |
only for tests |
testCompileOnly |
compileOnly but only for tests |
|
in transition currently: java library plugin replaced some of these
compile: split into implementation and api. See Gradle_Configuration_Api_vs_Implementation
testCompile became testImplementation
Sometimes you may like Gradle as a development tool but have enterprise standarization on Maven.
You can, however, get the best of both worlds through these hacks
dependencies {
def pomXml = new XmlSlurper().parse('pom.xml')
def pomDependencies = pomXml.dependencies.dependency
pomDependencies.each { dependency ->
def dependencySpec = "${dependency.groupId}:${dependency.artifactId}:${dependency.version}"
println "dynamically adding ${dependencySpec} from pom.xml"
if (dependency.scope == 'test') {
dependencies.add 'testCompile', dependencySpec
} else {
dependencies.add 'compile', dependencySpec
}
}
}
<<Kotlin_Gradle_BuildSrc>>
If you are using buildSrc to abstract imperative logic
In Gradle 7.3.3 Gradle plugins target Kotlin 1.4 for compatibility reasons.
Except that version - since Kotlin 1.6 is out as of the time of this writing - is deprecated with a compiler warning (Which isn't good if you run -Wall)
For now the best thing to do is to turn off -Wall in your buildSrc build.gradle.kt file, which hopefully shouldn't affect the rest of your project.
Github issues in Gradle repo about this:
- "Please remove fixed Kotlin runtime version limitation in Gradle".
- "Upgrade embedded Kotlin to 1.6.10"
build + --scan
will be prompted, you can add something to your settings.gradle to automate accepting
Plugins are reusable Gradle logic (libraries) that can provide their own tasks
Standard dist includes a bunch of plugins. For these you don't need to specify the version of the plugin, because what Gradle ships with is what it ships with
class MyTask extends DefaultTask {
@TaskAction // <-- the method that's called at Gradle execution time. DefaultTask has only ONE TaskAction method!
void sayHello() {
}
}
- Script
- Project
- Gradle
- Settings
- Task
- Action
Project object provides an .ext property you can add arbitrary stuff to project
These map to a (built in) Gradle build script
- configure environment (init.gradle, gradle.properties)
- find projects and build scripts (settings.gradle too)
Can contain enterprise wide servings ie where to download the enterprise’s plugins
$GRADLE_HOME/init.d/
Init.gradle In this lifecycle the script object has (delegate of Gradle) Settings.gradle In this lifecycle the script object has (delegate of settings)
- evaluate all build scripts (build.gradle)
- this is where the task execution DAG is created
- build object model
Settings.gradle In this lifecycle the script object has (delegate of Project)
Phase where dependencies are resolved
- Execute (subset of) tasks
In this lifecycle the script object has (delegate of Project)
anything in a doFirst
or doLast
block happens HERE.
Backs Gradle’s incremental build functionality
TODO: document me
Sub projects can have dependencies on (sibling projects for example)
Can configure a project from any other project - cross project configuration
if you set something in gradle.properties and use that in your build script this means you could override this on the command line. (Vs having a hard coded value users can't easily change)