Заменить токен в файле перед его созданием, но сохранить токен в источниках

Я хочу заменить токен @[email protected] в исходном java файле версией перед сборкой (Gradle - моя предпочтительная система сборки).

В моем текущем скрипте ant.replace(file: 'src/main/java/randers/notenoughvocab/main/Reference.java', token: '@[email protected]', value: version) он заменяет вхождения @[email protected] в фактический исходный файл, поэтому после сборки все вхождения шаблона были заменены версией, и если я изменю версию, файл сборки gradle больше не найдет там никаких шаблонов, и версия не обновится.

Я также видел задачу здесь, но я не понимаю, какие значения должны применяться для моего конкретного проекта.

Макет проекта для моего проекта, если это необходимо:
Screenshot of file layout taken from IntelliJ IDEA 14.1.2, Dark theme

Мой файл сборки gradle: просмотр на github

Ответ 1

Вам нужно заменить токены @[email protected], прежде чем публиковать свое программное обеспечение. Здесь я определил задачу compileForRelease, которая ее выполняет:

import org.apache.tools.ant.filters.ReplaceTokens

task sourcesForRelease(type: Copy) {
    from 'src/main/java'
    into 'build/adjustedSrc'
    filter(ReplaceTokens, tokens: [VERSION: '2.3.1'])
}

task compileForRelease(type: JavaCompile, dependsOn: sourcesForRelease) {
    source = sourcesForRelease.destinationDir
    classpath = sourceSets.main.compileClasspath
    destinationDir = file('build/adjustedClasses')
}

Я не рекомендую возиться со стандартными задачами, определенными плагином Java, потому что это добавит лишние накладные расходы для каждой сборки.

Ответ 2

ПРЕДУПРЕЖДЕНИЕ: Как указано в комментариях исходным кодом фильтрации @Raffaele, могут возникнуть серьезные проблемы. Этот ответ предполагает, что вы хорошо знаете, что вы делаете, и осознаете потенциальные проблемы, которые могут возникнуть.

Проблема заключается в том, что исходные файлы java не копируются - они только скомпилированы - на месте. Поэтому вам нужно:

  • Перед компиляцией - скопируйте файл, содержащий @[email protected]
  • Отфильтровать файл.
  • Compile
  • Восстановить исходный файл.

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

apply plugin: 'java'

version = '0.0.1'
group = 'randers.notenoughvocab'
archivesBaseName = 'NotEnoughVocab'

def versionFile = 'src/main/java/randers/notenoughvocab/main/Reference.java'
def tempDir = 'build/tmp/sourcesCache'
def versionFileName = 'Reference.java'

compileJava.doFirst {
    copy {
        from(versionFile)
        into(tempDir)
    }
    ant.replace(file: versionFile, token: '@[email protected]', value: version)
}

compileJava.doLast {
    copy {
        from(tempDir + '/' + versionFileName)
        into(project.file(versionFile).parent)
    }
}

Ответ 3

Я столкнулся с той же проблемой. И я не смог найти рабочее решение. Ни один из примеров, которые я нашел, не работал. Я считаю, что это может быть фактом, что я новичок в Gradle, и эти примеры опускают некоторые очевидные фрагменты кода. Поэтому я просмотрел Gradle и нашел свое собственное решение, которое я хотел бы поделиться:

import org.apache.tools.ant.filters.ReplaceTokens

// Change compiler source directory
sourceSets {
    main {
        java {
            srcDirs = ['build/src/main/java']
        }
    }
}

// Prepare sources for compilation
task prepareSources(type: Copy) {
    from('src/main/java')
    into('build/src/main/java')
    filter(ReplaceTokens, tokens: [pluginVersion: version])
}

// Prepare sources, before compile
compileJava {
    dependsOn prepareSources
}

Ответ 4

Я нашел некоторые ответы несколько неудовлетворительными, поэтому вот мое решение:

import org.apache.tools.ant.filters.ReplaceTokens

task processSource(type: Sync) {
    from sourceSets.main.java
    inputs.property 'version', version
    filter(ReplaceTokens, tokens: [VERSION: version])
    into "$buildDir/src"
}

compileJava {
    source = processSource.outputs
}

Это решает различные проблемы следующим образом:

  1. В отличие от ответа @Opal, основной источник остается неиспорченным; вместо этого он ставится с изменениями в $buildDir/src с processSource задачи processSource, которая отражает стандартные processResources.
  2. В отличие от ответа @Gregory Stachowiak, sourceSets.main.java.srcDirs остается значением по умолчанию, и в определении местоположения, которое (пока) не существует, нет ловкости рук
  3. В отличие от ответа @Raffaele, не существует отдельной задачи для выпуска против других сборок. Я не согласен с тем, что разделение их желательно; Я думаю, что дополнительная сложность не стоит того, если вы не измерили какой-либо удар по производительности и не нашли его неприемлемым. Прежде чем перейти к решению @Raffaele, я бы даже, например, предпочел ограничить область действия filter шаблонами include/exclude.
  4. Зависимости задачи неявно определяются через выходные данные.
  5. Все местоположения взяты из значений по умолчанию, и ничего не напечатано строго. Единственное магическое значение здесь - это src, каталог в $buildDir куда $buildDir обработанные исходные файлы.
  6. (Изменение: добавлено 2019/1/12) Другие ответы неправильно обрабатывают ситуации, когда изменилась только версия. Изменение версии само по себе должно сделать недействительным вывод задания. Это достигается с помощью inputs.property.
  7. (Изменить 2019/5/20) Использует Sync а не Copy чтобы файлы, удаленные из источника, также удалялись из отфильтрованного источника (спасибо, @Earthcomputer).

Ответ 5

В дополнение к другим ответам я нашел эту идиому более простой, если есть только одно значение, которое вы хотите изменить:

task generateSources(type: Copy) {
    from 'src/main/java'
    into 'build/src/main/java'
    filter { line -> line.replaceAll('xxx', 'aaa') }
}