Build Systems
Build Systems
Build
Systems
Build system – Software that automates the process of getting some kind of an artifact
(executable, library) from the source code. Build systems can be used for:
● Configuring your build once and using it forever (copy-paste into new projects)
● Unifying builds and reusing logic in various projects
● Dependencies management*
● Testing and verification
● Incremental builds*
How?
Maven
pom.xml
Declarative: You define the configuration without specifying how to achieve it.
Convention: You describe what you need with specific rules.
Lifecycle: It can support everything from compilation to tests and so on.
Plugins allow you to do the unconventional heavy-lifting.
Coordinates are located in pom.xml: groupId, artifactId, version.
Repositories: You can load (and cache) the dependencies on demand.
Learn more: search.maven.org (Maven Central)
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Gradle
build.gradle
gradl settings.gradle
e
● Gradle root project might have subprojects that have subprojects and so on
dependencies {
implementation(kotlin("stdlib"))
}
tasks {
withType<JavaCompile> {
targetCompatibility = "11"
}
}
Gradle repositories
Specify where to find the libraries needed by the project. The search is carried out from top to
bottom
repositories {
mavenCentral()
google()
maven {
url = uri("https://your.company.com/maven")
credentials {
Don’t push the credentials to GitHub,
username = "admin"
password = "12345"
please!
} Use secrets, environmental variables, etc.
}
flatDir {
dirs("libraries")
}
}
Gradle dependencies
● testCompilationOnly
● testRuntimeOnly
● testImplementation
● testApi
Gradle dependencies
val ktorVersion: String = "6.6.6"
dependencies {
// string notation, e.g. group:name:version
implementation("commons-lang:commons-lang:2.6")
implementation("io.ktor:ktor-serialization-jackson:$ktorVersion")
// map notation:
implementation("org.jetbrains.kotlinx", "kotlinx-datetime", "7.7.7")
// dependency on another project
implementation(project(":neighborProject"))
// putting all jars from 'libs' onto the compile classpath
implementation(fileTree("libs"))
// api dependency – internals are accessible
api("io.ktor:ktor-server-content-negotiation:$ktorVersion")
// test dependencies
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
testImplementation(kotlin("test"))
}
Gradle dependencies
dependencies {
implementation("org.hibernate:hibernate") {
version {
// If there is a version conflict, strictly select version "3.1" of hibernate
strictly("3.1")
}
exclude(module = "cglib") // by artifact name
exclude(group = "org.jmock") // by group
exclude(group = "org.unwanted", module = "buggyModule") // by both
// disabling all transitive dependencies of this dependency
isTransitive = false
}
}
BOM
There are direct and transitive dependencies, which may lead to version conflicts.
myProject -> thing:1.0 -> anotherThing:1.1
myProject -> thirdThing:1.0 -> anotherThing:1.2
dependencies {
implementation(enforcedPlatform("io.ktor:ktor-bom:$ktorVersion"))
implementation(enforcedPlatform("io.ktor:ktor-server-core"))
implementation(enforcedPlatform("io.ktor:ktor-server-netty"))
}
Gradle tasks
Partial task graph for a
standard Java build
A task is a set of instructions for Gradle to perform:
compileJav processResource
● Compile the source code a s
● Run tests
● Build a JAR
classes
● Publish to Maven (or somewhere else)
● Etc.
test jar
// build.gradle.kts – buildfile
tasks.create<Copy>("copy") {
description = "Copies sources to the destination directory"
group = "Custom"
from("src")
into("dst")
}
Older versions of Gradle only support the create(…) API, which eagerly creates and
configures tasks when it is called and should be avoided. You can use register(...) instead.
Gradle tasks
Kotlin (and Groovy) are actually too powerful to be build configuration languages.
@TaskAction
fun execute() {
if (n.get() < 0) {
throw StopExecutionException("n must be non-negative")
}
var first = 0
var second = 1
for (i in 1..n.get()) {
second += first
first = second - first
} tasks.register<FibonacciTask>("Fib_11") {
println("Result = $first") n.set(11)
} }
}
Gradle tasks
tasks.withType<Test> {
dependsOn(tasks.withType<PublishToMavenLocal>{}, "jar_name")
}
Plugin – A set of tasks that help deal with something specific: Kotlin, Java, Protobuf, etc.
Plugins block is compiled separately before everything else. Artifacts are placed in the
classpath, which allows the IDE to provide auto-completion and other useful features.
`apply false` is useful when you need the plugin for some subprojects.
plugins {
kotlin("jvm") version "1.7.10"
application // A plugin that runs a project as a Java application
id("org.jlleitschuh.gradle.ktlint") version "10.3.0" apply false
}
Gradle plugins
// projectRoot/build.gradle.kts
class SamplePlugin : Plugin<Project> {
override fun apply(target: Project) {
target.tasks.register("pluginTask") {
doLast { println("A plugin task was called") }
}
}
}
apply<SamplePlugin>()
Gradle plugins
// projectRoot/buildSrc/build.gradle.kts // projectRoot/buildSrc/src/main/kotlin/SamplePlugin.kt
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.gradle.api.XXX
// projectRoot/buildSrc/src/main/kotlin/SamplePlugin.kt [CONTINUED]
Properties are used to configure the behavior of Gradle itself and specific projects.
From highest to lowest precedence:
● Environment variables
// gradle.properties
kotlin.code.style=official
username=student
// build.gradle.kts
val username: String by project
val kotlinCodeStyle = project.property("kotlin.code.style") as String
tasks.register("printProps") {
doLast {
println(username)
println(kotlinCodeStyle)
println(System.getProperty("idea.version"))
}
}
Gradle settings
Before Gradle assembles the projects for a build, it creates a Settings instance and loads the
settings file into it. Only one settings file is stored in the Gradle project, and it is used to:
● modify the parameters from the command line, e.g., add a new project property
// settings.gradle.kts
rootProject.name = "Project’s name"
include(
"Module1",
"Module2"
)
A Gradle wrapper (gradlew) is a shell script that downloads and caches the required version of
Gradle.
● gradlew – used in *nix
● gradlew.bat – used in Windows
● Caching
● Multi-module projects
● More blocks:
○ allprojects { } and subprojects { }
○ publishing { }
○ artifacts { }
● Compatibility
● Resolution strategies
● Source sets
Thanks!