Хобрук: Ваш путь к мастерству в программировании

Как объявить спецификации вывода задачи Gradle Antlr, чтобы избежать ненужных перестроек

У меня есть типичный проект 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

Ответы:


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 работает правильно. Нет необходимости указывать каждый отдельный выходной файл. Это довольно хорошо объясняется в разделе 15.9.2 документа 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
26.04.2015
  • Это решение также решило проблему, описанную мной в stackoverflow.com/questions/29802668/ 26.04.2015

  • 2

    Не могли бы вы попробовать следующий фрагмент кода:

    generatedFiles = ['MyLexer.java',] // and so on..
    generatedFiles.each { f -> outputs.file("$target/$f") }
    
    23.04.2015
  • Спасибо за ответ. ваш код в конечном итоге не устранил проблему, но он выполнил то, что я изначально просил, а именно, он определил отдельные выходные файлы вместо папки. Как я объясняю в опубликованном мной ответе, это не было настоящей проблемой. В итоге этот код мне не понадобился, но я думаю, что мне пришлось немного подправить синтаксис, чтобы заставить его работать, прежде чем я окончательно его удалил. Я думаю, что синтаксис, который скомпилирован правильно, был примерно таким: createdFiles = ['MyLexer.java',] // и так далее .. createdFiles.each {f - ›outputs.file ({$ target} / {$ f}) } 26.04.2015
  • Новые материалы

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..

    Настольный ПК как «одно кольцо, чтобы править всеми» домашних компьютеров
    Вид после 9 месяцев использования С настольных компьютеров все началось, но в какой-то момент они стали «серверами», и мы все перешли на ноутбуки. В прошлом году я столкнулся с идеей настольных..

    Расширенные методы безопасности для VueJS: реализация аутентификации без пароля
    Руководство, которое поможет вам создавать безопасные приложения в долгосрочной перспективе Безопасность приложений часто упускается из виду в процессе разработки, потому что основная..

    стройный-i18следующий
    Представляем стройную оболочку для i18next. Эта библиотека, основанная на i18next, заключает экземпляр i18next в хранилище svelte и отслеживает события i18next, такие как languageChanged,..

    Обзор 20 основных и современных методов работы с массивами в JavaScript
    Вы знаете их всех? В этом коротком посте я покажу сводку методов, доступных в JavaScript для работы с массивами. Я надеюсь, что вы найдете это полезным! В конце поста вы найдете ссылку на..

    Да, но я чувствую необходимость указать, что это или не единственные два.
    Да, но я чувствую необходимость указать, что это или не единственные два. Обучение с подкреплением (в качестве примера) также является важным.