PatchingSources
2006-07-25 - pitti's presentation on applying patches.
IRC log: http://netz.smurf.noris.de/logs/freenode/2006/07/25/%23ubuntu-motu-school.log
Patching Ubuntu packages
Why use separate patches
In earlier times, people just applied patches inline (i. e. directly in the source code tree). However, this makes it very hard to extract patches later to modify them, send them upstream, etc. Also this means that new upstream versions are a pain, since they generate a lot of rejections when applying the package diff.gz to them. With split-out patches it is much easier to send them upstream, keep track of them, develop them, etc., since you always see which changes belong together. The ideal state is an unmodified tarball from upstream, plus clean and separate patches, plus the packaging bits in debian/. That means that lsdiff <sourcepackage>.diff.gz only contains debian/.
The first attempts to split-out patches were pretty trivial: storing patches in debian/patches/, and adding some patch/patch -R snippets to debian/rules. This worked for small patches, but provided no tools for editing these patches, updating them for new upstream versions, etc.
Thus several standard patch systems were created which are easy to deploy and provide tools for patch juggling and editing.
What I would like to do now is to introduce the most common patch systems and show some hands-on demo how to add a new patch and how to edit one. For this, I will download a source package from the current dapper archive, quickly explain the patch system, and show how to apply some (braindead) modifications to it. I recommend you to do the same steps in a terminal, so that you get a feeling for the process and can immediately ask questions.
If you want to try the stuff yourself, please do the following commands as preparation:
sudo apt-get install dpatch dbs quilt patchutils cdbs apt-get source cron udev pmount gnome-volume-manager ed xterm wget http://people.ubuntu.com/~pitti/scripts/dsrc-new-patch chmod 755 dsrc-new-patch
cron: inline patches
No patch system at all, nothing much to say about this. You directly edit the files in the source tree. This is convenient for a simple and quick change, but will bite back for new upstream versions (see above)
udev: separate patches, but no patch system
This case is the most complicated one since you have to do all the hard work manually. In order to make you understand what a patch system does, and to give you a fallback method that will *always* work with any patch system, I handle this first.
The general approach is:
- copy the clean source tree to a temporary directory
- apply all patches up to the one you want to edit; if you want to create a new patch, apply all existing ones (this is necessary since in general patches depend on previous patches) if you want, you can use debian/rules for this: remove the patches that come *after* the one you want to edit, and call 'debian/rules patch'. The actual name for the patch target varies, I have seen the following ones so far: patch setup apply-patches unpack patch-stamp. You have to look in debian/rules how it is called.
- copy the whole source tree again: cp -a /tmp/old /tmp/new
- go into /tmp/new, do your modifications
- go back into your original source tree, generate the patch with
diff -Nurp old new > mypatchname.patch
and move the patch to .../debian/patches/mypatchname.patch
udev example 1, let's create a new patch 90_penguins.patch:
cd /whereever/you/unpacked/the/source/udev-079 cp -a . /tmp/old pushd /tmp/old debian/rules patch cp -a . /tmp/new; cd ../new sed -i 's/Linux/Penguin/g' README cd .. diff -Nurp old new > 90_penguins.patch popd mv /tmp/90_penguins.patch debian/patches rm -rf /tmp/old /tmp/new
Now take a look at your shiny new debian/patches/90_penguins.patch.
Pretty much work, isn't it? Since this happens pretty often, I created a very dumb helper script 'dsrc-new-patch' for this purpose. Using this, above steps would reduce to:
dsrc-new-patch 90_penguins.patch sed -i 's/Linux/Penguin/g' README <press Control-D to leave the subshell>
However, this script is currently too dumb to edit existing patches, or to put patches somewhere else than the top of the patch stack. If you need this, then you need to do the manual approach. Assume we want to edit 10-selinux-include-udev-h.patch. This patch does not depend on the previous patches, so it's a bit easier that way, too:
udev example 2:
cd /whereever/you/unpacked/the/source/udev-079 cp -a . /tmp/old pushd /tmp/old cp -a . /tmp/new; cd ../new patch -p1 < debian/patches/10-selinux-include-udev-h.patch # above will apply the current version of the patch, so that you can edit it sed -i '1 s/$/***** HELLO WORLD ****/' udev_selinux.c cd .. diff -Nurp old new > 10-selinux-include-udev-h.patch popd mv /tmp/10-selinux-include-udev-h.patch debian/patches rm -rf /tmp/old /tmp/new
Now debian/patches/10-selinux-include-udev-h.patch contains both the original change and our modification of the first line of the source file.
Since this is so hideously complicated, patch systems were invented to aid you with that. Let's look at the most popular ones now (they are sufficient to allow you to patch about 90% of the archive's source packages; for the rest you have to resort to the manual approach above).
pmount: cdbs with simple-patchsys
cdbs' simple-patchsys.mk module matches its name, it has no bells and whistles whatsoever. However, it is pretty popular since it is sufficient for most tasks, and long ago I wrote a script 'cdbs-edit-patch' which most people can live with pretty well. This script is contained in the normal cdbs package.
You just supply the name of a patch to the script, and depending on whether it already exists or not, it will create a new patch or edit an existing one.
Example with pmount:
cd /whereever/you/unpacked/the/source/pmount-0.9.11 cdbs-edit-patch 03-simple-readme.patch echo 'This should document pmount' > README <press Control-D to leave the subshell>
Editing an already existing patch works exactly the same way.
With the latest edgy version, cdbs-edit-patch also works for packages using tarball.mk, and can edit patches which produce rejections.
ed: dpatch
dpatch is a pretty robust and proven patch system which also ships a script 'dpatch-edit-patch'. The two most important things you should be aware of:
- dpatch does not apply debian/patches/*, but instead applies all patches mentioned in debian/patches/00list, in the mentioned order. That means that you do not have to rely on asciibetical ordering of the patches and can easily disable patches, but you have to make sure to not forget to update 00list if you add a new patch.
- dpatch patches are actually scripts that are executed, not just patches fed to 'patch'. That means you can also do fancy things like calling autoconf or using sed in a dpatch if you want.
The manpage is very good and has examples, too, so I will only give an example here:
cd /whereever/you/unpacked/the/source/ed-0.2 dpatch-edit-patch 05_ed.1-warning-fix <edit stuff, press Ctrl+D>
This will edit an already existing patch and take care that all previous patches are applied in order.
dpatch-edit-patch foo.dpatch 06_testsuite-Makefile.dpatch <edit stuff, press Ctrl+D> echo foo.dpatch >> debian/patches/00list
This will create a new patch foo.dpatch relative to the already existing 06_testsuite-Makefile.dpatch. If your patch is very confined and does not depend on other patches, you can leave out the second argument.
xterm: quilt
quilt is the other non-dumb standard patch system. Like dpatch, it has a list of patches to apply in patches/series (to use debian/patches, packages need to add a sylink). It is non-trivial to set up and has a lot of advanced commands which make it very flexible, but not very easy to use.
So I will only show a small example here, too. First, use the existing machinery to set up symlinks and directories for quilt:
cd /whereever/you/unpacked/the/source/xterm-208 debian/rules prepare export QUILT_PATCHES=debian/patches
Now let's edit the already existing patch 901_xterm_manpage.diff:
quilt push 901_xterm_manpage.diff sed -i 's/Copyright/Copyleft/' xterm.man quilt refresh 901_xterm_manpage.diff quilt pop -a
So unlike the other patch systems, quilt works with patched inline sources, but keeps track of modifications.
Finally, let's add a new patch to the top of the stack:
quilt push -a quilt new muhaha.diff quilt add README # you have to do that for all files you modify sed -i '1 s/^/MUHAHA/' README quilt refresh quilt pop -a