Can I call the Gradle properties task on all projects at once in a multi-project build?

1.3k views Asked by At

I would like to view the properties of a Gradle project, to manually ensure the values look right.

When I call the properties task from the root of a multi-project build, it lists the properties of the root project:

$ gradle -q properties

------------------------------------------------------------
Root project
------------------------------------------------------------

allprojects: [root project 'myapp', project ':api', project ':model', project ':ui']
ant: org.gradle.api.internal.project.DefaultAntBuilder@12345
antBuilderFactory: org.gradle.api.internal.project.DefaultAntBuilderFactory@12345
artifacts: org.gradle.api.internal.artifacts.dsl.DefaultArtifactHandler_Decorated@12345
asDynamicObject: DynamicObject for root project 'myapp'
baseClassLoaderScope: org.gradle.api.internal.initialization.DefaultClassLoaderScope@12345
[...]

I can also request the properties for each individual subproject:

$ gradle -q :api:properties

------------------------------------------------------------
Project :api - The shared API for the application
------------------------------------------------------------

allprojects: [project ':api']
ant: org.gradle.api.internal.project.DefaultAntBuilder@12345
antBuilderFactory: org.gradle.api.internal.project.DefaultAntBuilderFactory@12345
artifacts: org.gradle.api.internal.artifacts.dsl.DefaultArtifactHandler_Decorated@12345
asDynamicObject: DynamicObject for project ':api'
baseClassLoaderScope: org.gradle.api.internal.initialization.DefaultClassLoaderScope@12345
[...]

However, what I really want to do is list the properties for a given project and all its subprojects at once.

I'm a bit surprised that calling the task at the root level only gives the root level properties. This seems to contradict the Gradle documentation about executing tasks by name in a multi-project build:

The command gradle test will execute the test task in any subprojects, relative to the current working directory, that have that task. If you run the command from the root project directory, you’ll run test in api, shared, services:shared and services:webservice. If you run the command from the services project directory, you’ll only execute the task in services:shared and services:webservice.

The basic rule behind Gradle’s behavior is: execute all tasks down the hierarchy which have this name. Only complain if there is no such task found in any of the subprojects traversed.

How do I list all properties for a project and its subprojects at once? And as a bonus, why isn't the properties task run at the root level also running for the sub-projects? I am using the currently latest version of Gradle (6.7.1).

2

There are 2 answers

0
M. Justin On BEST ANSWER

Per this Gradle Forum response, the properties task is not running for subprojects since the impliesSubProjects property of the properties task is true in the property implementation:

The task is configured to ignore subproject tasks by the HelpTasksPlugin using an internal API

[…]

Then TaskNameResolver does not look at subprojects for the task if impliesSubProjects is true.

Experimentation shows that the impliesSubProjects property can be configured in the build file, allowing the task to be run for the parent project and all its children:

properties {
    impliesSubProjects = false
}

Output

$ gradle -q properties

------------------------------------------------------------
Root project
------------------------------------------------------------

allprojects: [root project 'myapp', project ':api', project ':model', project ':ui']
ant: org.gradle.api.internal.project.DefaultAntBuilder@12345
antBuilderFactory: org.gradle.api.internal.project.DefaultAntBuilderFactory@12345
artifacts: org.gradle.api.internal.artifacts.dsl.DefaultArtifactHandler_Decorated@12345
asDynamicObject: DynamicObject for root project 'myapp'
baseClassLoaderScope: org.gradle.api.internal.initialization.DefaultClassLoaderScope@12345
[...]

------------------------------------------------------------
Project :api - The shared API for the application
------------------------------------------------------------

allprojects: [project ':api']
ant: org.gradle.api.internal.project.DefaultAntBuilder@12345
antBuilderFactory: org.gradle.api.internal.project.DefaultAntBuilderFactory@12345
artifacts: org.gradle.api.internal.artifacts.dsl.DefaultArtifactHandler_Decorated@12345
asDynamicObject: DynamicObject for project ':api'
baseClassLoaderScope: org.gradle.api.internal.initialization.DefaultClassLoaderScope@12345
[...]

------------------------------------------------------------
Project :model - Shared object model
------------------------------------------------------------
[...]

Note, however, that the impliesSubProjects property is defined in the internal API (org.gradle.api.internal.AbstractTask and is not part of the public Task API (see Avoid using internal Gradle APIs for more details on internal APIs). Therefore, as mentioned in the linked forum response, this is not intended to be used in builds:

keep in mind that it’s an internal implementation detail not intended to be used by builds and there are no guarantees it will keep working in future Gradle releases.

However, in the case of a one-off property comparison that's not a persistent part of the build, this sort of unintended usage is likely acceptable.

0
kaushik On

May be I understood your question incorrectly. If you want to list all properties including all subproject along with the root project, then you can create a build file as follows:

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

allprojects {
    repositories {
        mavenCentral()
    }

    group = "com.nononsensecode"
    version = "0.0.1"

}

plugins {
    kotlin("jvm") version "1.4.20" apply false
    base
}

configure(subprojects) {
    apply {
        plugin("org.jetbrains.kotlin.jvm")
    }

    configure<JavaPluginExtension> {
        sourceCompatibility = org.gradle.api.JavaVersion.VERSION_1_8
        targetCompatibility = org.gradle.api.JavaVersion.VERSION_1_8
    }

    tasks.withType<KotlinCompile>().configureEach {
        kotlinOptions {
            jvmTarget = "1.8"
            freeCompilerArgs = listOf("-Xjsr305=strict")
        }
    }

    dependencies {
        val implementation by configurations

        implementation(kotlin("stdlib-jdk8"))
        implementation(kotlin("reflect"))
    }

    task("listAllProperties") {
        val subProjectPropertiesMap = mutableMapOf<String, Map.Entry<String, Any?>>()
        subprojects.forEach { subProject ->
            subProject.properties.forEach { property ->
                subProjectPropertiesMap[subProject.name] = property
            }
        }

        val allProperties = AllProperties(properties, subProjectPropertiesMap)
        println(allProperties)
    }
}

data class AllProperties(
    val properties: Map<String, Any?>,
    val subProjectProperties: MutableMap<String, Map.Entry<String, Any?>>
)

Then run the task gradlew listAllProperties. If you want, you can create a toString method to print all properties in a beautiful way.