英文:
Using custom binary gradle plugin within a multi-module gradle project
问题
插件
我已经创建了一个自定义的二进制Gradle插件,包含了之前保存在各个项目build.gradle中并从项目复制到项目的所有功能。因为我们有几种不同类型的项目,我为每种类型创建了一个不同的插件,以便加载正确的插件可以提供所有期望的功能。因为一切都如此相似,我有一个包含所有插件代码的单个项目,并发布它们。
// 插件结构
my-gradle-plugin
|-src/main/java
| |-com.company.gradle.plugin
| |-common.CommonPlugin.java
| |-a.TypeAPlugin.java
| |-b.TypeBPlygin.java
| |-等等
|-build.gradle
// build.gradle
gradlePlugin {
plugins {
common {
id = 'com.company.gradle.plugin.core'
implementationClass = 'com.company.gradle.plugin.common.CommonPlugin'
}
a {
id = 'com.company.gradle.plugin.a'
implementationClass = 'com.company.gradle.plugin.a.TypeAPlugin'
}
// 等等
}
}
通用插件由所有插件使用,并在每个"项目类型"插件中的代码中加载。
// TypeAPlugin.java
public class TypeAPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getPlugins().apply(CommonPlugin.class);
}
// 省略部分
}
发布
插件的发布是通过maven-publish
处理的,对于我的测试,我通过publishToMavenLocal
进行发布,并通过publish
将其部署到我们内部的Nexus仓库。我们的Nexus仓库在系统级别通过~/.gradle/init.gradle
配置为插件仓库。
settingsEvaluated { settings ->
settings.pluginManagement {
repositories {
mavenLocal()
maven {
allowInsecureProtocol = 'true'
url 'http://myNexusHost/repository/internal'
mavenContent {
releasesOnly()
}
}
maven {
allowInsecureProtocol = 'true'
url 'http://myNexusHost/repository/snapshots'
}
}
}
}
我们的Nexus仓库的适当配置也在同一个~/.gradle/init.gradle
中完成。
客户端项目
如果我尝试在另一个项目中使用它,那么在一个简单/独立项目内,我可以添加以下内容:
plugins {
id 'com.company.plugin.a' version '1.2.3'
}
一切都很好。插件被加载并按预期工作。现在,我正试图在一个多模块项目中使用它,具有以下结构:
projectRoot
|-buildSrc
| |-src/main/groovy/project-convensions.groovy
| |-build.gradle
|-moduleA
| |-src/main/java
| |-build.gradle
|-moduleB
| |-src/main/java
| |-build.gradle
|-等等
|-settings.gradle
// settings.gradle
rootProject.name = 'projectRoot'
include 'moduleA'
include 'moduleB'
// 等等
我想要完成的目标是在buildSrc项目的project-convensions.groovy中包含com.company.plugin.a
,以便通过它对所有嵌套模块可用(即:将来修改其版本将在一个地方完成,而不是针对每个模块)。
我尝试了简单地按照独立项目的方式包含相同的插件部分,但不起作用。
// buildSrc/src/main/groovy/project-convensions.groovy
plugins {
id 'com.company.plugin.a' version '1.2.3'
}
// moduleA/build.gradle
plugins {
id 'project-convensions'
}
// 错误
无效的插件请求 [id: 'com.company.plugin.a', version: '1.2.3']。预编译脚本的插件请求不得包含版本号。请从有问题的请求中删除版本,并确保包含请求的插件模块 'com.company.plugin.a' 是一个实现依赖项。
好吧,接下来,我尝试将版本放入buildSrc/build.gradle
,但也不起作用。
// buildSrc/build.gradle
dependencies {
implementation 'com.company.plugin.a:com.company.plugin.a.gradle.plugin:1.2.3'
}
// buildSrc/src/main/groovy/project-convensions.groovy
plugins {
id 'com.company.plugin.a'
}
// moduleA/build.gradle
plugins {
id 'project-convensions'
}
// 错误
> Failed to apply plugin 'project-conventions'.
> Failed to apply plugin class 'com.company.gradle.plugin.common.CommonPlugin'.
> plugin has been deployed with wrong coordinates: expected group to be 'com.company' and name to be 'my-gradle-plugin'
现在我不知所措...第二种方法是我用于成功使用其他第三方插件的方法,但在这里不起作用。我应该以不同的方式应用此插件吗?我需要将my-gradle-plugin
拆分为每个都有自己的项目(因此"库"的部署坐标与插件的坐标相匹配)吗?为什么这对独立项目有效,但在这种嵌套方式下不起作用?
需要注意的是,如果我通过第一种方法将插件分别应用于projectRoot
中的每个模块(就像它是一个独立项目一样),那么它会起作用...。
我正在使用Gradle 7.2.0。
更新 - 尝试通过<group>:<artifact>:<version>
访问
// buildSrc/build.gradle
dependencies {
implementation 'com.company:my-gradle-plugin:1.2.3'
}
// buildSrc/src/main/groovy/project-convensions.groovy
plugins {
id 'com.company.plugin.a'
}
// moduleA/build.gradle
plugins {
id '
<details>
<summary>英文:</summary>
**The Plugin**
I have created a custom binary Gradle plugin that encompasses all of the capabilities that we were previously keeping in individual project build.gradles and copying around from project to project. Because we have a couple of different types of project, I created a different plugin for each type such that loading the correct plugin gives all of the expected capabilities. Because everything is so similar I have a single project that contains the code for all plugins, and publishes all of them.
// Plugin Structure
my-gradle-plugin
|-src/main/java
| |-com.company.gradle.plugin
| |-common.CommonPlugin.java
| |-a.TypeAPlugin.java
| |-b.TypeBPlygin.java
| |-etc
|-build.gradle
// build.gradle
gradlePlugin {
plugins {
common {
id = 'com.company.gradle.plugin.core'
implementationClass = 'com.company.gradle.plugin.common.CommonPlugin'
}
a {
id = 'com.company.gradle.plugin.a'
implementationClass = 'com.company.gradle.plugin.a.TypeAPlugin'
}
// etc
}
The common plugin is used by all, and loaded in code by each "project type" plugin
// TypeAPlugin.java
public class TypeAPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getPlugins().apply(CommonPlugin.class);
}
// snip
}
**Publishing**
The publishing of the plugin is handled via `maven-publish`, where for my testing I publish via `publishToMavenLocal` and it is deployed to our in-house Nexus repo via `publish`. Our Nexus repo is configured to act as a plugin repository at the system level via `~/.gradle/init.gradle`
settingsEvaluated { settings ->
settings.pluginManagement {
repositories {
mavenLocal()
maven {
allowInsecureProtocol = 'true'
url 'http://myNexusHost/repository/internal'
mavenContent {
releasesOnly()
}
}
maven {
allowInsecureProtocol = 'true'
url 'http://myNexusHost/repository/snapshots'
}
}
}
}
The appropriate configuration for our Nexus repo is also done within the same `~/.gradle/init.gradle`
**Client Project**
If I attempt to use it within another project then I can so with within an simple/individual project by adding
plugins {
id 'com.company.plugin.a' version '1.2.3'
}
and all is good in the world. The plugin is loaded and works as expected. Now however I'm trying to use it within a multi-module project with the following structure
projectRoot
|-buildSrc
| |-src/main/groovy/project-convensions.groovy
| |-build.gradle
|-moduleA
| |-src/main/java
| |-build.gradle
|-moduleB
| |-src/main/java
| |-build.gradle
|-etc
|-settings.gradle
// settings.gradle
rootProject.name = 'projectRoot'
include 'moduleA'
include 'moduleB'
// etc
What I want to accomplish is include `com.company.plugin.a` in the buildSrc project-convensions.groovy, such that it becomes available through that to all nested modules (i.e.: changing its version down the road will be done in one place, rather than for each module).
I tried to simply include the same plugins section as per an individual project, but that doesn't work
// buildSrc/src/main/groovy/project-convensions.groovy
plugins {
id 'com.company.plugin.a' version '1.2.3'
}
// moduleA/build.gradle
plugins {
id 'project-convensions'
}
// Error
Invalid plugin request [id: 'com.company.plugin.a', version: '1.2.3']. Plugin requests from precompiled scripts must not include a version number. Please remove the version from the offending request and make sure the module containing the requested plugin 'com.company.plugin.a' is an implementation dependency
Alright, so next I tried placing the version into `buildSrc/build.gradle`, but that is also not working.
// buildSrc/build.gradle
dependencies {
implementation 'com.company.plugin.a:com.company.plugin.a.gradle.plugin:1.2.3'
}
// buildSrc/src/main/groovy/project-convensions.groovy
plugins {
id 'com.company.plugin.a'
}
// moduleA/build.gradle
plugins {
id 'project-convensions'
}
// Error
> Failed to apply plugin 'project-conventions'.
> Failed to apply plugin class 'com.company.gradle.plugin.common.CommonPlugin'.
> plugin has been deployed with wrong coordinates: expected group to be 'com.company' and name to be 'my-gradle-plugin'
Now I'm at a loss... The second approach is what I've used for other third-party plugins successfully, but yet it's not working here. Should I be applying this plugin differently? Do I need to break up `my-gradle-plugin` such that each has it's own project (and thus the deployment coordinate of the "library" matches that of the plugin? Why would this work for an individual project but not in this nested manner?
To note, if I apply the plugin to each module within `projectRoot` individually via the first approach (as if it were an independent project) then it works....
I am using Gradle 7.2.0
**Update** - trying to access via `<group>:<artifact>:<version>`
// buildSrc/build.gradle
dependencies {
implementation 'com.company:my-gradle-plugin:1.2.3'
}
// buildSrc/src/main/groovy/project-convensions.groovy
plugins {
id 'com.company.plugin.a'
}
// moduleA/build.gradle
plugins {
id 'project-convensions'
}
// Error
> Failed to apply plugin 'project-conventions'.
> Failed to apply plugin class 'com.company.gradle.plugin.common.CommonPlugin'.
> plugin has been deployed with wrong coordinates: expected group to be 'com.company' and name to be 'my-gradle-plugin'
No dice unfortunately :-(
**Update** - Stacktrace
The source of the problem is as follows
Caused by: java.lang.IllegalStateException: plugin has been deployed with wrong coordinates: expected group to be 'com.company' and name to be 'my-gradle-plugin'
at com.company.gradle.plugin.GradleHelper.lambda$getPluginVersion$2(GradleHelper.java:25)
at com.company.gradle.plugin.GradleHelper.getPluginVersion(GradleHelper.java:25)
at com.company.gradle.plugin.common.CommonPlugin.applyCommonDependencies(FrameworkCorePlugin.java:99)
at com.company.gradle.plugin.common.CommonPlugin.apply(FrameworkCorePlugin.java:50)
at com.company.gradle.plugin.common.CommonPlugin.apply(FrameworkCorePlugin.java:38)
at org.gradle.api.internal.plugins.ImperativeOnlyPluginTarget.applyImperative(ImperativeOnlyPluginTarget.java:43)
at org.gradle.api.internal.plugins.RuleBasedPluginTarget.applyImperative(RuleBasedPluginTarget.java:51)
at org.gradle.api.internal.plugins.DefaultPluginManager.addPlugin(DefaultPluginManager.java:187)
at org.gradle.api.internal.plugins.DefaultPluginManager.access$100(DefaultPluginManager.java:52)
at org.gradle.api.internal.plugins.DefaultPluginManager$AddPluginBuildOperation.run(DefaultPluginManager.java:282)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:56)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$run$1(DefaultBuildOperationExecutor.java:74)
at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.runWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:45)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:74)
at org.gradle.api.internal.plugins.DefaultPluginManager.lambda$doApply$0(DefaultPluginManager.java:167)
at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:44)
at org.gradle.api.internal.plugins.DefaultPluginManager.doApply(DefaultPluginManager.java:166)
... 196 more
</details>
# 答案1
**得分**: 0
感谢@Vampire能够看到我在从未发布到这里的代码中看不到的问题。
这个问题与Gradle本身无关,而是与我在插件中使用的机制有关,以便它可以确定自己的版本。虽然我对此没有真正的记忆,但显然我遵循了gradle论坛上概述的解决方案(https://discuss.gradle.org/t/how-can-a-custom-gradle-plugin-determine-its-own-version/36761/2),并且不出所料地遇到了与该帖子中的OP相同的问题。这最终意味着要做与该帖子中的OP所做的完全相同的事情:
**原始**(不工作)
```java
public class GradleHelper {
public static String getPluginVersion(Project project) {
final Configuration classpath = project.getBuildscript().getConfigurations().getByName("classpath");
final String version = classpath.getResolvedConfiguration().getResolvedArtifacts().stream()
.map(artifact -> artifact.getModuleVersion().getId())
.filter(id -> "com.company".equals(id.getGroup()) && "my-gradle-plugin".equals(id.getName()))
.findAny()
.map(ModuleVersionIdentifier::getVersion)
.orElseThrow(() -> new IllegalStateException("plugin has been deployed with wrong coordinates: expected group to be 'com.company' and name to be 'my-gradle-plugin'"));
return version;
}
}
更新(工作)
public class GradleHelper {
public static String getPluginVersion(Project project) {
Properties props = new Properties();
try {
props.load(GradleHelper.class.getClassLoader().getResourceAsStream("myplugin.properties"));
} catch (IOException e) {
throw new IllegalStateException("Unable to determine the plugin version", e);
}
return props.getProperty("version");
}
}
// my-gradle-plugin/build.gradle
task generateResources {
ext {
propFile = file("$buildDir/generated/myplugin.properties")
}
outputs.file propFile
doLast {
mkdir propFile.parentFile
propFile.text = "version=$project.version"
}
}
processResources {
from files(generateResources)
}
通过对包含版本信息的属性文件进行此更改,并从该文件加载它,修复了多模块项目的问题。
英文:
Kudos to @Vampire for seeing what I was unable to see in code that I never posted to here.
The issue has nothing to do with Gradle as such, but rather a mechanism I employed in the plugin so that it could determine its own version. While I have no real recollection of this, I evidently followed the solution as outlined on the gradle forums (https://discuss.gradle.org/t/how-can-a-custom-gradle-plugin-determine-its-own-version/36761/2) and unsurprisingly ran into the exact issue as the OP in that thread. What this ultimately means is doing exactly what the OP did in that thread:
Original (Not working)
public class GradleHelper {
public static String getPluginVersion(Project project) {
final Configuration classpath = project.getBuildscript().getConfigurations().getByName("classpath");
final String version = classpath.getResolvedConfiguration().getResolvedArtifacts().stream()
.map(artifact -> artifact.getModuleVersion().getId())
.filter(id -> "com.company".equals(id.getGroup()) && "my-gradle-plugin".equals(id.getName()))
.findAny()
.map(ModuleVersionIdentifier::getVersion)
.orElseThrow(() -> new IllegalStateException("plugin has been deployed with wrong coordinates: expected group to be 'com.company' and name to be 'my-gradle-plugin'"));
return version;
}
}
Updated (Working)
public class GradleHelper {
public static String getPluginVersion(Project project) {
Properties props = new Properties();
try {
props.load(GradleHelper.class.getClassLoader().getResourceAsStream("myplugin.properties"));
} catch (IOException e) {
throw new IllegalStateException("Unable to determine the plugin version", e);
}
return props.getProperty("version");
}
}
// my-gradle-plugin/build.gradle
task generateResources {
ext {
propFile = file("$buildDir/generated/myplugin.properties")
}
outputs.file propFile
doLast {
mkdir propFile.parentFile
propFile.text = "version=$project.version"
}
}
processResources {
from files(generateResources)
}
Making this change to include the version in a property file, and loading it from said file, fixed the issue with multi-module projects.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论