Hi, another question. I've read about <https://doc...
# configuration-cache
l
Hi, another question. I've read about https://docs.gradle.org/current/userguide/configuration_cache_requirements.html#dependency_management_types but having a hard time to figure out what is allowed and what not and how to move forward to a CC compatible solution, is e.g. ResolvedDependency allowed? I have this task at the moment:
Copy code
project.tasks.register("checkDependencies") {
    val configurationsToCheck = mutableSetOf<Configuration>()
    listOf("compileClasspath", extension.configuration.get()).forEach { configToCheck: String ->
        configurationsToCheck += project.configurations[configToCheck]
    }

    doLast {
        val snapshotDeps = mutableListOf<String>()

        val resolvedDependencies = mutableSetOf<ResolvedDependency>()
        configurationsToCheck.forEach { configToCheck: Configuration ->
            configToCheck
                .resolvedConfiguration.firstLevelModuleDependencies
                .forEach { resolvedDependencies += it }
        }
        val sortedValues = resolvedDependencies.toMutableList()
        sortedValues.sortWith(compareBy({ it.moduleGroup }, { it.moduleName }))
        sortedValues.forEach { dep: ResolvedDependency ->
            if (dep.moduleGroup.startsWith("com.example")) {
                // body work
                Logging.getLogger("com.example").quiet("Found $dep")
            }
        }

        if (!snapshotDeps.isEmpty()) {
            throw GradleException("Found snapshot dependency: ${snapshotDeps.joinToString(separator = "\n")}")
        }
    }
}
Which does look over the 1st level declared deps on the given configurations and searches for patterns which I don't want there - this fails with CC enabled and Gradle 9. Anyone have some pointers (to the docs) / ideas how to get those info / task / work done in a CC compatible way? Thanks.
j
My rule of thumb is to only either use the rules listed in the documentation or a type that is explicitly marked with Serializable.
In my experience, ResolvedDependency is off the table. My solution is to create a custom type (like a record) that only contains the information I need to work with.
v
l
Sounds promising, I will read / have a look there, thx.
I've tried this now which seems to work and does what I want - anything obvious what I should not use or do?
Copy code
val configurationsToCheck = mutableSetOf<Configuration>()
    listOf("compileClasspath", extension.configuration.get()).forEach { configToCheck: String ->
        configurationsToCheck += project.configurations[configToCheck]
    }
    val preRelease = providers.gradleProperty("preRelease").orElse("")
    val snapshotDeps = mutableListOf<String>()
    
    val ids = mutableListOf<ComponentArtifactIdentifier>()

    configurationsToCheck.forEach { conf ->
        val artifacts: Provider<Set<ResolvedArtifactResult>> =
            conf.incoming
                .artifactView {
                    componentFilter {
                        when (it) {
                            is ModuleComponentIdentifier ->
                                it.group.startsWith("com.example") && !it.group.startsWith("<http://com.example.org|com.example.org>")

                            else -> false
                        }
                    }
                }.artifacts.resolvedArtifacts

        ids.addAll(
            artifacts.get().map { artifact ->
                artifact.id
            },
        )
    }

    doLast {
        ids.sortBy { it.componentIdentifier.displayName }
        ids.forEach { artifact ->
            Logging.getLogger("com.example").quiet("Checking artifact => $artifact")
            val componentId: ModuleComponentIdentifier = artifact.componentIdentifier as ModuleComponentIdentifier
            if (!Utils.isValidDependency(componentId.version, preRelease.get().isNotBlank())) {
                snapshotDeps += "$artifact"
            }
        }

        if (!snapshotDeps.isEmpty()) {
            throw GradleException("Found snapshot dependency: ${snapshotDeps.joinToString(separator = "\n")}")
        }
    }
v
Doing
get()
on a
Provider
curing configuration phase is practically always a bad idea. That introduces the same race conditions you earned from using
afterEvaluate
.
l
argl, you're right.
I wanted to map that - because I only need the identifier and need to do this in the config phase and thought like here: https://docs.gradle.org/7.5-rc-1/userguide/more_about_tasks.html#:~:text=Given%20the%20ResolvedArtifactRe[…]%20wired%20to%20an%20%40Input%20property but oviously I did that wrong with the
.get()
.
nah, that was a 7.5 link, this is not even there in current - going back to read the api docs 😉
👌 1
I rewrote it like this:
Copy code
val componentProviders = mutableListOf<Provider<List<ComponentArtifactIdentifier>>>()

...
componentProviders.add(artifacts.map { it.map { it.id } })

doLast {
        val ids = mutableListOf<ComponentArtifactIdentifier>()
        componentProviders.forEach {
            ids.addAll(it.get())
        }
        ...
}
That way I retain the provider to the execution phase, would that be ok?
Is it possible to make gradle warn me / fail the build, if I am going to eager evaluate providers in the configuration phase, to catch this?
v
I don't think so. There are valid use-cases to do so, if you really need a provider at execution time. Sometimes it also is ok, for example getting the
provider.gradleProperty
provider is fine, as its value cannot change, at lest as of today. Once you are able to programmatically change Project Properties, it becomes not-ok if it could be avoided.
👍 1