Gradle任务:替换.java文件中的字符串

34

我希望在代码编译之前替换Config.java文件中的几行。我所能找到的只是在复制文件时通过过滤器解析文件。一旦我必须将其复制,我就必须将其保存在某个地方 - 这就是为什么我采用这种解决方案:将其复制到临时位置,并同时替换行 > 删除原始文件 > 将复制的文件复制回原始位置 > 删除临时文件。是否有更好的解决方案?

4个回答

31

也许你应该尝试类似于Ant的replaceregexp

task myCopy << {
    ant.replaceregexp(match:'aaa', replace:'bbb', flags:'g', byline:true) {
        fileset(dir: 'src/main/java/android/app/cfg', includes: 'TestingConfigCopy.java')
    }
}

这个任务将会把所有的 aaa 替换成 bbb。不过,这只是一个例子,您可以根据自己的目的修改它或尝试一些类似于ant的解决方案。


3
请参考以下另一种方法: https://dev59.com/nWQm5IYBdhLWcg3wowIF#17572644。 - marciowb

29
为了补充 lance-java 的回答,如果你只想更改一个值,我发现这个成语更简单:
task generateSources(type: Copy) {
    from 'src/replaceme/java'
    into "$buildDir/generated-src"
    filter { line -> line.replaceAll('xxx', 'aaa') }
}

注意:请记住,Copy任务仅在源文件更改时运行。如果您希望根据其他条件进行替换,请使用Gradle的增量构建功能来指定。


2
小心,我们遭受了巨大的打击。我们使用 -Pversion={version} 运行,但是该目标一直说源文件没有被修改,因此我们的新版本从未被放入文件中。这对许多事情都有效,但不适用于版本控制。我们曾经吃过苦头。 - Dean Hiller
@DeanHiller 感谢您的评论。我不确定我是否完全理解您的意思。您是说您有一些源文件没有更改,因此Gradle没有按照您想要的方式“更改”它们,即使它应该更改其中的“版本”吗? - Vic Seedoubleyew
我们尝试使用该方法进行版本控制,但如果源文件没有更改,它实际上根本不会运行,并且不会进行任何替换,因此在从开发构建切换到发布构建时,我们的输出文件中将出现“version-token-to-be-replaced”。目前还不确定最佳方法是什么...也许只需生成文件即可。 - Dean Hiller
我会在我的回答中加上这个警告。请随意在其他答案上发表相同的评论,因为它们都涉及到这个特殊情况。 - Vic Seedoubleyew
我可以告诉你,“filter”是有问题的。它会导致我出现“java.lang.ClassFormatError: Incompatible magic value 4022320623 in class file...”的错误。 - Alexis Evelyn
显示剩余2条评论

14
  1. 我绝对不会覆盖原始文件。
  2. 我更喜欢按目录而非文件名来组织事物,所以如果是我,我会把Config.java放在它自己的文件夹里(例如:src/replaceme/java)。
  3. 我会在$buildDir下创建一个generated-src目录,这样它就可以通过clean任务删除。

您可以使用复制任务和ReplaceTokens过滤器。例如:

apply plugin: 'java'
task generateSources(type: Copy) {
    from 'src/replaceme/java'
    into "$buildDir/generated-src"
    filter(ReplaceTokens, tokens: [
        'xxx': 'aaa', 
        'yyy': 'bbb'
    ])
}
// the following lines are important to wire the task in with the compileJava task
compileJava.source "$buildDir/generated-src"
compileJava.dependsOn generateSources

我已经使用了replace方法,它运行得非常完美,但是一旦我手动编辑Config.java文件,可能会出现一些不需要的状态,所以你的解决方案似乎更好。compileJava.source = [files]到底是做什么的?它如何知道我想要替换'com.example.cfg'包中的Config.java文件,而不是同名文件在'com.example.remote.cfg'包中的文件? - Srneczek
正如我所说,我更喜欢使用目录而不是特定的文件名模式。因此,在我的示例中,src/replaceme/java 中的所有内容都将被替换,而不管包是什么。compileJava.source generateSources.outputs.files 调用 JavaCompile.source(...),最终将生成的源代码添加到普通源文件(在 src/main/java 中)旁边进行编译。在我的建议中没有 "=" 符号。 - lance-java

1
如果您确实希望通过使用临时文件策略来覆盖原始文件,则以下操作将创建过滤后的文件,将其复制到原始文件中,然后删除临时文件。
task copyAtoB(dependsOn: [existingTask]) {
    doLast {
        copy {
            from("folder/a") {
                include "*.java"
            }
            // Have to use a new path for modified files
            into("folder/b")
            filter {
                String line ->
                    line.replaceAll("changeme", "to this")
            }
        }
    }
}

task overwriteFilesInAfromB(dependsOn: [copyAtoB]) {
    doLast {
        copy {
            from("folder/b") {
                include "*.java"
            }
            into("folder/a")
        }
    }
}

// Finally, delete the files in folder B
task deleteB(type: Delete, dependsOn: overwriteFilesInAfromB) {
    delete("folder/b")
}

nextTask.dependsOn(deleteB)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接