Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transitive dependency isn't analyzed for potentially containing a plugin when plugins configuration block is used #18628

Closed
Anatolii opened this issue Oct 13, 2021 · 9 comments
Labels
a:feature A new functionality closed:not-fixed Indicates the issue was not fixed and is not planned to be in:plugin-management

Comments

@Anatolii
Copy link

Anatolii commented Oct 13, 2021

When a Gradle plugin (A) depends on other Gradle plugin (B) as transitive dependency,
then if plugin A is loaded inside plugins block, it doesn't mean the plugin B also can be used without being explicitly mentioned.

// settings.gradle.kts
pluginManagement {
    plugins {
        id("A") version "demo" // has B as "implementation" or "api" dependency
    }
}

doesn't mean

// build.gradle.kts
plugins {
    id("A")
    id("B")
}

Expected Behavior

Transitive dependencies of a Gradle plugin are analyzed for potentially containing plugin descriptors too.

Current Behavior

The transitive dependency is not being analyzed, hence can not be used in build scripts.
User is required to specify the plugin ID and version in their scripts.
Adding the plugin dependency to classpath in the buildscript configuration block works. But promotes legacy way of applying plugins.

Context

My team has plugins which are re-used in lots of repositories. Those plugins can have dependency on other internal plugin binaries, or external, e.g. Android plugin.
Currently we apply our high-level plugins in a legacy way, using "buildscript" configuration block.
We would like to switch to the new way, using pluginManagement inside settings.gradle file, and using IDs instead of full dependency notation.
Currently we can not do the transition because this would mean mentioning in settings.gradle files all plugins that our high-level plugins rely on. This also can lead to a wrong version being selected by user for a transitive dependency of one of our plugins, which is not ready for the newer version yet, or doesn't support an older one.
We also want to keep simple the repositories where plugins are applied, so they won't require much changes and versions tracking when we update our plugins.

The reason for raising a bug, is that specifying a plugin ID inside plugins configuration block leads to the plugin's marker being downloaded, then the dependency it has will be downloaded and plugin would be loaded. So why not continue searching for plugins inside transitive dependencies? This could be configurable, but should be possible.

It also feels like the plugins configuration block was designed to load only plugin IDs specified inside of it, and even if a jar file has more, it doesn't mean other plugins will be abailable for use. So maybe there's another alternative which doesn't require usage of buildscript configuration, or buildSrc project?

Steps to Reproduce

PluginDependencyIssueDemo.zip
The project contains two tests which illustrate the problem

Your Environment

Build scan URL: https://gradle.com/s/jcir4qkpwptjg

@github-actions
Copy link

This issue has been automatically marked as stale because it has not had recent activity. Given the limited bandwidth of the team, it will be automatically closed if no further activity occurs. If you're interested in how we try to keep the backlog in a healthy state, please read our blog post on how we refine our backlog. If you feel this is something you could contribute, please have a look at our Contributor Guide. Thank you for your contribution.

@github-actions github-actions bot added the stale label Oct 15, 2022
@ov7a
Copy link
Member

ov7a commented Jul 26, 2023

Sorry for the late reply!

After discussing that we decided that this should not be implemented in the way you described.
A plugin depending on another plugin does not mean the other plugin should be applied.
It is possible for the plugin to apply another plugin as a part of its configuration.
See the documentation.

@ov7a ov7a closed this as not planned Won't fix, can't repro, duplicate, stale Jul 26, 2023
@ov7a ov7a added closed:not-fixed Indicates the issue was not fixed and is not planned to be and removed stale labels Jul 26, 2023
@Anatolii
Copy link
Author

A plugin depending on another plugin does not mean the other plugin should be applied.

I didn’t mean that either. User should define which plugins they want to apply and where (unless a plugin applies another one internally as you mentioned).
What I meant is that the other plugin is not possible later to use in build scripts. But if the plugin B description was analyzed while loading dependencies of plugin A and added to classpath as well (not applied), then developers would be able to use plugin B in project configurations as well.

@ov7a
Copy link
Member

ov7a commented Jul 26, 2023

That part was a bit confusing, sorry for the misunderstanding.

Despite that, the expected way to apply a plugin in your case is either:

  1. Add it explicitly to your plugins block in settings script:
File(testProjectDir, "settings.gradle.kts")
    .also { it.createNewFile() }
    .writeText("""
        pluginManagement { 
            repositories { 
                mavenLocal()
                gradlePluginPortal()
            }
            plugins {
                id("$dependentPluginId") version "$version"
                id("$dependencyPluginId") version "$version" // <-- here
            }
        }
    """.trimIndent())

or
2. Delete the plugins block from the settings script completely and have versions in build script:

File(testProjectDir, "settings.gradle.kts")
    .also { it.createNewFile() }
    .writeText("""
        pluginManagement { 
            repositories { 
                mavenLocal()
                gradlePluginPortal()
            }
        }
    """.trimIndent())

File(testProjectDir, "build.gradle.kts")
    .also { it.createNewFile() }
    .writeText("""
        plugins {
            id("$dependentPluginId") version "$version"
            id("$dependencyPluginId") version "$version"
        }
    """.trimIndent())

@ov7a
Copy link
Member

ov7a commented Jul 26, 2023

The problem with your suggestion is the ambiguity of the dependencyPluginId version. It should be explicitly defined somewhere. Consider a case when multiple plugins have the same dependency plugin with different versions - which version should be chosen? It brings complication into plugins definition.

@Anatolii
Copy link
Author

I'm glad you've mentioned plugin version example, @ov7a .
Let's say we have two versions for A plugin: Av1 and Av2.
Plugin B depends on implementation of Av1; plugin C depends on implementation of Av2. Both do not apply plugin A internally. Both depend on plugin identifier package, not the actual implementation package.
Think about use cases when plugins A,B,C are independently developed plugins, maintained by different teams in different companies, who don't speak with each other.
In settings someone adds

pluginManagement {
    plugins {
        id("B") version "Bv1"
        id("C") version "Cv1"
        id("A") version "Av3"
    }
}

When someone uses in build script following code:

plugins {
    id("A")
    id("B")
}

which version of A is going to be loaded? Will there be a conflict?
In this case there's no clear way to know/control which version of A will be used, and how Av3 will impact plugin B behaviour, as it also will appear on the same project's build script classpath but only one version of A can be there. Am I correct? If so, now you should see the problem.
So for the team I've mentioned in my initial message, we would greatly benefit from being able to skip specifying id("A") version "Av3" and still be able to apply plugin A in places where we need, and resolve version conflicts when needed.
In order to achieve that, on the pluginManagement step not only the direct dependency should be analyzed that represents plugin identifier, but also the transitive dependencies of the identifier to see if some of those represent a plugin identifier as well.

@ov7a
Copy link
Member

ov7a commented Jul 26, 2023

We do have a related issue for this:

There's nothing special in depending on another plugin without applying it - it is the same as having some "regular" third-party library as a common dependency for multiple plugins.

@Anatolii
Copy link
Author

Awesome! Indeed, the overall problem can be split into two parts.

So instead of specifying the plugin IDs in Plugins Management config, Gradle could figure out additional plugin IDs from dependencies. That would allow creation of plugins bundles which are not necessary mantained by same team, or enterprises to distribute plugin definitions without asking all projects to copy-paste lots of plugin IDs in Plugin Management config. Also new settings plugins could appear which would reliably know much more about plugins that will be available in a build when that settings plugin is applied.

@ov7a
Copy link
Member

ov7a commented Jul 27, 2023

I see your point, but I think the second point is covered by convention plugins in a project or by a proper plugin which combines (and applies) other plugins. If you want to reuse some code, these are the approaches you should consider. I think they should work in most (sane) cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a:feature A new functionality closed:not-fixed Indicates the issue was not fixed and is not planned to be in:plugin-management
Projects
None yet
Development

No branches or pull requests

3 participants