[英]How do I include Groovy Extension methods in jar?
I have a groovy library that I publish as a jar file on a nexus repository.我有一个 groovy 库,我将它作为 jar 文件发布在一个 nexus 存储库上。 When I use extension methods in the library from another project's Gradle script, I get a MissingMethodException .当我从另一个项目的 Gradle 脚本中使用库中的扩展方法时,我得到一个MissingMethodException 。
As a reference, say I have a String extension method, such as:作为参考,假设我有一个 String 扩展方法,例如:
static boolean containsIgnoreCase(String self, String str) {
self.toLowerCase().contains(str.toLowerCase())
}
If in my library I call the method using "foobar".containsIgnoreCase("Foo")
, I will get the exception.如果在我的库中我使用"foobar".containsIgnoreCase("Foo")
调用该方法,我将得到异常。 If I instead call it using StringExtensions.containsIgnoreCase("foobar", "Foo")
, it works, no problem.如果我改为使用StringExtensions.containsIgnoreCase("foobar", "Foo")
调用它,它可以工作,没问题。
My guess is that this is an issue with publishing the Groovy project without the META-INF file that defines the extensions.我的猜测是,在没有定义扩展的 META-INF 文件的情况下发布 Groovy 项目是一个问题。 Here is the project structure:这是项目结构:
- Library
- src/main/
- groovy/
- (here are my sources)
- resources/META-INF/groovy
- org.codehaus.groovy.runtime.ExtensionModule (contains details about my extension classes)
My ExtensionModule file looks like this:我的 ExtensionModule 文件如下所示:
moduleName=string-extensions
moduleVersion=1.0
extensionClasses=com.my.project.StringExtensions
In my publish block in build.gradle , I use the following:在build.gradle的发布块中,我使用以下内容:
plugins {
id 'groovy'
id 'java'
id 'maven-publish'
}
//...
sourceSets {
main.groovy.outputDir = sourceSets.main.java.outputDir
test.groovy.outputDir = sourceSets.test.java.outputDir
}
//...
publishing {
publications {
library(MavenPublication) {
from components.java
}
}
//...
}
What do I need to include in my publication in order to get the extension methods correctly registered when I use this library in another project's Gradle build script?当我在另一个项目的 Gradle 构建脚本中使用此库时,我需要在我的出版物中包含什么才能正确注册扩展方法? I have added the classpath/repo url for the dependency in my buildscript, and can access methods of the library - these just fail when calling an extension method.我已经为我的构建脚本中的依赖项添加了类路径/repo url,并且可以访问库的方法 - 这些只是在调用扩展方法时失败。
Bad news: there is a bug in Gradle because of which Gradle build scripts don't support Groovy extension modules at the moment.坏消息:Gradle 中存在一个错误,因为 Gradle 构建脚本目前不支持 Groovy 扩展模块。
One thing to note: the Groovy docs seem to be wrong in that they specify to put the module descriptor under META-INF/groovy/
.需要注意的一件事: Groovy 文档似乎是错误的,因为他们指定将模块描述符放在META-INF/groovy/
下。 When I do that, I can't even use the extension method of the resulting library in a plain Groovy application.当我这样做时,我什至不能在普通的 Groovy 应用程序中使用生成的库的扩展方法。 I had to put the descriptor under META-INF/services/
instead to make it work.我不得不将描述符放在META-INF/services/
下才能使其工作。
That still doesn't help with the Gradle use case.这对 Gradle 用例仍然没有帮助。 However, it shows that building and publishing works correctly: FWIW, I've just set up two tiny projects myself and I can reproduce the issue that you're seeing (with Gradle 6.7.1).但是,它表明构建和发布工作正常:FWIW,我自己刚刚建立了两个小项目,我可以重现您看到的问题(使用 Gradle 6.7.1)。 With the mentioned switch to the META-INF/services/
directory, I could at least get the extension to work in a Groovy application.通过上面提到的切换到META-INF/services/
目录,我至少可以让扩展在 Groovy 应用程序中工作。 So, given that you can access the Groovy methods as non-extension methods in your Gradle build, looking at the rest of your question and by switching to META-INF/services/
, I would suppose that your build incl.因此,鉴于您可以在 Gradle 构建中访问 Groovy 方法作为非扩展方法,查看问题的 rest 并切换到您的构建META-INF/services/
我会假设您的构建/ the publication should be configured correctly, too.发布也应该正确配置。
The following two minimal reproducer projects show that the publication works, that the library can be used in a Groovy application and that it fails in a Gradle build.以下两个最小复制器项目表明该出版物有效,该库可用于 Groovy 应用程序,并且在 Gradle 构建中失败。 Gradle Wrapper files are not shown. Gradle 包装文件未显示。
├── my_extension_lib
│ ├── build.gradle
│ └── src
│ └── main
│ ├── groovy
│ │ └── MyExtension.groovy
│ └── resources
│ └── META-INF
│ └── services
│ └── org.codehaus.groovy.runtime.ExtensionModule
└── my_groovy_app
├── build.gradle
└── src
└── main
└── groovy
└── Test.groovy
First run ./gradlew publish
under my_extension_lib/
.首先在my_extension_lib/
下运行./gradlew publish
。 Then run ./gradlew run
under my_groovy_app
.然后在my_groovy_app
下运行./gradlew run
。
my_extension_lib/build.gradle
plugins {
id 'groovy'
id 'maven-publish'
}
group = 'com.example'
version = '1.0'
repositories {
jcenter()
}
dependencies {
implementation 'org.codehaus.groovy:groovy-all:2.4.15'
}
publishing {
publications {
library(MavenPublication) {
from components.java
}
}
repositories {
maven {
name = 'test'
url = 'file:///tmp/my_test_repo'
}
}
}
my_extension_lib/src/main/groovy/MyExtension.groovy
class MyExtension {
static boolean containsIgnoreCase(String self, String str) {
self.toLowerCase().contains(str.toLowerCase())
}
}
my_extension_lib/src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
moduleName=My Test
moduleVersion=1.0
extensionClasses=MyExtension
my_groovy_app/build.gradle
/* Test extension in Gradle */
buildscript {
repositories {
maven {
url = 'file:///tmp/my_test_repo'
}
jcenter()
}
dependencies {
classpath 'com.example:my_extension_lib:1.0'
}
}
// works, i.e., the publication is ok
println(MyExtension.containsIgnoreCase("foobar", "Foo"))
// doesn’t work due to Gradle bug
//println("foobar".containsIgnoreCase('Foo'))
/* Test extension in Groovy application */
apply plugin: 'groovy'
apply plugin: 'application'
application {
mainClass = 'Test'
}
repositories {
maven {
url = 'file:///tmp/my_test_repo'
}
jcenter()
}
dependencies {
implementation 'org.codehaus.groovy:groovy-all:2.4.15'
implementation 'com.example:my_extension_lib:1.0'
}
my_groovy_app/src/main/groovy/Test.groovy
class Test {
static void main(String... args) {
// works, i.e., extension library JAR was published correctly
println("foobar".containsIgnoreCase('Foo'))
}
}
not really and answer - just another portion of information.不是真的和回答 - 只是信息的另一部分。
groovy version 2.5+ takes care about both: META-INF/services
and META-INF/groovy
groovy 2.5+ 版同时兼顾: META-INF/services
和META-INF/groovy
link: https://github.com/apache/groovy/blob/1c358e84e427b3a6ff808533a93e1d76f4fa0d67/src/main/java/org/codehaus/groovy/runtime/m12n/ExtensionModuleScanner.java#L42链接: https://github.com/apache/groovy/blob/1c358e84e427b3a6ff808533a93e1d76f4fa0d67/src/main/java/org/codehaus/groovy/runtime/m12n/ExtensionModuleScanner.java#L42
before groovy 2.5 only META-INF/services
was processed在 groovy 2.5 之前,仅处理META-INF/services
groovy scans resources named org.codehaus.groovy.runtime.ExtensionModule
only once when statically creating MetaClassRegistry singleton in GroovySystem.java groovy scans resources named org.codehaus.groovy.runtime.ExtensionModule
only once when statically creating MetaClassRegistry singleton in GroovySystem.java
link: https://github.com/apache/groovy/blob/1c358e84e427b3a6ff808533a93e1d76f4fa0d67/src/main/java/groovy/lang/GroovySystem.java#L37链接: https://github.com/apache/groovy/blob/1c358e84e427b3a6ff808533a93e1d76f4fa0d67/src/main/java/groovy/lang/GroovySystem.java#L37
to implement it somehow using this approach check how it's done in GrapeIvy.groovy class使用这种方法以某种方式实现它检查它是如何在 GrapeIvy.groovy class 中完成的
link: https://github.com/apache/groovy/blob/1c358e84e427b3a6ff808533a93e1d76f4fa0d67/src/main/groovy/groovy/grape/GrapeIvy.groovy#L292链接: https://github.com/apache/groovy/blob/1c358e84e427b3a6ff808533a93e1d76f4fa0d67/src/main/groovy/groovy/grape/GrapeIvy.groovy#L292
I think it's much easier to create a plugin for gradle and use metaClass to define custom methods我认为为 gradle 创建插件并使用 metaClass 定义自定义方法要容易得多
String.metaClass.up={ delegate.toUpperCase() }
task x{
doLast{
println( "hello".up() )
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.