is there a preferred way to conditionally register...
# community-support
c
is there a preferred way to conditionally register a task based on whether another task exists?
c
unsure on the conditional registration; in the past I’ve addressed this by having the task always registered and conditionally enabled.
βž• 1
m
+1. Immediate reaction would be something like so:
Copy code
tasks.register("foo")

tasks.configureEach {
  if (name == "foo") {
    // Gradle fails here
    tasks.register("bar")
  }
}
But this doesn't work.
TBH, if feels like this could be possible, albeit dangerous
c
but if a task you depend on doesn't exist then your task won't work and shouldn't be listed in tasks right?
why doesn't that work?
m
Something like concurrent mutation exception or something. You add to a list while iterating it.
At least that's my current mental model, I didn't check the implementation
You can register always but depend on conditionally, that should work
Copy code
tasks.register("foo")
val bar = tasks.register("bar")
bar.configure { isEnabled = false }

tasks.configureEach {
  if (name == "foo") {
    bar.configure { 
      dependsOn(this@configureEach)
      isEnabled = true
    }
  }
}
p
Tasks are generally registered by a plugin, so you should use
pluginManager.withPlugin
instead.
What's your task name/use case?
c
I'm adding a task based on whether a publication is configured. I actually think this will work...
Copy code
tasks
      .withType(PublishToMavenRepository.class)
      .stream()
      .filter(task -> task.getName().startsWith("publishMavenPublicationTo"))
      .toList()
      .forEach(task -> {
        var repository = task.getRepository();
        tasks.register(repository.getName() + "ArtifactPath", ArtifactPathTask.class, t -> {
          t.getDirectory().set(project.getLayout().dir(project.provider(() -> new File(repository.getUrl()))));
          t.getProjectName().set(project.getName());
          t.getProjectGroup().set(project.getGroup().toString());
          t.getProjectVersion().set(project.provider(project.getVersion()::toString));
        });
      });
but simply the existence of a plugin wouldn't tell me whether a publishing repository was added
p
You should not use stream, filter and forEach
πŸ‘† 1
πŸ‘†πŸΎ 1
But instead named and configureEach
c
but didn't we just discuss how that wouldn't work?
πŸ‘ŽπŸΎ 1
see martin's comment
p
And you should use the maven publish extension and extract the repositories because the publish plugin creates the tasks based on the extension
Yes, but Martin uses configureEach
The Java apis don't use configureEach but create the tasks.
That's totally different
c
you do realize how it sounds like you're contradicting yourself right now
p
Nope πŸ˜ŽπŸ˜‚
c
"use configureEach" but martin says "use configureEach doesn't work" "yes but martin uses configureEach. use configureEach" this is my interpretation of the conversation
πŸ˜„ 2
p
You should use this code:
Copy code
pluginManager.withPlugin("maven-publish") {
  val publishExtension = extensions.getByType<PublishingExtension>()
  publishExtension.repositories.withType<MavenArtifactRepository>().configureEach {
}
m
Yea, react to the "repositories" instead of reacting to the "tasks"
πŸ‘ 1
Both are probably really similar in practice though. But the "repositories" is cleaner since you don't need the tasks details
Also saves you from String comparisons
p
iterating though the tasks realizes them.
m
Not if you
.configureEach {}
, right?
p
Yeah, but configureEach is lazy :) And forEach is not.
m
Yup
forEach{}
and all collection APIs are verboten
p
And you should not register a task inside configureEach of another task.
πŸ‘ 1
βž• 1
Use the extension. This is the domain object that you can iterate and use it to register tasks.
πŸ‘ 1
m
Completely tangential but I realized recently that
.all{}
is on both
NamedDomainObjectContainer
and
Iterable<T>
so depending your compile type, it will do things lazy or eagerly , differently, fun times! πŸ˜„
p
Hm? All is eager
m
Yea even more fun, sorry
.all {}
is eager but live on a
NamedObjectContainer
It will be called when objects get added
p
No:
This method is a terminal eager operation. It will cause the realization of all elements of this collection.
m
yea but if you add after the fact, the closure gets called (I think?)
The behaviour is different, let me check
p
Yeah, it calls any closures immediately, even configureEach
Because all realizes all elements, so all configureEach (before and after) closures are called immediately
m
Copy code
tasks.all {
  println("${it.name}")
  true
}

tasks.register("foo")
This prints("foo")
Copy code
(tasks as Iterable<Task>).all {
  println("${it.name}")
  true
}

tasks.register("foo")
This doesn't
Not that I care much, it's just a fun Gradle puzzler
It's "terminal eager" but not "terminal terminal" πŸ™ƒ
Changing this will probably break a bunch of plugins that inadvertantly rely on this behaviour
p
What not, of course not
πŸ‘€ 1
all is a closure from the Gradle type. Using java Iterable "creates a copy", not technically correct, but this is the behavior.
βž• 1
It creates a "one shot" iterator.
m
Yea
p
So adding new elements after calling Java
all
won't contain/realize the new elements.
m
Yea, this is my mental model as well. It was just fun to bump into that
This was in KGP source code. Everyone is still calling eager apis...
c
read as KGB source code πŸ˜›
πŸ•΅οΈ 1
πŸ˜„ 1
sorry, been watching spy shows recently
p
Well, KGP also uses coroutines for custom "stages" internally to not use `Provider`s... I agree, they are annoying to use, but still.
πŸ™ˆ 1
c
ugh... that won't exactly work because I'm also dependent on the output of the task... which is why I was using the task...
and I need to detect whether it's a local or a remote... so much pain!
p
Thats okay, just use
tasks.named("publishMavenPublicationTo" + name)
and the providers
Inside the configureEach of the repositories.
c
(insert repsonse to subsequent question about casing), I'll get it... but in the mean time I have to go hastle my apt complex about their rent issues in this ... transitional time 😞
p
And local vs remote: use the url
c
Yep, got a parse the URL though to make sure its scheme isn't starting with HTTP which I'm sure From a simplistic standpoint is not sufficient but given it's a convention plug-in it should be enough
p
What do you mean with
parse
? You can just use URI.getScheme, but yeah, you need to "parse" the scheme or just check if the scheme is http/https
c
I need to parse the scheme. That's what I mean. If this wasn't going into a shared convention plug-in, I would probably make a ton of effort to figure out which things are considered valid local versus valid remote, but I'm not going to do that
πŸ‘ 1
e
> Well, KGP also uses coroutines for custom β€œstages” internally to not use `Provider`s I think its because the programming model of coroutines fits the gradle environment. Coroutines is the superior programming model for gradle plugins imho.
v
Afair
register
within
someTasks.configureEach
does not work (if task-configuraton avoidance is not broken) as the closure is called too late to register a new task, but you could still use
create
instead of
register
within the
configureEach
.