在多模块Gradle项目中使用自定义二进制Gradle插件

huangapple go评论58阅读模式
英文:

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 &quot;project type&quot; 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&#39;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&#39;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&#39;m at a loss... The second approach is what I&#39;ve used for other third-party plugins successfully, but yet it&#39;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&#39;s own project (and thus the deployment coordinate of the &quot;library&quot; 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 `&lt;group&gt;:&lt;artifact&gt;:&lt;version&gt;`

// 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(&quot;classpath&quot;);
        final String version = classpath.getResolvedConfiguration().getResolvedArtifacts().stream()
            .map(artifact -&gt; artifact.getModuleVersion().getId())
            .filter(id -&gt; &quot;com.company&quot;.equals(id.getGroup()) &amp;&amp; &quot;my-gradle-plugin&quot;.equals(id.getName()))
            .findAny()
            .map(ModuleVersionIdentifier::getVersion)
            .orElseThrow(() -&gt; new IllegalStateException(&quot;plugin has been deployed with wrong coordinates: expected group to be &#39;com.company&#39; and name to be &#39;my-gradle-plugin&#39;&quot;));
        return version;
    }
}

Updated (Working)

public class GradleHelper {
    
    public static String getPluginVersion(Project project) {
        Properties props = new Properties();
        try {
            props.load(GradleHelper.class.getClassLoader().getResourceAsStream(&quot;myplugin.properties&quot;));
        } catch (IOException e) {
            throw new IllegalStateException(&quot;Unable to determine the plugin version&quot;, e);
        }
        return props.getProperty(&quot;version&quot;);
    }
}

// my-gradle-plugin/build.gradle
task generateResources {
   ext {
      propFile = file(&quot;$buildDir/generated/myplugin.properties&quot;)
   } 
   outputs.file propFile 
   doLast {
      mkdir propFile.parentFile
      propFile.text = &quot;version=$project.version&quot; 
   } 
} 
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.

huangapple
  • 本文由 发表于 2023年6月22日 04:47:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76527032.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定