Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I apply a patch file in Gradle?

Tags:

patch

gradle

I have a Gradle build script that successfully builds my project and compiles all the artifacts I need.

However, in a couple of cases I'd like to give other developers the option to patch some of the files. For example, in one of the archives there's an xml file with information about database hooks - some of the devs use other versions (or even engines) and need to change these before they can use the build output.

Instead of having them make changes to a version-controlled file, which they might commit by mistake, I'd like to give them the option to have a local, individual patch file which the build script applies.

In an old ant script, we did something like this

<target name="appcontext-patch" if="applicationContext.patch.file">
    <patch patchfile="${applicationContext.patch.file}" originalfile="${dist.dir}/applicationContext.xml"/>
</target>

but I can't figure out how to do the equivalent in Gradle. Is there a better (i.e. more idiomatic) way of doing this than trying to directly convert this into a call to ant.patch?

Some context
This is how the file ends up in the archive in the first place:

into('META-INF') {
    from 'deployment', {
        include 'applicationContext.xml'
        rename { fn ->  "jboss-spring.xml" }
    }
}

It would be fantabulous if I could just do something like

into('META-INF') {
    from 'deployment', {
        include 'applicationContext.xml'
        rename { fn -> "jboss-spring.xml' }
        patch 'local/applicationContext.xml.patch'
    }
}

and have the patch file applied before the file is put in the archive. I don't mind writing some code to make this possible, but I'm quite new to Gradle and I have no idea where to begin.

like image 210
Tomas Aschan Avatar asked Jun 12 '13 11:06

Tomas Aschan


People also ask

What is gradle assemble command?

assemble will build your artifacts, and build will assemble your artifacts with additional checks. build depends on assemble , so build is sort of a superset of assemble. You can have a look on the tasks that will be executed by using the --dry-run flag. e.g. gradlew build --dry-run.

What are responsibilities of Gradle build file?

Gradle comes to your rescue whenever you wish to build an Android Project. It makes sure that you can generate an apk from . java and . xml files in the most efficient way possible.


2 Answers

If you'd like to do this more on the fly I can think of two main techniques. Both involve writing some code, but they may be more appealing to you and I'm pretty confident gradle doesn't have this behavior built-in anywhere.

Personally I think #1 is the better solution, since you don't need to muck around with the internals of the Copy task. A custom filter feels cleaner and more reusable.

1) Write a custom filter that you specify in your copy task. I can't help with the details of how to write a custom filter, but I'd start here. You should be able to put the custom filter in buildSrc (lots of info about that at gradle.org) and then you simply need to import it at the top of your gradle file. If you write it in groovy I think you can even just use ant.patch() again.

task copyAndPatch() {
    into('META-INF') {
    from 'deployment', {
        include 'applicationContext.xml'
        rename { fn -> "jboss-spring.xml' }
        filter(MyCustomFilterThatDoesAPatch, patchFile: 'local/applicationContext.xml.patch')
    }
}

2) Write a custom task. Again, I'll leave the details to the experts but you can probably get away with subclassing the Copy task, adding a 'patch' property, and then jumping in during execution to do the dirty work.

like image 134
Josh Gagnon Avatar answered Oct 29 '22 21:10

Josh Gagnon


RFC 5621 defines an XML patching language that uses XPath to target the location in the document to patch. It's great for tweaking config files.

There is an open source implementation in Java (Disclaimer: I am the author). It includes a filter that can be used from Gradle to patch XML files during any task that implements CopySpec. For example:

buildscript {
    repositories { jcenter() }
    dependencies { classpath "com.github.dnault:xml-patch:0.3.0" }
}

import com.github.dnault.xmlpatch.filter.XmlPatch

task copyAndPatch(type: Copy) {
    // Patch file in RFC 5621 format
    def patchPath = 'local/applicationContext-patch.xml'

    inputs.file patchPath
    
    into('META-INF') {
        from 'deployment', {
            include 'applicationContext.xml'
            rename { 'jboss-spring.xml' }
            filter(XmlPatch, patch: patchPath)
        }
    }
}
like image 2
dnault Avatar answered Oct 29 '22 23:10

dnault