【问题标题】:Setting compiler args as list of string in new language plugin将编译器参数设置为新语言插件中的字符串列表
【发布时间】:2025-12-04 11:45:02
【问题描述】:

我正在为一种新语言开发一个插件,并且我正在尝试向编译器添加对编译选项的支持。我以org.gradle.api.tasks.compile.CompileOptions 类为起点,实现了我自己的类,如下所示:

class SvCompileOptions extends AbstractOptions {
    private List<String> compilerArgs = Lists.newArrayList();

    @Input
    public List<String> getCompilerArgs() {
        return compilerArgs;
    }

    public void setCompilerArgs(List<String> compilerArgs) {
        this.compilerArgs = compilerArgs;
    }
}

在我的 build.gradle 文件中,我尝试执行以下操作:

compileSv {
    options.compilerArgs += [ "-foo" ]
}

(compileSv 是一个具有 SvCompileOptions 类型的 options 属性的任务。)

我收到以下错误:

A problem occurred evaluating project ':uvc2'.
> java.lang.AbstractMethodError (no error message)

如果我将这一行替换为:

compileSv {
    options.compilerArgs.add("-foo")
}

然后一切正常,但不是很渐变。

谁能指出我做错了什么?


根据@tim_yates 的建议,我添加了一个附加到compilerArgs 的函数:

class SvCompileOptions extends AbstractOptions {

    void compilerArgs(String... args) {
        this.compilerArgs.addAll(args as List)
    }
}

根据@Opal 的建议,我创建了一个准系统示例:

// File 'build.gradle'

buildscript {
    dependencies {
        classpath 'com.google.guava:guava:16+'
    }
    repositories {
        mavenCentral()
    }
}

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;


class SvCompileOptions extends AbstractOptions {

    private List<String> compilerArgs = Lists.newArrayList();

    @Input
    public List<String> getCompilerArgs() {
        return compilerArgs;
    }

    public void setCompilerArgs(List<String> compilerArgs) {
        this.compilerArgs = compilerArgs;
    }

    void compilerArgs(String... args) {
        this.compilerArgs.addAll(args as List)
    }

}


class SvCompile extends DefaultTask {

    @TaskAction
    protected void compile() {
        println options.compilerArgs
    }

    @Nested
    SvCompileOptions options = new SvCompileOptions()

}


task compileSv(type: SvCompile)


compileSv {
    options.compilerArgs 'foo', 'bar'
}

代码将参数附加到空列表并按预期打印[foo, bar]。如果我们尝试使用以下内容覆盖参数:

compileSv {
    options.compilerArgs = ['one', 'two']
}

打印一条错误消息:

* What went wrong:
A problem occurred evaluating root project 'so_compile_options2'.
> SvCompileOptions.setProperty(Ljava/lang/String;Ljava/lang/Object;)V

我不确定为什么在build.gradle 中内联类时错误消息会有所不同,但我认为这是导致我看到的AbstractMethodError 的原因。

正如@Opal 所指出的,这个问题是由AbstractOptions 类中的一些魔法引起的。我尝试将以下方法添加到编译选项类中,但错误消息仍然存在:

class SvCompileOptions extends AbstractOptions {

    private static final ImmutableSet<String> EXCLUDE_FROM_ANT_PROPERTIES =
            ImmutableSet.of("compilerArgs");

    @Override
    protected boolean excludeFromAntProperties(String fieldName) {
        return EXCLUDE_FROM_ANT_PROPERTIES.contains(fieldName);
    }

    // ...
}

exclude 函数似乎根本没有被调用,就好像我在其中添加了一个虚拟打印它永远不会发出一样。

【问题讨论】:

  • 您能提供一个SSCCE吗?并使用-s 开关运行脚本?
  • 如果你不扩展 AbstractOptions 它会按预期工作,所以我猜这门课引入了一些 magic - 现在不能帮助你 - 必须做我的自己的工作。
  • @Opal 感谢有关AbstractOptions 的提示。 SSCCE 是什么意思?
  • 这只是一个工作示例:sscce.org :)
  • @Opal 我很遗憾没有提供示例,但是当我发布问题时我没有可用的代码/基础设施。我希望这是一个简单的问题,有经验的 Gradle 用户可以立即发现。我稍后会更新问题。

标签: gradle groovy gradle-plugin


【解决方案1】:

这应该可以工作

options.compilerArgs << "-foo"

如果您想添加多个值(您可能会在某个时候这样做),这样做的惯用方法是将您的类更改为:

class SvCompileOptions extends AbstractOptions {
    @Input
    List<String> compilerArgs = Lists.newArrayList();

    def args(String... args) {
        this.compilerArgs += args.toList()
    }
}

那么,你可以这样做:

compileSv {
    options.args "-foo", "-bar"
}

【讨论】:

  • 确实如此。感谢您的建议,我也会添加类似的内容。不过,这仍然不能解释错误。
  • 尽管蒂姆有效,但它看起来像是一种解决方法。
  • @TudorTimi 如果您只是删除 getter 和 setter 并注释该字段,它是否有效?
  • @tim_yates 注释字段是什么意思? (新手问题)
  • 将注释放在字段上,而不是注释方法(如我的回复中),如果您删除 private 您可以删除 getter 和 setter 方法,因为 groovy 会为您生成它们跨度>
【解决方案2】:

@Opal 提供了宝贵的见解,即从 AbstractOptions 扩展是问题的根源,并在 Gradle Github 页面上找到与 removing the AbstractOptions class altogether 相关的问题后,我认为最好的办法就是不要使用基类。

在整合 @tim_yates 对 'append' 方法的建议后,我将采用以下实现:

class SvCompileOptions {

    private List<String> compilerArgs = Lists.newArrayList();

    @Input
    public List<String> getCompilerArgs() {
        return compilerArgs;
    }

    // Overwrites
    public void setCompilerArgs(List<String> compilerArgs) {
        this.compilerArgs = compilerArgs;
    }

    // Appends
    void compilerArgs(String... args) {
        this.compilerArgs.addAll(args as List)
    }

}

这允许用户附加参数:

compileSv {
    options.compilerArgs 'foo', 'bar'
}

并覆盖它们:

compileSv {
    options.compilerArgs = ['one', 'two']
}

【讨论】: