skip to content

-P vs -D vs ORG_GRADLE_PROJECT_

The three ways a value reaches a build — -P project properties, -D system properties, and the ORG_GRADLE_PROJECT_ / systemProp. environment conventions. Interviewers ask because the environment form is how CI injects secrets without putting them on a command line.

on this pageshow

questions

5

What is the difference between passing -Pname=value and -Dname=value on the Gradle command line?

level: juniorimportance: must knowfreq 70%

answer

  1. -P = project property
  2. -D = JVM system property
  3. separate namespaces, no crossover
  4. findProperty / System.getProperty
  5. forked test JVM doesn't inherit -D

basics

~10 s

-P sets a Gradle project property (read in the build script as a project property). -D sets a JVM system property (read via System.getProperty). They land in different namespaces.

solid answer

~40 s

`-Pname=value` defines a **project property**, available on the `Project` object and via the providers API; it is Gradle's own concept and is the idiomatic way to pass build inputs like a version override. `-Dname=value` sets a standard **JVM system property** on the Gradle daemon's JVM, retrievable with `System.getProperty("name")` — the same mechanism every Java program uses. So `-P` is build-domain configuration and `-D` is JVM-level configuration. A subtle trap: a project property named `foo` and a system property named `foo` are unrelated; reading one will not see the other. Tasks like tests that fork a new JVM do NOT automatically inherit `-D` properties — you must forward them explicitly via `systemProperty(...)` in the `test {}` block.

code

bash · 5 lines
bash
# project property: read via findProperty / providers.gradleProperty
./gradlew build -PappVersion=1.4.0

# JVM system property: read via System.getProperty
./gradlew test -Denv=staging

go deeper

for a junior

State the basic split: -P is a Gradle project property, -D is a JVM system property, read with different accessors.

for a middle

Add that the namespaces never cross and that forked test JVMs need -D properties forwarded explicitly.

for a senior

Discuss when to choose each (build inputs vs JVM/tool config) and the configuration-cache-friendly providers accessors.

for a principal

Frame conventions for a team: standardize on -P for build inputs, document required -D flags, and avoid leaking secrets through command-line properties visible in process listings.

## Two separate namespaces Gradle distinguishes two kinds of named values you can inject from the command line: - **Project properties** (`-P`): a Gradle concept. They are attached to the `Project` and form the input vocabulary the build script speaks. You read them with `project.findProperty("x")`, `project.property("x")`, or the lazy `providers.gradleProperty("x")`. - **System properties** (`-D`): the plain JVM mechanism. `-Dfoo=bar` puts `foo` into the system property table of the JVM running Gradle (the daemon). You read it with `System.getProperty("foo")` or `providers.systemProperty("foo")`. The two tables never cross-populate. `-Pfoo=bar` does NOT make `System.getProperty("foo")` return anything, and vice versa. ## Why both exist `-P` is for **your build's own configuration** — overriding a publish version, toggling a feature, choosing an environment. `-D` is for **JVM-level / tool-level configuration** — things a library or the JVM itself reads, e.g. `-Dorg.gradle.jvmargs`, `-Dhttp.proxyHost`, or a system property a plugin documents. ## The forked-JVM gotcha Gradle's own daemon JVM receives `-D` properties, but tasks that fork a new JVM (most importantly `Test`) start a fresh process that does NOT inherit them. To make a system property visible inside tests you forward it: ```kotlin tasks.test { systemProperty("env", System.getProperty("env") ?: "local") } ``` ## Reading a project property defensively `project.property("x")` throws if absent; `findProperty("x")` returns null. The modern, configuration-cache-friendly path is `providers.gradleProperty("x")`, which yields a lazy `Provider<String>`. Knowing which flag feeds which API is the core skill: pick `-P` for build inputs, `-D` for JVM/system-level inputs, and never assume one is visible through the other's accessor.

  • Will System.getProperty("appVersion") return anything after passing -PappVersion=1.4.0?
    No. -P populates Gradle's project-property table only; System.getProperty reads the JVM system-property table, which -P never touches. You'd need -DappVersion=1.4.0 for that.
  • Why might a -D property set on the command line not show up inside a test?
    The Test task forks a new JVM that doesn't inherit the daemon's system properties. You must forward it explicitly with systemProperty(...) in the test block.

-P is a note pinned to the build's own bulletin board; -D is a setting written into the JVM's global config file. Reading one board never shows notes from the other.

saying these in an interview costs you the question

  • Claiming -P and -D are interchangeable.
  • Assuming -D properties are automatically visible inside forked test JVMs.
  • Saying project properties are read with System.getProperty.

context

open as a page

What does the ORG_GRADLE_PROJECT_<name> environment variable do, and when would you use it instead of -P?

level: middleimportance: must knowfreq 60%

basics

~10 s

ORG_GRADLE_PROJECT_name=value sets a Gradle project property named name via an environment variable — equivalent to -Pname=value, but without putting it on the command line. Handy in CI where you inject env vars.

open as a page

How do you set a JVM system property for a Gradle build without using -D on the command line?

level: middleimportance: should knowfreq 45%

basics

~10 s

Put systemProp.name=value in a gradle.properties file. Gradle strips the systemProp. prefix and sets name as a JVM system property — the persistent equivalent of -Dname=value.

open as a page

You need to inject a publish version and a signing secret into a Gradle build from CI. How do you wire each, and why?

level: seniorimportance: should knowfreq 40%

basics

~10 s

Inject both as ORG_GRADLE_PROJECT_ env vars: ORG_GRADLE_PROJECT_version and ORG_GRADLE_PROJECT_signingKey. They become project properties, keep the secret off the command line, and need no -P args.

open as a page

What are common pitfalls with -P values regarding empty/boolean values and reading absent properties?

level: middleimportance: nice to knowfreq 30%

basics

~10 s

-P values are always strings. -Pflag with no =value yields an empty string, not a boolean true. Reading an absent property with project.property() throws; findProperty() returns null.

open as a page