Skip to content

Latest commit

 

History

History
443 lines (271 loc) · 15.5 KB

learning_gradle.md

File metadata and controls

443 lines (271 loc) · 15.5 KB
path title
/learnings/gradle
Learnings: Gradle

Table Of Contents

About Gradle

frequent releases: every 6-8 weeks

Using Gradle

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)

Gradle Wrapper

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

Neat command line flags

--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)

Files

  • 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

Dependencies

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

Dependency management

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

and transitive dependency management

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

See also

  • Gradle_Transitive_Deps_And_Implementation_Deps

and a version catalog

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.

Gotchas

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

See also

Configurations

configurations are also a way to label (and group) dependencies. Configuration types may be provided by plugins.

Tasks

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 Sample

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

Notes about using doFirst and doLast

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.

Source

Displaying tasks in gradle task

<<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'
}

Baeldug: gradle custom task

in Kotlin mode

setting properties of Tasks

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 Interacting with Java

<<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).

operability aspects

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.

Java specific configuration types and transitioning thereof

<<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

See also:

<<Gradle_Interact_With_Maven>>

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

Pull dependancies out of pom.xml and dynamically add them to the gradle build

Source

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
        }
    }
}

Making your own Gradle plugins (and or buildSrc)

And Kotlin version issues

<<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:

Build Scan

build + --scan

will be prompted, you can add something to your settings.gradle to automate accepting

Plugins

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

https://plugins.gradle.org

Writing your own

Task plugin

class MyTask extends DefaultTask {
  @TaskAction  // <-- the method that's called at Gradle execution time. DefaultTask has only ONE TaskAction method!
  void sayHello() {

  }

}

Gradle Object mode

  • Script
  • Project
  • Gradle
  • Settings
  • Task
  • Action

Project object provides an .ext property you can add arbitrary stuff to project

See also

Lifecycle

These map to a (built in) Gradle build script

Initialization:

  • configure environment (init.gradle, gradle.properties)
  • find projects and build scripts (settings.gradle too)

init.gradle and other init scripts

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)

Configuration

  • 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

Execution

  • Execute (subset of) tasks

In this lifecycle the script object has (delegate of Project)

anything in a doFirst or doLast block happens HERE.

See also

Build Cache

Backs Gradle’s incremental build functionality

TODO: document me

Multi project builds with Gradle

Sub projects can have dependencies on (sibling projects for example)

Can configure a project from any other project - cross project configuration

Misc workflow / developer experience tips for people using your Gradle build scripts

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)