What is the difference between passing -Pname=value and -Dname=value on the Gradle command line?
answer
- -P = project property, -D = JVM system property
- project.property vs System.getProperty
- gradle.properties: bare key vs systemProp.
- providers.gradleProperty / providers.systemProperty
- -D not auto-propagated to forked JVMs
basics
~10 s-P sets a Gradle project property, read via project.property("name"). -D sets a JVM system property, read via System.getProperty("name"). -P is Gradle-specific; -D is standard JVM.
solid answer
~40 s`-Pname=value` defines a **project property** on the Gradle `Project` object. You read it in the build script with `project.property("name")`, `findProperty("name")`, or the `hasProperty`/`providers.gradleProperty` APIs. It is purely a Gradle concept. `-Dname=value` sets a **JVM system property** on the Gradle daemon's JVM (the standard `java -D` mechanism). You read it with `System.getProperty("name")` or `providers.systemProperty("name")`. It also reaches forked processes only if you propagate it. The practical distinction: `-P` is for parameterizing the build itself (toggling a flavor, passing a version), while `-D` is for JVM/tooling configuration and properties consumed by code (including `org.gradle.*` daemon flags). Both can also be set in `gradle.properties` — keys there with no prefix become project properties, and `systemProp.foo=bar` becomes the system property `foo`.
code
kotlin · 10 lines// build.gradle.kts
// gradle build -PreleaseType=rc -DbuildId=42
val releaseType = providers.gradleProperty("releaseType").getOrElse("snapshot")
val buildId = providers.systemProperty("buildId").getOrElse("local")
tasks.register("info") {
doLast {
println("releaseType=$releaseType buildId=$buildId")
}
}go deeper
State the two read APIs correctly: -P -> project.property, -D -> System.getProperty. That alone is a passing answer.
Map both to gradle.properties (bare key vs systemProp.) and mention the lazy providers.* APIs.
Discuss configuration-cache implications and that -D is not auto-propagated to forked JVMs/test workers.
Frame a team convention: -P for build parameterization, -D reserved for JVM/daemon tuning; standardize via gradle.properties to keep CLI invocations reproducible.
## Two different namespaces Gradle exposes two distinct ways to pass key/value data on the command line, and they land in **two different places**. ### `-P` — project properties `-Pname=value` (or `--project-prop name=value`) sets a property on the Gradle `Project` model. These are the natural way to parameterize a build. You read them with: - `project.property("name")` — throws if missing - `project.findProperty("name")` — returns `null` if missing - `project.hasProperty("name")` - `providers.gradleProperty("name")` — the configuration-cache-safe, lazy `Provider<String>` API (preferred in modern Gradle) Project properties can also be declared in a `gradle.properties` file with a bare key: ```properties myFlag=true ``` ### `-D` — JVM system properties `-Dname=value` sets an ordinary **JVM system property** on the Gradle daemon process — exactly like `java -Dname=value`. You read them with `System.getProperty("name")` or, lazily, `providers.systemProperty("name")`. In `gradle.properties` the same thing is written with the `systemProp.` prefix: ```properties systemProp.name=value ``` ### Why both exist `-P` is Gradle's own configuration channel — it never collides with the JVM. `-D` is the universal JVM channel, used both by Gradle internals (e.g. `-Dorg.gradle.*` flags configure the daemon) and by any library that reads system properties. Because `-D` properties live on the daemon JVM, they are **not** automatically visible to forked JVMs (test workers, `JavaExec`); you must propagate them explicitly via `systemProperty(...)` on the task. ### Mapping summary | Source | Project property | System property | |---|---|---| | CLI | `-Pkey=value` | `-Dkey=value` | | gradle.properties | `key=value` | `systemProp.key=value` | | read API | `project.property` / `providers.gradleProperty` | `System.getProperty` / `providers.systemProperty` | ### Lazy access matters Under the configuration cache, calling `System.getProperty` or `project.property` directly at execution time can be flagged as an undeclared input. The `providers.gradleProperty`/`providers.systemProperty` APIs declare the value as a tracked build input, so the cache invalidates correctly when it changes.
- Where do you put the equivalent of -Pfoo=bar and -Dfoo=bar in gradle.properties?`foo=bar` (bare key) for the project property, and `systemProp.foo=bar` for the system property.
- Why might a -D property not show up inside your test code?Tests run in a forked JVM. The -D only set the daemon's JVM; you must propagate it with `tasks.test { systemProperty("foo", ...) }` (or `systemProperties(...)`).
saying these in an interview costs you the question
- Claiming -P and -D are interchangeable aliases for the same thing.
- Saying -D project properties are read with project.property (they are system properties).