Gradle properties allow you to define and configure settings that can be used across your build scripts. These properties can be defined globally for the entire project or locally within tasks. They are commonly used to store configuration values, such as version numbers, file paths, and custom task settings.
Gradle Properties:
Project Properties (gradle.properties)
Project Properties
Project Properties are global to the project and can be defined:
In the gradle.properties file
OR passed via the command line.
Command line properties overridegradle.properties.
Accessed in build scripts using project.propertyName.
To pass project properties via the command line in Gradle, you can use the -P flag followed by the property name and value. This allows you to override or define properties at runtime without modifying the gradle.properties file.
Example of Passing a Project Property via the Command Line:
You can pass the version property like this:
Glass thought Sandbox Snapshot
Command to reproduce:
gt.sandbox.checkout.commit d043a3a \&& cd "${GT_SANDBOX_REPO}" \&& cmd.run.announce "./gradlew go -Pversion=9.9"
Gradle allows you to define task-specific properties that can be configured dynamically. These properties can be set and modified during the Configuration phase and accessed during the Execution phase.
Example of Task Property:
tasks.register("myTask") { // Define a custom task property val greeting = project.objects.property(String::class.java) // Configure the property during the Configuration phase greeting.set("Hello, Gradle!") doLast { // Access and use the property during the Execution phase println(greeting.get()) }}
Glass thought Sandbox Snapshot
Command to reproduce:
gt.sandbox.checkout.commit 4beb72b \&& cd "${GT_SANDBOX_REPO}" \&& cmd.run.announce "./gradlew myTask"
Task property greeting is defined and configured during the Configuration phase (greeting.set()).
The property value is accessed and used during the Execution phase (greeting.get()).
Why use task properties and not variables
Why use task properties and not variables
Why Not Just Use Variables
The use of task properties in Gradle, like project.objects.property(String::class.java), provides important advantages over using regular variables within a task block. Here are the key reasons to use task properties instead of simple variables:
1. Gradle’s Incremental Build Support:
Task properties allow Gradle to track inputs and outputs, enabling incremental builds. This means that if the property value hasn’t changed, Gradle can avoid re-executing the task, improving build performance.
Regular variables like someVariable are not tracked by Gradle, so changes to these won’t trigger task re-execution or up-to-date checks.
2. Deferred Configuration:
Task properties are part of Gradle’s configuration system and can be configured after the task is defined (even from the command line or other tasks). This makes them more flexible, allowing dynamic behavior depending on the project state.
Regular variables are evaluated immediately when the task is configured, and their values are fixed during the configuration phase.
3. Lazy Evaluation:
With task properties, you can benefit from lazy evaluation, meaning the property values are only evaluated when accessed in the execution phase. This can help with more complex tasks where you want values to be set dynamically based on other conditions in the build.
In contrast, regular variables are eagerly evaluated when the task is configured.
4. Task Serialization:
When you define task properties, Gradle can serialize these properties, which is essential when running builds in parallel or in distributed build environments (like using Gradle’s build cache).
Regular variables aren’t part of Gradle’s internal task model, so they can’t be tracked, serialized, or cached.
Example Comparison:
Using a Task Property:
tasks.register("myTask") { // Define a custom task property val greeting = project.objects.property(String::class.java) // Configure the property during the Configuration phase greeting.set("Hello, Gradle!") doLast { println(greeting.get()) }}
Advantages: greeting is tracked by Gradle, can be lazily configured, and can enable incremental build support.
Disadvantages: greeting is not tracked by Gradle, so if you change its value or input/output files, Gradle won’t know and can’t optimize the build. It’s also evaluated immediately during configuration.
5. External Configuration:
Task properties can be set or overridden via external configurations like the command line or other build scripts, adding to their flexibility.
Regular variables are static and cannot be reconfigured dynamically from outside the task definition.
When to Use Task Properties:
When tasks need incremental build support, such as for tasks that depend on external input or outputs.
When the task properties need to be configured dynamically, for example, based on user input, environment variables, or project states.
For tasks running in parallel or in distributed environments, where Gradle needs to serialize and track task state.
Conclusion:
While you can use regular variables for simple cases, task properties offer significant advantages in terms of incremental builds, lazy evaluation, external configuration, and task state tracking, making them crucial for complex, efficient Gradle builds.
These properties are not tracked by gradle and can cause tasks that should be-reran to be skipped when gradle runs its up-to-date-checks-task-avoidance:
System Properties
System Property (in Gradle)
System Properties:
System properties can be defined at runtime by passing them as JVM arguments. They can be accessed using System.getProperty(). Note: System Properties are not-tracked-by-gradle .
Example:
gradle build -DmyProperty=value
Access in build script:
val myProperty = System.getProperty("myProperty")println("System property: $myProperty")
Correct, system properties are not tracked by Gradle by default. Just like environment variables, changes to system properties won’t automatically trigger Gradle tasks to be re-executed, and they aren’t included in Gradle’s up-to-date checking or incremental build system.
Why System Properties Aren’t Tracked:
System properties are typically set outside of the Gradle build process (e.g., through JVM arguments or command line options like -DmyProperty=value), so Gradle does not track them as part of its normal task input/output model.
If a task depends on system properties, but those properties change between builds, Gradle won’t automatically detect this and re-execute the task unless you explicitly tell Gradle to treat those properties as inputs.
Workaround: Manually Declare System Properties as Task Inputs
To ensure Gradle considers system properties in its up-to-date checks and re-executes tasks when system properties change, you can declare them as task inputs manually, similar to environment variables.
Example: Tracking a System Property as a Task Input
tasks.register("printSysProp") { val sysProp = System.getProperty("myProperty") // Declare the system property as an input inputs.property("myProperty", sysProp) outputs.file("build/output.txt") // Output file doLast { val outputFile = file("build/output.txt") outputFile.writeText("System property: $sysProp") println("doLast is running... System property: $sysProp") }}
Glass thought Sandbox Snapshot
Command to reproduce:
gt.sandbox.checkout.commit 05d2b11 \&& cd "${GT_SANDBOX_REPO}" \&& cmd.run.announce "gs"
Recorded output of command:
On branch sandbox-commit-d7ac1d1Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: lib/build.gradle.ktsno changes added to commit (use "git add" and/or "git commit -a")
In this example:
The system property myProperty is declared as a task input using inputs.property().
Gradle will now track this property, and if it changes, the task will be re-executed.
How to Pass a System Property via Command Line:
./gradlew printSysProp -DmyProperty=someValue
Key Points:
System properties are not tracked by default, meaning they won’t trigger task re-execution if they change.
To ensure a task is re-executed when a system property changes, you need to manually declare the system property as a task input using inputs.property().
Once declared as a task input, Gradle will include the system property in its up-to-date checks.
Conclusion:
Just like environment variables, system properties are not automatically tracked by Gradle. However, you can manually declare system properties as task inputs if they are critical to the task’s behavior and you want Gradle to re-run the task when they change.
These are properties defined outside of Gradle (in the environment) and can be accessed using System.getenv(). Note: Environment Variables are not-tracked-by-gradle .
Example:
export MY_ENV_VAR="some_value"gradle build
Access in build script:
val envVar = System.getenv("MY_ENV_VAR")println("Environment variable: $envVar")
Environment variables are NOT automatically tracked by Gradle. This means that changes to environment variables won’t trigger tasks to be re-executed, and they won’t be considered in Gradle’s up-to-date checking or incremental build system.
Why Environment Variables Aren’t Tracked:
Gradle’s incremental build support is based on its ability to track task inputs and outputs, such as files or task properties explicitly defined within the build script.
Environment variables exist outside of Gradle’s scope, so it doesn’t automatically know about changes to them. As a result, tasks depending on environment variables can still be considered up-to-date even if the environment variables change.
Example: Environment Variable is not Tracked
Glass thought Sandbox Snapshot & Explanation
In this example we will run the following shell, which will vary the environment variable MY_ENV_VAR between two values and run the task saveEnvVarToFile which writes the environment variable to a file.
This is the gradle task that writes the environment variable to a file:
tasks.register("saveEnvVarToFile") { val envVar = System.getenv("MY_ENV_VAR") // Without declaring the property as input and relying on env variable. // inputs.property("MY_ENV_VAR", envVar) outputs.file("build/output.txt") // Output file doLast { val outputFile = file("build/output.txt") outputFile.writeText("Environment property: $envVar \n") println("Environment variable: $envVar") }}
Notice that we have explicitly commented out the inputs.property("MY_ENV_VAR", envVar) line, which would have declared the environment variable as a task input. Now when we run the 2nd run even though the environment variable has changed, the task is still considered up-to-date.
Workaround: Manually Declare Environment Variables as Inputs
If you want Gradle to consider environment variables when determining whether a task is up-to-date, you can declare them as task inputs. This way, Gradle will re-execute the task when the environment variable changes.
Example: Tracking an Environment Variable as a Task Input
tasks.register("saveEnvVarToFile") { val envVar = System.getenv("MY_ENV_VAR") // Declare the environment variable as input: inputs.property("MY_ENV_VAR", envVar) outputs.file("build/output.txt") // Output file doLast { val outputFile = file("build/output.txt") outputFile.writeText("Environment variable saved in file: $envVar \n") println("Environment variable: $envVar") }}
> Task :lib:saveEnvVarToFileEnvironment variable: HI4BUILD SUCCESSFUL in 350ms1 actionable task: 1 executedEnvironment variable saved in file: HI4 > Task :lib:saveEnvVarToFileEnvironment variable: HI5BUILD SUCCESSFUL in 342ms1 actionable task: 1 executedEnvironment variable saved in file: HI5
Gotcha
The gotcha with this approach is that we need to know which tasks depend on which environment variables and declare them as inputs manually.
Key Points:
Environment variables are not tracked by default, so changes to them won’t trigger task re-execution.
To ensure a task re-executes when an environment variable changes, declare the environment variable as a task input using inputs.property().
By declaring it as an input, Gradle will treat the environment variable as part of the task’s state and include it in the up-to-date checks.
Conclusion:
While Gradle doesn’t automatically track environment variables, you can make Gradle aware of them by manually declaring them as task inputs. This ensures that tasks depending on environment variables will re-run when those variables change.