如何声明 gradle Antlr 任务输出规范以避免不必要的重建 [英] How do I declare gradle Antlr task output specs to avoid unnecessary rebuilds

查看:36
本文介绍了如何声明 gradle Antlr 任务输出规范以避免不必要的重建的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个典型的 Antlr 4.5 项目,其中包含两个语法文件:MyLexer.g4 和 MyParser.g4.Antlr 从中生成 6 个输出文件:MyLexer.java、MyLexer.tokens、MyParser.java、MyParser.tokens、MyParserBaseListener.java 和 MyParserListener.java.gradle 任务都正常工作,因此输出文件都按预期生成、编译和测试.

I have a typical Antlr 4.5 project with two grammar files: MyLexer.g4 and MyParser.g4. From them, Antlr generates 6 output files: MyLexer.java, MyLexer.tokens, MyParser.java, MyParser.tokens, MyParserBaseListener.java and MyParserListener.java. The gradle tasks are all working correctly so that the output files are all generated, compiled and tested as expected.

问题是 gradle 认为 6 个目标文件总是过时的,因此每次运行或调试会话都必须重新生成它们,因此即使没有任何源文件发生更改,也必须重新编译主 Java 项目.

The problem is that gradle sees the 6 target files as always being out of date, so every run or debug session has to regenerate them and therefore has to recompile the main java project even if none of the source files have changed.

生成文件的 gradle 任务将输出规范定义为生成 6 个输出文件的文件夹.我认为我需要一种方法来将其定义为 6 个特定文件而不是输出文件夹.我只是不知道这样做的语法.

The gradle task which generates the file has the output spec defined as the folder into which the 6 output files are generated. I think that I need a way to define it as being the 6 specific files rather than the output folder. I just don't know the syntax to do that.

这是我的 build.gradle 文件的相关部分:

Here's the pertinent part of my build.gradle file:

ext.antlr4 = [
    antlrSource:    "src/main/antlr",
    destinationDir: "src/main/java/com/myantlrquestion/core/antlr/generated",
    grammarpackage:               "com.myantlrquestion.core.antlr.generated"
]

task makeAntlrOutputDir << {
    file(antlr4.destinationDir).mkdirs()
}

task compileAntlrGrammars(type: JavaExec, dependsOn: makeAntlrOutputDir) {
    // Grammars are conveniently sorted alphabetically. I assume that will remain true.
    // That ensures that files named *Lexer.g4 are listed and therefore processed before the corresponding *Parser.g4
    // It matters because the Lexer must be processed first since the Parser needs the .tokens file from the Lexer.
    // Also note that the output file naming convention for combined grammars is slightly different from separate Lexer and Parser grammars.
    def grammars = fileTree(antlr4.antlrSource).include('**/*.g4')
    def target = file("${antlr4.destinationDir}")
    inputs.files grammars
    // TODO: This output spec is incorrect, so this task is never considered up to date.
    // TODO: Tweak the outputs collection so it is correct with combined grammars as well as separate Lexer and Parser grammars.
    outputs.dir target

    main = 'org.antlr.v4.Tool'
    classpath = configurations.antlr4
    // Antlr command line args are at https://theantlrguy.atlassian.net/wiki/display/ANTLR4/ANTLR+Tool+Command+Line+Options
    args = ["-o", target,
            "-lib", target,
            //"-listener",      //"-listener" is the default
            //"-no-visitor",    //"-no-visitor" is the default
            "-package", antlr4.grammarpackage,
            grammars.files 
    ].flatten()

    // include optional description and group (shown by ./gradlew tasks command)
    description = 'Generates Java sources from ANTLR4 grammars.'
    group       = 'Build'
}

compileJava {
    dependsOn compileAntlrGrammars
    // this next line isn't technically needed unless the antlr4.destinationDir is not under buildDir, but it doesn't hurt either
    source antlr4.destinationDir
}

task cleanAntlr {
    delete antlr4.destinationDir
}
clean.dependsOn cleanAntlr

推荐答案

我发现问题不是目标文件过期了,而是由于 cleanAntlr 任务中的错误,它们每次都被删除运行任何 gradle 任务.问题是在 gradle 的初始化和配置阶段,cleanAntlr 中的所有代码都在运行,即使 cleanAntlr 任务本身没有被执行.

I discovered the problem was not that the target files were out of date, but rather, due to a bug in the cleanAntlr task, they were being deleted every time that any gradle task was run. The problem was that all of the code in cleanAntlr was being run during gradle's initialization and configuration phase, even if the cleanAntlr task itself wasn't being executed.

最初,任务定义为:

task cleanAntlr {
    delete antlr4.destinationDir
}
clean.dependsOn cleanAntlr

解决方案是这样定义它:(注意任务名称后面的<<".)

The solution was to define it like this: (Note the "<<" after the task name.)

task cleanAntlr << {
    delete antlr4.destinationDir
}
clean.dependsOn cleanAntlr

... 或者,为了更清楚,使用这个更冗长但功能等效的任务定义:

... or, for additional clarity, use this more verbose, but functionally equivalent task definition:

task cleanAntlr {
    doLast() {
        // Be sure to wrap the execution phase code inside doLast(). 
        // Otherwise it will run during the initialization or configuration phase, even when an unrelated task is is run.
        // It would also run when the NetBeas IDE first loaded the project.
        //println 'Deleting Antlr Directory: ' + antlr4.destinationDir
        delete antlr4.destinationDir
    }
}
clean.dependsOn cleanAntlr

修复了该错误后,compileAntlrGrammars 任务的原始输出规范可以正常工作.无需指定每个单独的输出文件.这在 https://gradle.org/docs/的第 15.9.2 节中有很好的解释current/userguide/more_about_tasks.html.

With that bug fixed, the original outputs specification for the compileAntlrGrammars task works correctly. There is no need to specify each individual output file. This is explained quite well in section 15.9.2 of https://gradle.org/docs/current/userguide/more_about_tasks.html.

def grammars = fileTree(antlr4.antlrSource).include('**/*.g4')
def target = file("${antlr4.destinationDir}")
inputs.files grammars
outputs.dir target

这篇关于如何声明 gradle Antlr 任务输出规范以避免不必要的重建的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆