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

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

问题描述

我有一个典型的Antlr 4.5项目,有两个语法文件:MyLexer.g4和MyParser.g4。 Antlr从它们生成6个输出文件:MyLexer.java,MyLexer.tokens,MyParser.java,MyParser.tokens,MyParserBaseListener.java和MyParserListener.java。毕业生任务都正常工作,以便输出文件全部按预期生成,编译和测试。



问题是,毕业生会将6个目标文件视为一直所以每个运行或调试会话都必须重新生成它们,因此即使没有源文件发生变化,也必须重新编译主java项目。



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



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

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

任务makeAntlrOutputDir<<< {
file(antlr4.destinationDir).mkdirs()
}

任务compileAntlrGrammars(类型:JavaExec,dependsOn:makeAntlrOutputDir){
//语法方便排序按字母顺序排列。我认为这将是真的。
//确保名为* Lexer.g4的文件被列出,因此在对应的* Parser.g4
//之前进行处理。这很重要,因为Lexer必须先被处理,因为Parser需要.tokens文件从Lexer。
//另请注意,组合语法的输出文件命名约定与单独的Lexer和Parser语法略有不同。
def grammars = fileTree(antlr4.antlrSource).include('** / *。g4')
def target = file($ {antlr4.destinationDir})
inputs.files语法
// TODO:此输出规范不正确,因此该任务从不被视为最新的。
// TODO:调整输出集合,使其组合语法以及单独的Lexer和Parser语法是正确的。
outputs.dir target

main ='org.antlr.v4.Tool'
classpath = configurations.antlr4
// Antlr命令行参数在https: //theantlrguy.atlassian.net/wiki/display/ANTLR4/ANTLR+Tool+Command+Line+Options
args = [-o,target,
-lib,target,
// - listener,// - listener是默认的
// - no-visitor,// - no-visitor是默认的
-package ,
grammars.files
] .flatten()

//包含可选描述和组(由./gradlew任务命令显示)
描述='从ANTLR4语法生成Java源'
group ='Build'
}

compileJava {
dependsOn compileAntlrGrammars
//下一行isn在技​​术上需要,除非antdr4.destinationDir不在buildDir,但它不会伤害
源antlr4.destinationDir
}

任务cleanAntlr {
删除antlr4.destinationDir
}
clean.dependsOn cleanAntlr
pre>

解决方案

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



最初,任务定义为:

 任务cleanAntlr {
delete antlr4.destinationDir
}
clean .dependsOn cleanAntlr

解决方案是定义如下:(注意<)在任务名称之后。)

  task cleanAntlr<< {
删除antlr4.destinationDir
}
clean.dependsOn cleanAntlr

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

  task cleanAntlr {
doLast(){
//确保在doLast()中包含执行阶段代码。
//否则,它将在初始化或配置阶段运行,即使运行不相关的任务也是如此。
//当NetBeas IDE首次加载项目时,它也会运行。
// println'删除Antlr目录:'+ antlr4.destinationDir
delete antlr4.destinationDir
}
}
clean.dependsOn cleanAntlr

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

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


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.

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.

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.

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

解决方案

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.

Originally, the task was defined as:

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

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

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

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