【问题标题】:How do I declare gradle Antlr task output specs to avoid unnecessary rebuilds如何声明 gradle Antlr 任务输出规范以避免不必要的重建
【发布时间】:2015-04-22 17:38:41
【问题描述】:

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

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

生成文件的 gradle 任务将输出规范定义为生成 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"
]

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

【问题讨论】:

    标签: java gradle dependencies antlr


    【解决方案1】:

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

    最初,任务被定义为:

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

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

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

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

    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/current/userguide/more_about_tasks.html 的第 15.9.2 节中有很好的解释。

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

    【讨论】:

    【解决方案2】:

    请你试试下面的代码:

    generatedFiles = ['MyLexer.java',] // and so on..
    generatedFiles.each { f -> outputs.file("$target/$f") }
    

    【讨论】:

    • 感谢您的回复。您的代码最终并没有解决问题,但它确实完成了我最初要求的操作,即它定义了单个输出文件而不是文件夹。正如我在发布的答案中解释的那样,这不是真正的问题。我最终不需要这段代码,但我认为我必须稍微调整语法才能让它运行,然后才能最终删除它。我认为正确编译的语法是这样的: generateFiles = ['MyLexer.java',] // 等等.. generatedFiles.each { f -> outputs.file("{$target}/{$f} ") }
    猜你喜欢
    • 2017-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-23
    • 1970-01-01
    • 2019-08-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多