如何在 jar 中包含 Groovy 扩展方法?

Phi*_*hil 5 java groovy extension-methods gradle maven-publish

我有一个 groovy 库,我将其作为 jar 文件发布在 Nexus 存储库上。当我使用另一个项目的 Gradle 脚本中的库中的扩展方法时,我收到MissingMethodException

作为参考,假设我有一个 String 扩展方法,例如:

static boolean containsIgnoreCase(String self, String str) {
    self.toLowerCase().contains(str.toLowerCase())
}
Run Code Online (Sandbox Code Playgroud)

如果在我的库中使用调用该方法"foobar".containsIgnoreCase("Foo"),我将得到异常。如果我改为使用 来调用它StringExtensions.containsIgnoreCase("foobar", "Foo"),它就可以工作,没问题。

我的猜测是,这是在没有定义扩展的 META-INF 文件的情况下发布 Groovy 项目的问题。这是项目结构:

- Library
  - src/main/
    - groovy/ 
      - (here are my sources)
    - resources/META-INF/groovy
      - org.codehaus.groovy.runtime.ExtensionModule (contains details about my extension classes)
Run Code Online (Sandbox Code Playgroud)

我的 ExtensionModule 文件如下所示:

moduleName=string-extensions
moduleVersion=1.0
extensionClasses=com.my.project.StringExtensions
Run Code Online (Sandbox Code Playgroud)

在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
        }
    }

    //...
}
Run Code Online (Sandbox Code Playgroud)

当我在另一个项目的 Gradle 构建脚本中使用此库时,我需要在出版物中包含哪些内容才能正确注册扩展方法?我已经在构建脚本中添加了依赖项的类路径/存储库 URL,并且可以访问库的方法 - 这些方法在调用扩展方法时会失败。

Chr*_*iki 1

坏消息:Gradle 中有一个错误,导致 Gradle 构建脚本目前不支持 Groovy 扩展模块。

\n

需要注意的一件事: Groovy 文档似乎是错误的,因为他们指定将模块描述符放在META-INF/groovy/. 当我这样做时,我什至可以在普通的 Groovy 应用程序中使用生成的库的扩展方法。我必须把描述符放在下面META-INF/services/才能使其正常工作。

\n

这仍然对 Gradle 用例没有帮助。但是,它表明构建和发布工作正常:FWIW,我自己刚刚设置了两个小项目,我可以重现您所看到的问题(使用 Gradle 6.7.1)。通过提到的目录切换META-INF/services/,我至少可以让扩展在 Groovy 应用程序中工作。因此,鉴于您可以在 Gradle 构建中将 Groovy 方法作为非扩展方法访问,查看问题的其余部分并切换到META-INF/services/,我认为您的构建包括。发布也应该正确配置。

\n

最小复制项目

\n

以下两个最小的复制器项目表明该发布有效,该库可以在 Groovy 应用程序中使用,但在 Gradle 构建中失败。Gradle Wrapper 文件未显示。

\n
\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 my_extension_lib\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 build.gradle\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 src\n\xe2\x94\x82\xc2\xa0\xc2\xa0     \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 main\n\xe2\x94\x82\xc2\xa0\xc2\xa0         \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 groovy\n\xe2\x94\x82\xc2\xa0\xc2\xa0         \xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 MyExtension.groovy\n\xe2\x94\x82\xc2\xa0\xc2\xa0         \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 resources\n\xe2\x94\x82\xc2\xa0\xc2\xa0             \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 META-INF\n\xe2\x94\x82\xc2\xa0\xc2\xa0                 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 services\n\xe2\x94\x82\xc2\xa0\xc2\xa0                     \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 org.codehaus.groovy.runtime.ExtensionModule\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 my_groovy_app\n    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 build.gradle\n    \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 src\n        \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 main\n            \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 groovy\n                \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 Test.groovy\n
Run Code Online (Sandbox Code Playgroud)\n

首先./gradlew publishmy_extension_lib/. 然后跑./gradlew runmy_groovy_app

\n my_extension_lib/build.gradle\n
plugins {\n    id \'groovy\'\n    id \'maven-publish\'\n}\n\ngroup = \'com.example\'\nversion = \'1.0\'\n\nrepositories {\n    jcenter()\n}\n\ndependencies {\n    implementation \'org.codehaus.groovy:groovy-all:2.4.15\'\n}\n\npublishing {\n    publications {\n        library(MavenPublication) {\n            from components.java\n        }\n    }\n    repositories {\n        maven {\n            name = \'test\'\n            url = \'file:///tmp/my_test_repo\'\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n my_extension_lib/src/main/groovy/MyExtension.groovy\n
class MyExtension {\n\n    static boolean containsIgnoreCase(String self, String str) {\n        self.toLowerCase().contains(str.toLowerCase())\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n my_extension_lib/src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule\n
plugins {\n    id \'groovy\'\n    id \'maven-publish\'\n}\n\ngroup = \'com.example\'\nversion = \'1.0\'\n\nrepositories {\n    jcenter()\n}\n\ndependencies {\n    implementation \'org.codehaus.groovy:groovy-all:2.4.15\'\n}\n\npublishing {\n    publications {\n        library(MavenPublication) {\n            from components.java\n        }\n    }\n    repositories {\n        maven {\n            name = \'test\'\n            url = \'file:///tmp/my_test_repo\'\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n my_groovy_app/build.gradle\n
/* Test extension in Gradle */\nbuildscript {\n    repositories {\n        maven {\n            url = \'file:///tmp/my_test_repo\'\n        }\n        jcenter()\n    }\n    dependencies {\n        classpath \'com.example:my_extension_lib:1.0\'\n    }\n}\n\n// works, i.e., the publication is ok\nprintln(MyExtension.containsIgnoreCase("foobar", "Foo"))\n// doesn\xe2\x80\x99t work due to Gradle bug\n//println("foobar".containsIgnoreCase(\'Foo\'))\n\n\n/* Test extension in Groovy application */\n\napply plugin: \'groovy\'\napply plugin: \'application\'\n\napplication {\n    mainClass = \'Test\'\n}\n\nrepositories {\n    maven {\n        url = \'file:///tmp/my_test_repo\'\n    }\n    jcenter()\n}\n\ndependencies {\n    implementation \'org.codehaus.groovy:groovy-all:2.4.15\'\n    implementation \'com.example:my_extension_lib:1.0\'\n}\n
Run Code Online (Sandbox Code Playgroud)\n my_groovy_app/src/main/groovy/Test.groovy\n
class Test {\n\n  static void main(String... args) {\n      // works, i.e., extension library JAR was published correctly\n      println("foobar".containsIgnoreCase(\'Foo\'))\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n