In my module I have this at the top ```plugins { ...
# community-support
s
In my module I have this at the top
Copy code
plugins {
  id("first.plugin")
  id("second.plugin")
}
The "second.plugin" is applying the android gradle plugin in my case. However inside "first.plugin" I have this sort of code:
Copy code
class FirstGradlePlugin : Plugin<Project> {
  override fun apply(project: Project) {
    fun Lint.configure(lintXmlFile: File, lintBaselineFile: File) {...impl here}
    when {
      pluginManager.hasPlugin(libs.plugins.androidApplication.get().pluginId) -> {
        configure<ApplicationExtension> { lint { configure(lintXmlPath, lintBaselineFile) } }
      }
      pluginManager.hasPlugin(libs.plugins.androidLibrary.get().pluginId) -> {
        configure<LibraryExtension> { lint { configure(lintXmlPath, lintBaselineFile) } }
      }
      else -> {
        pluginManager.apply(libs.plugins.lintGradlePlugin.get().pluginId)
        configure<Lint> { configure(lintXmlPath, lintBaselineFile) }
      }
    }
  }
}
However I realize that since I declare them in this order, the
else
is always hit, so I always setup the lint plugin, so then when the android gradle plugin tries to be applies it fails since it realizes that the task "lintFix" was already taken. By the accidentally added
libs.plugins.lintGradlePlugin
in this case. Is there a way for me to influence the order of this without having to be careful of which order I am declaring my plugins in each of my modules? Ideally I'd like it to work the same regardless of which order I use. Or do I perhaps do something completely wrong by doing this
hasPlugin
approach and I should just do something else all-together?
wrapping this
when
in a
Copy code
afterEvaluate {
  when {
    ...
  }
}
does actually work, but I feel like I am just working around the problem rather than fixing it
v
Your feelings are right. You should avoid
afterEvaluate
wherever you can, and having order requirements between plugins is bad practice.
t
Replace those
hasPlugin
conditions with
withPlugin()
Copy code
pluginManager.withPlugin("com.android.application") {
  configure<ApplicationExtension> { … }
}
pluginManager.withPlugin("com.android.library") {
  configure<LibraryExtension> { … }
}
The "problem" here will be the
else
. Either you use an
afterEvaluate
in which you check
hasPlugin
(or whether the
withPlugin
were called, setting some variable from them); or you let the user of the
first.plugin
manually apply the
com.android.lint
plugin, and you configure it with another
withPlugin
.
☝️ 2
v
Usually you would use
pluginManager.withPlugin
to react to other plugins being applied
But there is no really good way to react to a plugin not being applied
s
Okay, replacing
hasPlugin
with
withPlugin
sounds like a simple step, I shall do that. And yeah, it's very awkward since the lint plugin is something you can apply to normal non-android, just jvm modules. But then you have the lint plugin kinda already applied by default when you are in an android application or library. And then you get the same type in the end which you can configure all in one way. So this creates this awkward scenario where I want to only apply that standalone jvm lint plugin when I am in neither an android library nor an android application module, but I can't always just apply it, since it fails when I am in an android module, since it comes pre-applied there. This is my understanding of this problem at least, it's not unlikely that I am doing something else wrong of course. I will try and get it working with an afterEvaluate just around the
else
then for now and go from there unless I get some other idea which just works regardless of if I am in a jvm or non jvm module. Thanks a lot for the input!
e
ideally the lint plugin would be the same between an android project and a non-android project, but since it isn't, I'd create two plugins: one for android and one for non-android. they can be in the same build and both delegate to the same internal plugin, but one applies lint first and the other configures whatever android plugin is there
s
Uhh right, I think I understand what you mean here. I really wanted to avoid having to make this decision on the module level, but to be able to just apply my own plugin at the top and let it do the right thing regardless of if its applied in an android or non android module. Right now my modules look like this: Android:
Copy code
plugins {
 id("everything.plugin")
 id("plugin.that.adds.android.library")
}
Non-android
Copy code
plugins {
 id("everything.plugin")
 id("plugin.that.adds.jvm")
}
--- With your suggestion this would have to turn into Android:
Copy code
plugins {
 id("everything.plugin")
 id("plugin.that.does.withPlugin.and.configures.lint")
 id("plugin.that.adds.android.library")
}
Non-android
Copy code
plugins {
 id("everything.plugin")
 id("plugin.that.does.appliesLintItself.and.configures.lint")
 id("plugin.that.adds.jvm")
}
Is that what you mean?
e
yes something along those lines
s
Side note, but I changed my existing code to do this:
Copy code
var didConfigureLint = false
pluginManager.withPlugin(libs.plugins.androidApplication.get().pluginId) {
  configure<ApplicationExtension> { lint { configure(lintXmlPath, lintBaselineFile) } }
  didConfigureLint = true
}
pluginManager.withPlugin(libs.plugins.androidLibrary.get().pluginId) {
  configure<LibraryExtension> { lint { configure(lintXmlPath, lintBaselineFile) } }
  didConfigureLint = true
}
afterEvaluate {
  if (!didConfigureLint) {
    pluginManager.apply(libs.plugins.lintGradlePlugin.get().pluginId)
    configure<Lint> { configure(lintXmlPath, lintBaselineFile) }
  }
  dependencies {
    add("lintChecks", project(":hedvig-lint"))
  }
}
And this worked. Thanks for the tip about withPlugin ^^
Now I need to look into properly splitting it up as ephemient suggested to avoid the afterEvaluate completely. But I think I understand what to do. Thank you all three for your help, I really appreciate it!
👌 1
e
my idea is basically
Copy code
class AndroidLintPlugin : Plugin<Project> { // plugin.that.does.withPlugin.and.configures.lint
    override fun apply(target: Project) {
        with(target) {
            pluginManager.withPlugin(AndroidBasePlugin::class) {
                apply(CommonLintPlugin::class)

class JvmLintPlugin : Plugin<Project> { // plugin.that.does.appliesLintItself.and.configures.lint
    override fun apply(target: Project) {
        with(target) {
            apply(LintPlugin::class)
            apply(CommonLintPlugin::class)

class CommonLintPlugin : Plugin<Project> { // doesn't need id (unless you want one)
    override fun apply(target: Project) {
        with(target) {
            val lint: Lint = extensions.getByName("lint")
thank you 1
m
👀 1
This is one of the very few "valid" use cases of
afterEvaluate {}
IMO
In your specific case, I'd ask AGP to decouple lint though, same as @ephemient was hinting at here. It's probably a long way but worth asking IMO, especially if they want to promote lint usage outside of Android
s
It’s a good point. I just filed https://issuetracker.google.com/issues/378330024 to try to describe the problem there. Perhaps they have some good input on this, or they can tell us if it’s something they are considering fixing in some future. I am not sure if I did a good job explaining everything, if you feel like there’s anything that needs to be clarified in the question I would appreciate it if you could chime in to that issue. side note: I tried to file this to “Android Public Tracker > App Development > Android Studio > Gradle > Android Gradle Plugin” but I am blocked from filing to anything under “Android Studio”. I just went to the lowest ancestor in which I could file an issue, but this really does not feel like an “Android Studio” issue. Not sure if there’s a better component for this.
1
m
blocked from filing to anything under “Android Studio”.
That's the good thing to do 👍 The google team does the triage from there.
thank you 1