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

Unable to apply script plugin from precompiled script plugin #14517

Open
vpavic opened this issue Sep 14, 2020 · 13 comments
Open

Unable to apply script plugin from precompiled script plugin #14517

vpavic opened this issue Sep 14, 2020 · 13 comments
Labels

Comments

@vpavic
Copy link

vpavic commented Sep 14, 2020

I'm not sure whether this is a defect or simply a limitation that appears to be undocumented, below are the details.

Expected Behavior

Applying the common script plugin should be possible using precompiled script plugin in order to share the common build login between subprojects.

Current Behavior

Attempt to do so results in org.gradle.internal.service.UnknownServiceException: No service of type ClassLoaderScope available in ProjectScopeServices..

Context

I've run into this while trying to align with the latest recommendations on organizing multi-project builds.

Applying the script plugin is of course possible from the subprojects block (as shown in the reproduction project) or within a custom plugin using something like:

public class MyConventionPlugin implements Plugin<Project> {

    @Override
    public void apply(Project project) {
        project.getPlugins().withType(JavaPlugin.class, javaPlugin -> project.apply(
                action -> action.from(new File(project.getRootDir(), "gradle/dependency-versions.gradle"))));
    }

}

Steps to Reproduce

Clone the sample project and make the following changes:

Attempt to run any task results in:

An exception occurred applying plugin request [id: 'sample.java-conventions']
> Failed to apply plugin 'sample.java-conventions'.
   > org.gradle.internal.service.UnknownServiceException: No service of type ClassLoaderScope available in ProjectScopeServices.

Your Environment

$ ./gradlew -v

------------------------------------------------------------
Gradle 6.6.1
------------------------------------------------------------

Build time:   2020-08-25 16:29:12 UTC
Revision:     f2d1fb54a951d8b11d25748e4711bec8d128d7e3

Kotlin:       1.3.72
Groovy:       2.5.12
Ant:          Apache Ant(TM) version 1.10.8 compiled on May 10 2020
JVM:          11.0.8 (AdoptOpenJDK 11.0.8+10)
OS:           Linux 5.4.0-47-generic amd64
@big-guy
Copy link
Member

big-guy commented Sep 14, 2020

There's no intention to ever make this work, but we should forbid it directly if we can.

Our recommendation would be to convert the script plugin into another precompiled script plugin and apply that.

@vpavic
Copy link
Author

vpavic commented Sep 14, 2020

Thanks for the feedback.

Our recommendation would be to convert the script plugin into another precompiled script plugin and apply that.

The script common script plugin I'm trying to apply here simply populates the project extra properties with dependency versions so that they can be reused across the subprojects. With that in mind I doubt it would be desirable to make it into another precompiled script plugin (i.e. move it to buildSrc) as that would mean any dependency upgrade would invalidate the buildSrc causing it to be rebuilt, right?

@big-guy
Copy link
Member

big-guy commented Sep 14, 2020

Ah, ok. We're working on making the recommendations and documentation around that use case better in the next release (6.8 or so).

In short, what you can do is instead define a separate subproject that defines all of the versions you want to share.

This is what we do in the Gradle build:
https://github.com/gradle/gradle/blob/master/subprojects/distributions-dependencies/build.gradle.kts

We define a platform.

Defining the list of libs in buildSrc may or may not be the best idea (something we're considering), but this allows you to change the dependency version in your platform project without invalidating buildSrc.

Then in each of the subprojects, instead of doing something like:

api("org.foo:bar:${versions.foobar}")

You'd instead do (no version information):

api("org.foo:bar")

And in each subproject, you'd also add the platform as a dependency:

api(platform(":platform-project"))

The platform acts similar to a Maven BOM and shows up in reports as the source of the version information:

e.g., see how org.apache.ant:ant is a dependency of :core:compileClasspath, but the version is defined through :distributions-dependencies platform.

https://ge.gradle.org/s/ulwjybq5bksdi/dependencies?focusedDependency=WzE2LDEsMTQ2NSxbMTYsMSxbMTQ2NV1dXQ&focusedDependencyView=versions&toggled=W1sxNl0sWzE2LDFdLFsxNiwxLFsxNDY1XV1d

HTH

/cc @donat

@vpavic
Copy link
Author

vpavic commented Sep 16, 2020

Thanks for the suggestions @big-guy.

Having taken a closer look at the Platform plugin docs, I now see the sharing a set of dependency versions between subprojects use case you described there. Typically, I only considered the Platform plugin for the other two cases, but I like the idea of using it this way as well.

However, my concern is that this would imply the need to publish such a platform subproject. I'd like to avoid that as platform that centralizes the dependency management between subproject is likely to cover both subprojects that are published and the internal ones. Moreover, if I decided to publish a platform that links to subprojects as described here, that would mean I've got two published platforms which could potentially be confusing for the consumers.

I lurked around and found this sample demoing the concept of an internal platform. Is this something you'd recommend or even consider providing first-class support in future Gradle releases?

@big-guy
Copy link
Member

big-guy commented Sep 17, 2020

@jjohannes ☝️ maybe you could chime in on what the internal platform meant?

@jjohannes
Copy link
Contributor

@vpavic yes you can use an "internal platform" (working title ;)) for this. This is not a built-in feature of Gradle (yet) though (#10861).

To set it up as in the sample, you need to define a configuration to declare the dependency to the platform that is not part of the published dependencies. As it is done here: https://github.com/jjohannes/gradle-demos/blob/master/internal-platform/build.gradle.kts#L9-L19
Ideally you would do something like that in a convention plugin in buildSrc (and not in the root build file as I did in that sample, which I shall update).

This solution is also described in more detail here: #10861 (comment)

You then need to be careful when publishing that you publish enough version information, or use publishing of resolved versions.

@vpavic
Copy link
Author

vpavic commented Sep 17, 2020

Thanks @jjohannes, the internal platform approach worked great for me. 👍 It was exactly what I was looking for both from the perspective of build logic organization as well as the resulting publications. I'll follow #10861 for further developments on that approach.

Back to the originally reported problem, after some exploration it seems that it's not limited to only applying script plugin from precompiled script plugin but rather any use of the old syntax of applying plugins meaning it can be reproduced even with something like apply(plugin: "checkstyle").

This IMO adds to the already confusing situation with plugins {} vs apply() - I generally tend to prefer the old apply() way of declaring plugins as it's quite easy to run into some of the limitations of plugins {} DSL in a real world project. This is, to my knowledge at least, the first situation where the old syntax is limited compared to the plugins {} DSL. It's also somewhat confusing from the perspective of this statement from the docs:

The plugins {} block can currently only be used in a project’s build script and the settings.gradle file. It cannot be used in script plugins or init scripts.

@jjohannes
Copy link
Contributor

Yes the docs need some updates to clarify the situation. We are on it.

We are moving towards making the plugins {} block the default and will soon discourage using apply and discourage using script plugins in the documentation. (At some point in Gradle's future these concepts will then be deprecated.)

@leonard84
Copy link
Member

Just ran into this issue, maybe we could display a more helpful error message. It was actually rather simple to change the apply from to another convention plugin.

@jbartok jbartok self-assigned this Aug 31, 2021
@jbartok jbartok added @support Issues owned by GBT support team in:plugin-management and removed to-triage labels Aug 31, 2021
@jbartok
Copy link
Member

jbartok commented Sep 1, 2021

Note: add forbidding it explicitly, see Sterling's comment from the 15th of September.

@jbartok jbartok removed their assignment Sep 1, 2021
@rivancic
Copy link

If I understand your original issue @vpavic. You wanted to apply script plugin inside of precompiled script plugin and it doesn't work? As I am afraid I have the same issue atm.

@tresat tresat assigned tresat and unassigned tresat Feb 7, 2022
@ljacomet
Copy link
Member

ljacomet commented Nov 4, 2022

We should clarify documentation to indicate that project.apply is not supported in a precompiled script plugin.
And see about making calling project.apply an much more explicit error.

@ljacomet ljacomet added the has:reproducer DON'T USE label Nov 4, 2022
@ljacomet ljacomet added re:comprehensibility reasonable errors and warnings, clear dsl, mental overload and removed @support Issues owned by GBT support team labels Dec 21, 2023
@piotrminkina
Copy link

Same problem in Gradle 8.7… Workaround is: plugins.apply 'plugin.id'.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests