Alex Beggs
05/08/2025, 8:56 PMbase
• IncludeBuild second
◦ project a
◦ project b
:second:a
has a task for executing verifyPaparazzi
however I can't call it on Project base
verifyPaparazzi
and subsequently execute Ba's verifyPaparrazi
it complains that base doesn't have the task.
I can call :second:a:verifyPaparrazi
directly. Which means I can setup a task that is dependent on that task in the base
project as suggested here
My question is, how can I do this dynamically without violating the isolated project best practices.
second/settings.gradle.kts
gradle.lifecycle.afterProject {
if (this.name == "second") {
return@afterProject
}
if (this.rootProject.tasks.findByName("verifyPaparazzi") == null) {
this.rootProject.tasks.register("verifyPaparazzi")
}
val task = this.rootProject.tasks.findByName("verifyPaparazzi")
this.tasks.findByName("verifyPaparazzi")?.let {
task!!.dependsOn(it)
}
}
This then puts a verifyPaparazzi
call at the :second:verifyPaparrazi
level instead of :second:a:verifyPaparrazi
and additional projects that might have it. Collectively putting them into one call at the root of the included build second
The next step would be to add a task in the base
project
tasks.register("verifyPaparazzi") {
dependsOn(gradle.includedBuild("second").task(":verifyPaparazzi"))
}
am I thinking about this all wrong, or is there currently no way to add task dependencies dynamically without violating the Isolated Projects?Francisco Prieto
05/09/2025, 2:53 PMproject.tasks.register
, and it does it inside the onVariants
lambda provided by AGP.
One of these tasks has to search for another task by name, access its outputs, and map them as an input. Something like this:
val targetTaskProvider = project.tasks.named("targetTask") // some task created by another plugin
val myTask = project.tasks.register(...) { task ->
task.input.set(targetTaskProvider.flatMap { // do some processing on task outputs }
}
When using project.tasks.named
, if the task I’m looking for has not been registered yet, it will fail to find it. I found that wrapping everything in project.afterEvaluate
will ensure that the task is already there, but it doesn’t seem optimal. Is there any other alternative?yjxf vf
05/10/2025, 1:13 PMproject.getDependencies().registerTransform(
AccessTransform.class,
parameters -> {
parameters.parameters(p -> {
p.getAccessTransformerFiles().from(extension.getAccessTransformerFiles());
});
parameters.getFrom().attribute(
ModAccessTransformExtension.TRANSFORM_ACCESS,
false
).attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, ArtifactTypeDefinition.JAR_TYPE);
parameters.getTo().attribute(
ModAccessTransformExtension.TRANSFORM_ACCESS,
true
).attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, ArtifactTypeDefinition.JAR_TYPE);
}
);
Jonathing
05/13/2025, 7:10 PMit
on actions. The script will compile and the doCall
is intercepted to try and set the property of the current dynamic object. And this is not including types that are `DslObject`s or DynamicObjectAware
. Both method calls and the short-hand for getters and setters don't work.
Basically, this works:
accessTransformers.register {
it.config = project.file('accesstransformer.cfg')
}
and this doesn't:
accessTransformers.register {
config = project.file('accesstransformer.cfg')
}
why is that? and is there anything I can realistically do about this without needing to use something like a domain object container? For reference, this is the method they are calling in this example
default AccessTransformersContainer register(Action<? super AccessTransformersContainer.Options> options) {
return this.register(AccessTransformersExtension.DEFAULT_ATTRIBUTE, options);
}
and this is the exception being thrown:
Caused by: groovy.lang.MissingPropertyException: Could not set unknown property 'config' for project ':at-gradle-demo' of type org.gradle.api.Project.
at org.gradle.internal.metaobject.AbstractDynamicObject.setMissingProperty(AbstractDynamicObject.java:118)
at org.gradle.groovy.scripts.BasicScript$ScriptDynamicObject.setMissingProperty(BasicScript.java:170)
at org.gradle.internal.metaobject.AbstractDynamicObject.setProperty(AbstractDynamicObject.java:76)
at org.gradle.api.internal.project.DefaultDynamicLookupRoutine.setProperty(DefaultDynamicLookupRoutine.java:42)
at org.gradle.groovy.scripts.BasicScript.setProperty(BasicScript.java:74)
at org.gradle.internal.classpath.declarations.GroovyDynamicDispatchInterceptors.callInstrumentedSetProperty(GroovyDynamicDispatchInterceptors.java:102)
at org.gradle.internal.classpath.declarations.GroovyDynamicDispatchInterceptors.intercept_setProperty(GroovyDynamicDispatchInterceptors.java:89)
at build_es5ivf5z9jwb4t9pnjernbno6$_run_closure1.doCall$original([path]/at-gradle-demo/build.gradle:9)
If this is a bug, let me know so I can report it. But for now I'm under the assumption that there's a good reason for this.Giuseppe Barbieri
05/16/2025, 7:41 AMmaven
DSL of the publication task, so in my extension I simply wrote
val maven = objects.newInstance<MavenArtifactRepository>()
fun maven(action: Action<in MavenArtifactRepository>) = action.execute(maven)
but I get:
> Could not create an instance of type Library.
> > Could not create an instance of type Library$Into.
> > Could not create an instance of type org.gradle.api.artifacts.repositories.MavenArtifactRepository.
> > Could not generate a decorated class for type MavenArtifactRepository.
> > Cannot have abstract method AuthenticationSupported.getAuthentication(): AuthenticationContainer.
Is there a way to achieve that?Filip
05/16/2025, 2:21 PMtest
, most android modules have testDebugUnitTest
and the modules with flavors have also the flavor name inside the task name, like testStagingDebugUnitTest
.
Our goal however is be able to run all existing unit tests in a CI environment using a single command, e.g. runAllTest
.
Additionally, since there are different source sets in some of the flavor android modules, we'd like to have an option to configure what tasks in a given module should be executed to cover all source sets.
What would be the recommended way of approaching this topic, that would follow the best practices and work properly with configuration avoidance and configuration cache?
In the thread I'm pasting an initial draft of the solution, which definitely can be improved (it uses afterEvaluate
, which I'm aware is not recommended).
There's a convention plugin to register runAllTest
task, making it dependent on the selected test task name and a custom extension to allow a module define its desired task name.Francisco Prieto
05/26/2025, 10:53 PM@SkipWhenEmpty
for a single file?
I’d like to configure the input of a task in such a way that the task is skipped if:
• The file doesn’t exist.
• The input is set with a provider { null }
.
I think this PR asks for something similar, but I couldn’t make it work with a file collection.Alexey Loubyansky
05/31/2025, 6:38 AMtask.getTaskDependencies().getDependencies(task)
to work when Isolated Projects are enabled. Is there a way or an alternative to that in Isolated Projects mode? Interestingly, while task.getTaskDependencies().getDependencies(task)
return task dependencies, task.getDependsOn()
returns an empty set.
Thanks!Marek
06/02/2025, 6:18 PMext.compileSdk = 23
then subprojects could access it via rootProject.ext.compileSdk
. Now moved the common plugin configurations to convention plugins in an included build. Say in the included build I have now
object Constants {
val compileSdk = 23
}
I use this value in the convention plugins, but for legacy reasons this value is also needed in some other buildscript in the project. I don't want to have it duplicated just for that. I think I am able to read that value directly in *.kts
files but not in .gradle
files. (Why?) I have figured out I can create some kind of ConstantsConventionPlugin
class ConstantsConventionPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.extensions.extraProperties.set("ANDROID_COMPILE_SDK", Constants.compileSdk)
}
}
Can this be avoided? Is there a better alternative?Francois Dabonot (Frankois)
06/07/2025, 7:47 AMFelix de Souza
06/09/2025, 4:44 PM~/<user.home>/.ideaLibSources
which IntelliJ seems to pick up. This does not happen with later versions of gradle for whatever reason. The workaround I’ve been doing is:
• change the wrapper to -all
distribution and refresh
• go to a gradle class and select Choose sources
• Go to ~/<user.home>/.gradle/wrapper/dists/gradle-<version>-all/<some-hash>/gradle-<version>/src
and select that
This obviously doesn’t scale when gradle versions update automatically + many different gradle plugin repositories that I work between.
Trying to find an issue on YouTrack and Gradle’s github issue tracker has proven difficult since “gradle” and “intellij” are such common search terms 😅
has anyone actually figured out what the issue is and got it working again?
I have a rough feeling that:
• https://github.com/gradle/gradle/pull/29320
• https://github.com/gradle/gradle/issues/29483
are somewhat involved, but not sure, hoping someone will know 🙏kyle
06/10/2025, 5:35 PMproject.tasks.register('myCustomTestTask', Test {
TestRetryTaskExtension retry = it.extensions.getByType(TestRetryTaskExtension)
...
}
... but this throws o.g.a.UnknownDomainObjectException: Extension of type 'TestRetryTaskExtension' does not exist.Sergey Chelombitko
06/16/2025, 4:06 PMkotlin-dsl
plugin from a regular Project plugin with the default version?
I tried this way, it requires adding an explicit dependency org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin
which I want to avoid.
class MyConventionPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.pluginManager.apply("org.gradle.kotlin.kotlin-dsl")
}
}
tony
06/28/2025, 8:45 PMKelvin Chung
07/03/2025, 6:35 AMval testResultsElements = configurations.consumable("testResultsElementsForJvmTest") {
// This configuration mimics that defined by the "test-suite-base" plugin
description = "Binary results obtained from running the 'jvmTest' suites."
attributes.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.VERIFICATION))
attributes.attribute(VerificationType.VERIFICATION_TYPE_ATTRIBUTE, objects.named(VerificationType.TEST_RESULTS))
// FIXME how do we accommodate multiple JVM targets and their respective test suites?
attributes.attribute(TestSuiteName.TEST_SUITE_NAME_ATTRIBUTE, objects.named("test"))
val binaryDir = tasks.named<Test>("jvmTest").flatMap { it.binaryResultsDirectory }
outgoing.artifact(binaryDir) {
type = ArtifactTypeDefinition.DIRECTORY_TYPE
}
}
I'm wondering what I would have to change if I had two source sets that produced JVM code, so I would have two "testResults" configurations. And how that would relate to the test result aggregation plugin since I would need to differentiate them in such a way that the aggregation plugin could pick each of them separately.Vampire
07/09/2025, 11:28 PMwith
in the task implemenation?ysb33r
07/13/2025, 7:17 PMterraform test -junit-xml
. What I would like to do is to convert those XML files into HTML reports that look-and-feel the same as the ones generated by Test
tasks.
Does anyone know whether that can be achieved with what we have available in the Gradle API today (7.3+)?Thomas Broyer
07/15/2025, 3:27 PMgetMessage()
lazily using a helper class (from that same third-party lib).
The problem is that, if I don't do anything special, Gradle will somehow "copy" the exception outside of the classLoaderIsolation or back to the Gradle Daemon process, but won't "copy" that helper class, so when Gradle wants to print the error message to the console and calls the exception's getMessage()
method, that one will throw a ClassNotFoundException that will shadow the actual error.
How would you recommend handling this?Thomas Broyer
07/16/2025, 3:09 PMConfigurableFileCollection
properties be initialized with .convention()
or .from()
? What do you think?
(I initially went with .convention()
, but when the conventional value is a bit convoluted to compute –a file in the output of the processResources
task– it's asking too much to the users if they just want to add to the file collection, while on the other hand if initialized with .from()
and they don't want that value they can .setFrom()
to reinitialize it; what would be the use cases for .convention()
then?)Thomas Broyer
07/16/2025, 3:40 PMconvention()
or putAll()
? (my use-case is initializing it with providers.gradlePropertiesPrefixedBy(…)
)
I've found one instance of MapProperty in the Gradle codebase that's not left empty, and it's in the build logic (so not in a "public" plugin), and it uses a few calls to `put()`: https://github.com/gradle/gradle/blob/56ac845808133d19b7e03a3fcc1264f21122e37e/bui[…]/src/main/groovy/gradlebuild/docs/GradleReleaseNotesPlugin.javaMartin
07/17/2025, 12:03 PMMapProperty
questions:
• Is it possible to have null
as a value?
• Having MapProperty<String, Any?>
displays an error in my IDE but compiles fine. Where is the disconnect? How come the KIJP knows that the bound is non nullable?Jerome Haltom
07/17/2025, 2:04 PMAlexey Loubyansky
07/22/2025, 4:24 PMruntime
or runtimeElements
as a base variant but that introduces an ambiguity, which i'm struggling to find a way to avoid. Is there an example how it's supposed to be done?
Here is my playground plugin https://github.com/aloubyansky/playground/blob/gradle-comp-variants/plugin/src/main/java/org/example/PlaygroundPlugin.java
And here is the outcome of running it https://github.com/aloubyansky/playground/tree/gradle-comp-variants.
Thanks!Alexey Loubyansky
07/23/2025, 8:49 PMtestRuntimeClasspath
, resolve it and iterate through the resolved artifacts. But if there are test fixtures, it fails with
> Could not resolve all artifacts for configuration ':app:testRuntimeClasspathCopy'.
> Could not resolve project :app.
Required by:
project :app
> Unable to find a variant with the requested capability: feature 'test-fixtures':
- Variant 'testRuntimeClasspathCopy' provides 'playground:app:unspecified'
It looks like something is missing from the copy of the original configuration. Is this expected? Thanks!
I have a little playground project to reproduce the issue here https://github.com/aloubyansky/playground/tree/gradle-copyRecursive-test-fixtures
Commenting out https://github.com/aloubyansky/playground/blob/gradle-copyRecursive-test-fixtures/plugin/src/main/java/org/example/GreetingTask.java#L21 works.
(I realize referencing a Project
from a task is a bad practice, this is just a reproducer).Jakub Chrzanowski
07/28/2025, 7:38 PM8.14.3
to 9.0.0-rc-4
.
When running integration tests against Gradle 8.x, the plugin fails on java.lang.NoSuchMethodError
and java.lang.NoClassDefFoundError
.
In my code, I rely on sequenceOf()
and yield()
, whose signatures slightly changed in Kotlin 2.0:
sequenceOf()
* Exception is:
java.lang.NoSuchMethodError: 'kotlin.sequences.Sequence kotlin.sequences.SequencesKt.sequenceOf(java.lang.Object)'
at org.jetbrains.intellij.platform.gradle.resolvers.path.ModuleDescriptorsPathResolver.<init>(ModuleDescriptorsPathResolver.kt:19)
at Build_gradle.<init>(build.gradle.kts:53)
yield()
* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':signPlugin'.
....
Caused by: java.lang.NoClassDefFoundError: kotlin/coroutines/jvm/internal/SpillingKt
at org.jetbrains.intellij.platform.gradle.tasks.SignPluginTask$arguments$1.invokeSuspend(SignPluginTask.kt:208)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
My first though was to build the Gradle plugin targeting Kotlin 1.8 (we support Gradle 8.5+) using:
kotlin {
jvmToolchain(17)
compilerOptions {
apiVersion = KotlinVersion.KOTLIN_1_8
languageVersion = KotlinVersion.KOTLIN_1_8
}
}
but this didn't really help. Any ideas on how to deal with that?Kelvin Chung
07/28/2025, 9:03 PMRepositoryHandler
so that it has more conventional NamedDomainObjectContainer
semantics? It just seems like with the way Gradle has evolved over the years, something like
repositories {
maven(url) { name = "foobar" }
}
would be a bit clunky compared to
repositories.register<MavenArtifactRepository>("foobar") {
url.set(...)
}
Of course, I recognize that any effort to go in that direction would be nontrivial, since the API appears to not have a thing that is both "ordered" and "an extensible container", but one can dream, right?efemoney
07/31/2025, 9:53 AMSteve Ebersole
07/31/2025, 6:01 PMpublic abstract class HibernateOrmSpec {
...
@Optional
abstract public Property<EnhancementSpec> getEnhancement();
public void enhancement(Action<EnhancementSpec> action) {
EnhancementSpec spec = getObjectFactory().newInstance( EnhancementSpec.class );
action.execute( spec );
getEnhancement().set( spec );
}
}
abstract public class EnhancementSpec {
abstract public Property<Boolean> getEnableLazyInitialization();
...
}
Later, I try to see if the user specified this "enhancement" extension : if ( !getEnhancement().isPresent() ) ...
The odd part is this...
This works as expected:
hibernate {
enhancement {
}
}
However, this does not:
hibernate {
enhancement
}
Now I can easily accept that this second form is "not valid". But the problem I am having is actually being able to recognize this situation to properly handle it (whether that be treat it as the first form, throw an exception, etc.). The problem is that, as far as I can tell, Gradle just ignores it (it being the "enhancement" token). Its not added to ext properties or anything like that.
Any idea what Gradle actually does with that?Jerome Haltom
07/31/2025, 7:40 PMysb33r
08/08/2025, 3:26 PM-i
affect a task's class path?
That is the headline question, let me explain. I was testing up-to-date status of some tasks in plugin using testKit. Effectively, what I was doing in the test is something like
GradleRunner.create().withArguments('task1')
GradleRunner.create().withArguments('task2')
basically task2
depend on task1
which depends on task0
Thus in the first invocation task0
and task1
will have an outcome of SUCCESS
.
In the 2nd invocation, those two should have an outcome of UP_TO_DATE
, and task2
will be SUCCESS
. Not problem with that, until I did...
GradleRunner.create().withArguments('task1')
GradleRunner.create().withArguments('task2', '-i')
Suddenly, on the 2nd invocation the outcome of task0
was SUCCESS
instead of UP_TO_DATE
and Gradle logs stated that
Task ':task0' is not up-to-date because:
Class path of task ':task0' has changed from 58046a25460590d169a1847f193c5177 to b72b7af69ca1f7306a1e3a2dcf54a21a.
I find that unexpected behaviour. WDYT?