skip to content

ProviderFactory: Env, Sysprops, External

Reading environment variables, system properties, Gradle properties, and external command output through ProviderFactory rather than directly. Asked because these providers are exactly what makes a build configuration-cache safe.

on this pageshow

questions

6

What is ProviderFactory in Gradle, and how do you obtain it inside a plugin or task?

level: juniorimportance: must knowfreq 55%

answer

  1. service that builds lazy Providers
  2. project.providers or @Inject
  3. environmentVariable / systemProperty / gradleProperty
  4. deferred, config-cache-aware reads
  5. don't hold Project in a task

basics

~10 s

ProviderFactory creates lazy Provider values from sources like environment variables, system properties and Gradle properties. You get it via project.providers, or by @Inject in a task/plugin.

solid answer

~40 s

`ProviderFactory` is the service that produces lazy `Provider<T>` values from external inputs — environment variables (`providers.environmentVariable("X")`), system properties (`providers.systemProperty`), Gradle properties (`providers.gradleProperty`), and external processes (`providers.exec`, `providers.of(ValueSource)`). Each call returns a `Provider` that is only read when queried (e.g. `.get()`), so values stay lazy and deferred to execution time. You access it through `project.providers` in build scripts/plugins, or — for objects created by Gradle's instantiator (tasks, extensions, custom services) — by constructor injection: `@Inject constructor(providers: ProviderFactory)`. Using it instead of reading `System.getenv()` directly makes the inputs visible to the configuration cache, so the build re-runs when those inputs change rather than going stale.

code

kotlin · 6 lines
kotlin
abstract class MyTask @Inject constructor(
    private val providers: ProviderFactory
) : DefaultTask() {
    @get:Input
    val region: Provider<String> = providers.environmentVariable("AWS_REGION")
}

go deeper

for a junior

Name it as the source of lazy Providers and mention project.providers plus the env/sysprop/gradleProperty methods.

for a middle

Explain @Inject access in tasks and why laziness matters; mention orElse chaining of sources.

for a senior

Tie it to configuration-cache correctness and not holding Project references in tasks.

for a principal

Frame it as the single sanctioned channel for external inputs so the whole build's input graph is observable and cacheable.

## What ProviderFactory is `ProviderFactory` is a built-in Gradle service whose job is to wrap *external inputs* into lazy `Provider<T>` objects. A `Provider<T>` is a deferred value: it holds *how to compute* a value, not the value itself, and is only resolved when something calls `.get()`, `.getOrNull()`, or `.getOrElse(...)`. Reading inputs through `ProviderFactory` (rather than plain Java/Kotlin APIs like `System.getenv`) is what lets Gradle: - defer reads until they are actually needed (laziness), - track them as build inputs so the **configuration cache** invalidates correctly, - and chain/transform them with `map`/`orElse` without forcing eager evaluation. ## How to obtain it Two idiomatic ways: 1. **`project.providers`** — available anywhere you have a `Project` (build scripts, `Plugin.apply`). 2. **Constructor injection** — for types Gradle instantiates (tasks, extensions, `BuildService`s), annotate the constructor with `@Inject` and accept `ProviderFactory`. This is preferred inside tasks because you should never hold a `Project` reference in a task (it breaks the configuration cache). ## The core factory methods - `environmentVariable(name)` → `Provider<String>` - `systemProperty(name)` → `Provider<String>` - `gradleProperty(name)` → `Provider<String>` (reads `gradle.properties` / `-P`) - `provider { ... }` → wrap an arbitrary computation - `exec { ... }` / `of(ValueSource)` → config-cache-safe external/process reads ## Example ```kotlin abstract class PublishTask @Inject constructor( providers: ProviderFactory ) : DefaultTask() { @get:Input val token: Provider<String> = providers.environmentVariable("PUBLISH_TOKEN") .orElse(providers.gradleProperty("publishToken")) } ``` Here the token is read lazily and only at execution, and both sources are recorded as configuration-cache inputs.

  • Why inject ProviderFactory into a task instead of using project.providers?
    A task must not keep a reference to Project at execution time (it breaks the configuration cache). Injecting ProviderFactory gives the same capability without capturing Project.
  • When is the value of providers.environmentVariable actually read?
    Lazily — only when the Provider is queried with get()/getOrElse(). The factory just records intent until then.

saying these in an interview costs you the question

  • Saying ProviderFactory eagerly returns the current value
  • Claiming you should reach for project.providers from inside a task's execution

context

open as a page

Why should you read environment variables through providers.environmentVariable instead of System.getenv() in a Gradle build?

level: middleimportance: must knowfreq 60%

basics

~10 s

providers.environmentVariable returns a lazy, config-cache-tracked Provider. System.getenv() is read eagerly at configuration time and invisible to the configuration cache, so the build can go stale.

open as a page

What is the difference between providers.gradleProperty(name) and project.findProperty(name) / project.property(name)?

level: middleimportance: should knowfreq 40%

basics

~10 s

gradleProperty returns a lazy, config-cache-tracked Provider<String> from gradle.properties and -P flags. findProperty/property read eagerly and also see project ext/extra properties, not just Gradle properties.

open as a page

How do you run an external process during configuration in a config-cache-safe way using ProviderFactory?

level: seniorimportance: should knowfreq 45%

basics

~10 s

Use providers.exec { commandLine(...) } which returns an ExecOutput whose standardOutput is a Provider. It is lazy and tracked as a configuration-cache input, unlike project.exec.

open as a page

What pitfalls arise when wiring ProviderFactory values into task inputs, and how do you keep them lazy and config-cache-safe?

level: seniorimportance: should knowfreq 30%

basics

~10 s

Don't call .get() at configuration time, don't capture Project in task actions, and wire Providers into Property inputs with .set(provider) so resolution stays deferred and tracked.

open as a page

What is a ValueSource and when would you use providers.of(...) over providers.environmentVariable or providers.exec?

level: seniorimportance: should knowfreq 35%

basics

~10 s

A ValueSource is a reusable, parameterized unit of external reading. You implement obtain() and call providers.of(MySource) { parameters... } to get a lazy, config-cache-safe Provider for any custom external input.

open as a page