autonomousapps / gradle-best-practices-plugin

Gradle Plugin that detects violations of Gradle best practices in Gradle Plugins
Apache License 2.0
180 stars 3 forks source link

Extend usages to any project #12

Open TWiStErRob opened 1 year ago

TWiStErRob commented 1 year ago

Hi, I was trying to figure out https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin/issues/630 with this plugin, but I noticed that this plugin starts with:

pluginManager.withPlugin("java-gradle-plugin") {

which means I can't just apply it to my project to scan all build.gradle/build.gradle.kts files too.

Would it be possible to extend the scanning to those files? It's great for authoring plugins, but for discovering issues in potentially huge projects, where multiple third party plugins are applied (which don't use this plugin), it cannot be used :(

I ended up applying to includeBuild("gradle/plugins") as a fallback, at least that covers the majority of the Gradle scripts. I have a feeling this should be called out in the README.md too.

Also in a similar vein it would be nice to be able to analyze all the buildEnvironment for these. It only recognized usages in my own .class files, but I suspect there are others from third party plugins too that I'm not knowing about.

autonomousapps commented 1 year ago

Thanks for the issue. I recognize that the current limitation to analyzing only plugins means that this plugin misses a lot. I would be interested to hear your implementation suggestions for how to scan build.gradle[.kts] files. Could we still rely on the ASM/bytecode-based approach?

As far as analyzing third parties, I also agree that that would be useful. I don't have a lot of time, though (as my delay in response might already have indicated). PRs are welcome, or I also accept sponsorship to cover costs.

TWiStErRob commented 1 year ago

Could we still rely on the ASM/bytecode-based approach?

Since all build.gradle(.kts) files get compiled to .class file before they're executed on the JVM. These classes extend ProjectScript and are located in ${GRADLE_USER_HOME}/caches/${GradleVersion.current().baseVersion}/scripts/${hash}/(cp_)proj/ folders. So I would think it's definitely possible.

From here the question is: how do we find out which hashes are relevant?

From a build script, it's "trivial" to get the location: this.class.protectionDomain.codeSource.location which points to file:/{GRADLE_USER_HOME}/caches/jars-9/{anotherHash}/proj.jar.

However starting from a Project object, it's not that simple. I looked at Referring objects in the heap and only found one way so far: project.asDynamicObject.beforeConvention?.bean and this is null in some cases too.

All referrers ![image](https://github.com/autonomousapps/gradle-best-practices-plugin/assets/2906988/0f4b959f-56c0-4d96-a813-bfc2cf5577be)

Since we don't really need the instance of the script, only its class, we might get more luck looking for referrers to this.class, there are plenty (too many to investigate cleanly actually). We can even go one farther, we don't even need the class, only its classLoader, or its protectionDomain.

Definitely needs more investigation, or another idea.

autonomousapps commented 1 year ago

Are those proj.jars available on any available configuration/classpath? Are they part of the buildEnvironment? Is Project.getBuildScript() not useful?

TWiStErRob commented 1 year ago

As far as I saw, it's only exposed as an internal classloader (it's not a named dependency in a configuration, imagine being able to do buildscript { deps { exclude(build.gradle) } } 😅). I saw there's a special ClassLoaderScope for each module, and inside it for each build script. But I found no way to find child classloaders from a parent yet.

project.buildScript is a very high level DSL, we need to go way lower, sadly. I looked at buildscript.scriptClassPath first, but it was empty in my example project having no plugins.