This message was deleted.
# community-support
s
This message was deleted.
c
is it a custom task as in a separate source file that extends from say DefaultTask, etc, versus an in-line task definition? The latter can have a bunch of subtleties that capture the project.
related to this, even with a custom task, its possible that other build script code is using
doLast
or
doFirst
on that task which can be problematic.
m
The aggregate task is an inline task definition but fairly simple, although I'm now realizing it does something a bit weird:
Copy code
target.subprojects.filter {
                    it.isAndroidApp()
                }
This is called in a
tasks.register(..)
block at configuration time, which could be a problem, no?
I can verify we don't have
doLast
or
doFirst
blocks anywhere, we use
dependsOn
everywhere though
c
any use of subprojects is problematic for a few reasons - capturing project instance(s) is one, breaking project isolation is another.
if the intent is to aggregate across projects the idiom for that is to use a shared build service; each project contributes what it needs and you can wrap it all up at the end.
m
We have an effort planned to reduce/stop our usage of
subProjects {...
but that doesn't break the configuration cache AFAIK, right?
c
depends on how it is used. in your example it appears to be bound to task configuration. Other cases, such as looping through subprojects and doing non-task stuff, may be fine for CC (depends on specifics).
v
The CC report should show a stack trace that hopefully helps to identify the issue. Or you could set a breakpoint in
getProject
and then maybe follow where it comes from.
m
Unfortunately whenever I generate a trace, there's a
ConcurrentModificationException: (no message)
line that I can't follow further
c
doesn’t seem related; when the build runs and fails due to CC issue it will have a link to a CC report (local html file), in there should be the deets.
m
So I've generated a ton of reports and frankly they're all confusing. I've been trying to debug this trace:
Copy code
org.gradle.api.InvalidUserCodeException: Execution of task ':keys:compileKotlin' caused invocation of 'Task.project' by task ':compose:components:synthesizeDependenciesDebug' at execution time which is unsupported.
If I run
compileKotlin
by itself, no issues. If I run
synthesizeDependenciesDebug
by itself, no issues. But if I run them together, in aggregate, like so:
Copy code
register("someTask") {
  dependsOn(compileDebugKotlin)
  dependsOn(synthesizeDependenciesDebug)
}
then i get the configuration-cache issue. But if neither task calls
Task.project
, how is that error saying one task caused the invocation of that by another task?
c
Copy code
Execution of task ':keys:compileKotlin' caused invocation of 'Task.project' by task ':compose:components:synthesizeDependenciesDebug' at execution time which is unsupported.
There’s something cross-project going on - the paths are for different projects,
keys
and
:compose:components:synthesizeDependenciesDebug
. Are there places where tasks are wired across projects?
m
Indeed,
BaseProjectPlugin
unfortunately uses a bunch of
subprojects {...
lambdas to tie things together, but like so:
Copy code
register("detektScripts") {
                project.subprojects.forEach { subProject ->
                    subProject.tasks.findByName("detektBuildScripts")?.let { dependsOn(it) }
                }
            }
I was of the understanding that anything like the above is configuration-cache safe
c
Those patterns (subprojects/allprojects and wiring tasks across projects) have been long discouraged. Somewhere in there it is capturing a reference to a Project instance.
👍 1
Third anti-pattern: eager instantiation of tasks. Your build script is close to anti-pattern bingo! 😉
m
Yes, we've wanted to move away from using
subprojects
for a while but circumstances keep it there for now Also I'm assuming you're saying
findByName
shouldn't be used - I should be using
named
?
c
Somewhere this is a good table that maps the eager -> lazy, one sec…
named(String)
is the closest equivalent, but will fail if the task does not exist. Using
findByName(String)
will cause tasks registered with the new API to be created/configured.
Be aware of that difference in behaviour. https://docs.gradle.org/current/userguide/task_configuration_avoidance.html
👍 1
So this is a bad pattern:
Copy code
register("something")
   project.subprojects
But that shouldn't cause a configuration-cache issue, since that happens at configuration time, not execution time, right?
c
well… kinda. The code runs at configuration time, but may store stuff that is not evaluated until execution time. For this snippet:
Copy code
register("detektScripts") {
    project.subprojects.forEach { subProject ->
        subProject.tasks.findByName("detektBuildScripts")?.let { 
       		// unclear what the receiver for dependsOn is
        	dependsOn(it) 
        }
    }
}
…try using named() as that will avoid a dependency on a concrete Task instance (which has a Project reference).
👍 1
m
but may store stuff that is not evaluated until execution time.
What could this stuff be? If I inject
ProjectLayout
into a
Task
, is that non-CC-safe?
c
That is fine. In your case for that snippet it’s likely the concrete task instance dependency.
👍 1
m
So I've gone through and removed strict task dependencies wherever they existed, using
named
or
withType
when applicable, and I'm still seeing that same error log:
Copy code
execution of task :composition-locals:compileDebugKotlin📋 caused invocation of Task.project📋 in other task at execution time which is unsupported.
Trying to debug where this forced invocation is happening is proving to be fairly difficult - the CC report generated says like multiple task of a given project is not CC-safe, despite each of those tasks able to be stored in the CC when run individually How would I begin to pin down what tasks cause the invocation of another?