ben-manes / gradle-versions-plugin

Gradle plugin to discover dependency updates
Apache License 2.0
3.86k stars 199 forks source link

Configuration Cache support #666

Open rahulsainani opened 2 years ago

rahulsainani commented 2 years ago

Got this warning in Android Studio's build analyzer:

com.github.ben-manes.versions: not compatible 
You could save about 4.3s by turning Configuration cache  on. 
With Configuration cache, Gradle can skip the configuration phase entirely when nothing that affects the build configuration has changed.

The version of this plugin used in this build is not compatible with Configuration cache and we don’t know the version when it becomes compatible.  

 Plugin version: 0.42.0 
 Plugin dependency: com.github.ben-manes:gradle-versions-plugin

Is it possible to use configuration cache with this plugin?

ben-manes commented 2 years ago

Compatibility would require a rewrite to use per-project tasks and a root-level accumulator task (similar to jacoco). PRs are appreciated if someone wants to work on that. Instead we mark the task as not compatible so that the configuration cache ignores our task and lets you get the benefits in other builds.

tprochazka commented 2 years ago

Technically I think, that ignoring these plugin tasks until I call it is entirely enough. Because the most important is build speed during development and nobody actually needs to call this plugin as often as build&run operation. When you really want to check version updates, it is not so important that it will take a little bit longer.

Currently, last released version has this in the changelog

Merge pull request https://github.com/ben-manes/gradle-versions-plugin/pull/585 from eygraber/dont-fail-w-configuration-cache Prevent builds from failing w/ configuration cache enabled on Gradle 7.4+

So why does Android studio still display the warning that this plugin is blocking to use of compilation cache?

And there is also one possible workaround. It is possible detect that gradle is invoked from Android Studio IDE by this function

val Project.isIDEBuild: Boolean
    get() {
        return this.hasProperty("android.injected.invoked.from.ide")
    }

So you can apply the plugin only when the project is not invoked from IDE.

jaredsburrows commented 2 years ago

We would have to rewrite parts of this plugin to not pass project our @TaskAction to make --configuration-cache pass in a test.

ianbrandt commented 1 year ago

Testing upgrade to Gradle 8.1 RC1:

One oddity here, without org.gradle.configuration-cache.problems=warn, I also get configuration cache errors for the compileJava and jar tasks in addition to the dependencyUpdates task when running dependencyUpdates. I don't get these additional errors when just running build or jar, or if I do add org.gradle.configuration-cache.problems=warn.

Reproducer with org.gradle.configuration-cache.problems=warn currently set: https://github.com/sdkotlin/sd-kotlin-talks/tree/gradle-8.1 (specifically, https://github.com/sdkotlin/sd-kotlin-talks/tree/73c97f04b1bbbe361ea6c51ae1886f70e208bfa0).

ianbrandt commented 1 year ago

Testing with Gradle 8.1 RC2:

It would be nice to be able to set org.gradle.configuration-cache=true, org.gradle.configuration-cache.problems=warn, and still be able to use this plugin.

Reproducer: https://github.com/sdkotlin/sd-kotlin-talks/tree/929b0ccfd607bb41019cce34accd4abf94f35408.

Should I file a separate issue for it, since it's not the same thing as full support for the configuration cache?

ben-manes commented 1 year ago

I don't think there is any problem here? The task is correctly declaring that it does not support the configuration cache, which violates the idiom by spamming the logs regardless (https://github.com/gradle/gradle/issues/24435). You can suppress this noise using --quiet (or -q). Since this cache is not required and our incompatibility not harmful as an infrequent task, I don't see this being a problem for the plugin. The noise pollution is a Gradle issue.

A rewrite to support the configuration cache is more invasive. As a ten year old weekend project, I don't think myself or others are really up for that, especially since the Gradle/Github folks are discussing improvements for use by dependabot. I'd like to see if the Gradle team adds direct support or engineers a preferred solution. If not, then I think they would not break compatibility due to the need for this type of plugin, so it becomes hard to really justify the work involved given everyone contributes in their limited free time. If someone feels really passionate and sends a PR then I am always happy to review and add another wonderful contributor & maintainer.

ianbrandt commented 1 year ago

I'd expect the plugin to still print available updates even if org.gradle.configuration-cache=true and org.gradle.configuration-cache.problems=warn are set. With Gradle 8.1 RC2 it just prints, "No dependencies found."

ben-manes commented 1 year ago

Then that sounds like a Gradle bug if it broke between 8.0 and 8.1?

I upgraded and ran it on Caffeine where the report came out as expected. I'll try yours when I get some downtime, but I think you should report your findings for the Gradle team to hopefully fix during the RC process.

ben-manes commented 1 year ago

Running your reproducer and I think you should open a Gradle issue. I see that passing --no-configuration-cache will generate a report. This means all of our logic works correctly, except when caching is enabled in which case it must get empty results?

I am still trying to reproduce it outside of the plugin to understand where it deviates. I will likely have to build a custom version with logging to see where it breaks down, since it is very obscure.

Can you start a conversation with the Gradle folks?

ben-manes commented 1 year ago
subprojects {
  tasks {
    register("allConfigurationDependencies") {
      notCompatibleWithConfigurationCache("incompatible")
      doLast {
        project.configurations
        .forEach { conf ->
          conf
          .allDependencies
          .forEach { dep ->
            System.out.println(conf.name + ":\t" + dep.name)
          }
        }
      }
    }
  }
}

That will print out the dependencies when the configuration cache is disabled or if performed within an afterEvaluate block. If the cache is enabled then the task results are empty and the swallowed exception is logged, InvalidUserCodeException: Invocation of 'Task.project' by task ... at execution time is unsupported.

The notCompatibleWithConfigurationCache() should not enforce this as it is valid behavior to use without caching which adds this restriction, but it should be disabled when planning the task executions. The notCompatibleWithConfigurationCache has no effect if applied or not, as the build is always successful.

This is a regression in the build tool.

ben-manes commented 1 year ago

Hacking up your build, and I believe the configuration cache is buggy when crossing the includeBuild boundary.

If I add a dependency in platform:test-platform and the allConfigurationDependencies task defined above then,

hope that helps.

ianbrandt commented 1 year ago

Thanks for looking into the "No dependencies" issue! Initial comment posted to https://github.com/gradle/gradle/issues/24411#issuecomment-1491040354.

ianbrandt commented 1 year ago

Filed https://github.com/gradle/gradle/issues/24636.

FunkyMuse commented 1 year ago

Stacktrace

3 problems were found storing the configuration cache, 2 of which seem unique.
- Task `:dependencyUpdates` of type `com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask`: cannot serialize a lambda that captures or accepts a parameter of type 'org.gradle.api.artifacts.Configuration' as these are not supported with the configuration cache.
  See https://docs.gradle.org/8.2/userguide/configuration_cache.html#config_cache:requirements:disallowed_types
- Task `:dependencyUpdates` of type `com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask`: invocation of 'Task.project' at execution time is unsupported.
  See https://docs.gradle.org/8.2/userguide/configuration_cache.html#config_cache:requirements:use_project_during_execution

invocation of Task.project📋 at execution time is unsupported

This is the error I get