Patch Best Practice Guideline
First approach
A patch is just a file that make you able to fix trouble (such as a makefile which contains a variable which doesn't match with fedora environment) which could happen during the build of your package(s).
The goal of this guideline is to show you how you can do that easly.
So, i think the better way to learn and have nice skill in patching, it's to give you most examples as possible that you can practice to improve your knowledge.
How to create a patch
To be able to create a patch, you have for first to know (indeed) what files(s) you have to modify to fix errors. We will see this in examples use case below.
For now i will just show you action to realize to create your patch.
When you found out what file(s) (including its location) must be fix, The best way to procede is as follow:
1. Create a patch on one file.
Make a copy of file which must be fix.
$ cp bar.am bar.am.false
We will now edit the original file, the .false file will be the base changing usage that the generated patch command will use to make the difference. You can use your favorite text editor to procede or use vim as follow.
$ vi bar.am
When it's done, use :wq command to save and exit.
Now we can procede and generate the patch as follow.
$ diff -u bar.am.false bar.am > ~/rpmbuild/SOURCES/foo-%{version}-error_name_fixed.patch
2. Create a patch on multiple files.
Same thing, make a copy of each files you have to fix with extension like .false (in my example).
Then, use this follow to generate one patch file for more than one file-to-fix.
$ gendiff /top/directory .false >> ~/rpmbuild/SOURCES/foo-%{version}-error_name_fixed.patch
How to apply a patch
Using a patch is very simple as drink a beer :-).
Patch usage in SPEC file:
[...] Patch0: %{name}-%{version}-featuretofix.patch Patch1: %{name}-%{version}-toolstofix.patch [...] %setup -q -n %{name}-%{version} %patch -p1 0 ###''where 0 is Patch0, the patch number previously set.'' %patch -p1 1 [...]
or/and in some case
[...] %setup -q -n %{name}-%{version} %patch -p1 -b .featurestofix %patch -p1 -b .toolstofix [...]
When does a patch is really usefull ?
This question is really important. Indeed you should know when a patch is really usefull cause, in some case the use of patch can be avoid by using sed expression in SPEC file(s). I mean that for minor fix the use of Sed is more appropriate than patch, such as fix a wrong default installation path written in Makefile file.
1. A case where a patch can be avoid
You have to build a package for both arch i386 and x86_64, which contains some libraries files which should be install in following path i386:/usr/lib and x86_64:/usr/lib64 and where configure and makefile files doesn't provided the flags options --libdir to install these libraries in /usr/lib64 for x86_64 package but in /usr/lib.It's not that we want. So to fix this the use of Sed is more appropriate and easy as follow:
[...] %prep -q -n %{name}-%{version} sed -e 's|/usr/lib/*.so|%{_libdir}/*.so|g -i /path/to/file/to/fix [...} ]
2. A case where a patch have to be set
For some build package, you have many files which contain many expression which must be fix in diferrent files (to make the build able to work) in the source tarball. In this case, the use of patch is totally more appropriate than using Sed.
We will see that in examples use case below.
First Use Case: Easy patching
We'll take for this example the package bar where you can get from its related website here:
!### TO DO ####
Second Use Case: Advanced patching
For this advanced patching i will take an application that i packaged and hardly patched to be able to work on Fedora envornment.
This pakcage is keytouch (fr) , for now the rpm files are only available on my personnal repository (i plan to add them to the fedora repository). you can get the source on its website here
1. Understand the build error.
To understand the build error you have to be a lil' smart and be able to interprate the error.
Let's show the error that we get after launched the build of this package and interprate it.
Executing(%install): /bin/sh -e /var/tmp/rpm-tmp.22536 + umask 022 + cd /home/sth/rpmbuild/BUILD + cd keytouch-2.3.0 + LANG=C + export LANG + unset DISPLAY + rm -rf /var/tmp/keytouch-2.3.0-beta-5-1.fc6.lxtnow-root-sth + mkdir -p /var/tmp/keytouch-2.3.0-beta-5-1.fc6.lxtnow-root-sth//etc/X11/xinit/Xclients.d + mkdir -p /var/tmp/keytouch-2.3.0-beta-5-1.fc6.lxtnow-root-sth//etc/init.d/ + mkdir -p /var/tmp/keytouch-2.3.0-beta-5-1.fc6.lxtnow-root-sth//etc/pam.d/ + mkdir -p /var/tmp/keytouch-2.3.0-beta-5-1.fc6.lxtnow-root-sth//etc/security/console.apps/ + make install DESTDIR=/var/tmp/keytouch-2.3.0-beta-5-1.fc6.lxtnow-root-sth Making install in mxml make[1] : Entering directory <code>/home/sth/rpmbuild/BUILD/keytouch-2.3.0/mxml' make[1] : Nothing to be done for <code>install'. make[1] : Leaving directory <code>/home/sth/rpmbuild/BUILD/keytouch-2.3.0/mxml' Making install in string_to_keycode make[1] : Entering directory <code>/home/sth/rpmbuild/BUILD/keytouch-2.3.0/string_to_keycode' make[1] : Nothing to be done for <code>install'. make[1] : Leaving directory <code>/home/sth/rpmbuild/BUILD/keytouch-2.3.0/string_to_keycode' Making install in keytouchd make[1] : Entering directory <code>/home/sth/rpmbuild/BUILD/keytouch-2.3.0/keytouchd' make[2] : Entering directory <code>/home/sth/rpmbuild/BUILD/keytouch-2.3.0/keytouchd' /bin/sh ../mkinstalldirs /var/tmp/keytouch-2.3.0-beta-5-1.fc6.lxtnow-root-sth/usr/bin mkdir /var/tmp/keytouch-2.3.0-beta-5-1.fc6.lxtnow-root-sth/usr mkdir /var/tmp/keytouch-2.3.0-beta-5-1.fc6.lxtnow-root-sth/usr/bin /usr/bin/install -c keytouchd /var/tmp/keytouch-2.3.0-beta-5-1.fc6.lxtnow-root-sth/usr/bin/keytouchd XSESSION_KEYTOUCHD_LINE="keytouchd &"; \ XSESSION_DIR="/var/tmp/keytouch-2.3.0-beta-5-1.fc6.lxtnow-root-sth/etc/X11/Xsession.d"; \ if [ -d $XSESSION_DIR ] ; then \ echo '# $Id: 92keytouchd_launch' > $XSESSION_DIR/92keytouchd_launch; \ echo "$XSESSION_KEYTOUCHD_LINE" >> $XSESSION_DIR/92keytouchd_launch; \ chmod 644 $XSESSION_DIR/92keytouchd_launch; \ else \ XSESSION_FILE="/etc/X11/Xsession"; \ if [ "<code>grep \"$XSESSION_KEYTOUCHD_LINE\" \"$XSESSION_FILE\" -m 1 -h</code>" != "$XSESSION_KEYTOUCHD_LINE" ] ; then \ XSESSION_TMP=<code>cat "$XSESSION_FILE"</code>; \ echo "$XSESSION_KEYTOUCHD_LINE" > $XSESSION_FILE; \ echo "$XSESSION_TMP" >> $XSESSION_FILE; \ fi \ fi grep: /etc/X11/Xsession: No such file or directory cat: /etc/X11/Xsession: No such file or directory /bin/sh: line 10: /etc/X11/Xsession: Permission denied /bin/sh: line 11: /etc/X11/Xsession: Permission denied make[2] : *** [install-data-local] Error 1 make[2] : Leaving directory <code>/home/sth/rpmbuild/BUILD/keytouch-2.3.0/keytouchd' make[1] : *** [install-am] Error 2 make[1] : Leaving directory <code>/home/sth/rpmbuild/BUILD/keytouch-2.3.0/keytouchd' make: *** [install-recursive] Error 1 error: Bad exit status from /var/tmp/rpm-tmp.22536 (%install) RPM build errors: Bad exit status from /var/tmp/rpm-tmp.22536 (%install)
As you can see, the build error is clearly explicit and show why the build failed. It seem that we have files/directory that don't exist see grep and cat command error) but why ? If you look close you will see that
2. Create the patch to fix error.
--- keytouch-2.3.0-beta-4/keytouchd/Makefile.am.false1 2007-01-11 16:23:07.000000000 +0100 +++ keytouch-2.3.0-beta-4/keytouchd/Makefile.am 2007-01-11 16:27:43.000000000 +0100 @@ -22,13 +22,13 @@ install-data-local: @$(NORMAL_INSTALL) XSESSION_KEYTOUCHD_LINE="keytouchd &"; \ - XSESSION_DIR="$(DESTDIR)/etc/X11/Xsession.d"; \ + XSESSION_DIR="$(DESTDIR)/etc/X11/xinit/Xclients.d"; \ if [ -d $$XSESSION_DIR ] ; then \ echo '# $$Id: 92keytouchd_launch' > $$XSESSION_DIR/92keytouchd_launch; \ echo "$$XSESSION_KEYTOUCHD_LINE" >> $$XSESSION_DIR/92keytouchd_launch; \ chmod 644 $$XSESSION_DIR/92keytouchd_launch; \ else \ - XSESSION_FILE="/etc/X11/Xsession"; \ + XSESSION_FILE="/etc/X11/xinit/Xsession"; \ if [ "<code>grep \"$$XSESSION_KEYTOUCHD_LINE\" \"$$XSESSION_FILE\" -m 1 -h</code>" != "$$XSESSION_KEYTOUCHD_LINE" ] ; then \ XSESSION_TMP=<code>cat "$$XSESSION_FILE"</code>; \ echo "$$XSESSION_KEYTOUCHD_LINE" > $$XSESSION_FILE; \ --- keytouch-2.3.0-beta-4/keytouchd/Makefile.in.false1 2007-01-11 16:23:07.000000000 +0100 +++ keytouch-2.3.0-beta-4/keytouchd/Makefile.in 2007-01-11 16:27:14.000000000 +0100 @@ -333,13 +333,13 @@ install-data-local: @$(NORMAL_INSTALL) XSESSION_KEYTOUCHD_LINE="keytouchd &"; \ - XSESSION_DIR="$(DESTDIR)/etc/X11/Xsession.d"; \ + XSESSION_DIR="$(DESTDIR)/etc/X11/xinit/Xclients.d"; \ if [ -d $$XSESSION_DIR ] ; then \ echo '# $$Id: 92keytouchd_launch' > $$XSESSION_DIR/92keytouchd_launch; \ echo "$$XSESSION_KEYTOUCHD_LINE" >> $$XSESSION_DIR/92keytouchd_launch; \ chmod 644 $$XSESSION_DIR/92keytouchd_launch; \ else \ - XSESSION_FILE="/etc/X11/Xsession"; \ + XSESSION_FILE="/etc/X11/xinit/Xsession"; \ if [ "<code>grep \"$$XSESSION_KEYTOUCHD_LINE\" \"$$XSESSION_FILE\" -m 1 -h</code>" != "$$XSESSION_KEYTOUCHD_LINE" ] ; then \ XSESSION_TMP=<code>cat "$$XSESSION_FILE"</code>; \ echo "$$XSESSION_KEYTOUCHD_LINE" > $$XSESSION_FILE; \ --- keytouch-2.3.0-beta-4/keytouch-init/Makefile.am.false1 2007-01-11 16:17:10.000000000 +0100 +++ keytouch-2.3.0-beta-4/keytouch-init/Makefile.am 2007-01-11 16:19:04.000000000 +0100 @@ -19,11 +19,10 @@ install-data-local: @$(NORMAL_INSTALL) - if [ <code>which chkconfig</code> ] ; then \ + if [ /sbin/chkconfig<code> ] ; then \ sed 's,\(^keytouchinit=\)\(.*\),\1$(bindir)/keytouch-init,' keytouch-init.sh \ > $(DESTDIR)/etc/init.d/keytouch-init.sh; \ chmod +x $(DESTDIR)/etc/init.d/keytouch-init.sh; \ - chkconfig --add keytouch-init.sh; \ elif [ <code>which rc-update</code> ] ; then \ echo "$(bindir)/keytouch-init" > $(DESTDIR)/etc/init.d/keytouch-init.sh; \ chmod +x $(DESTDIR)/etc/init.d/keytouch-init.sh; \ --- keytouch-2.3.0-beta-4/keytouch-init/Makefile.in.false1 2007-01-11 16:17:10.000000000 +0100 +++ keytouch-2.3.0-beta-4/keytouch-init/Makefile.in 2007-01-11 16:18:28.000000000 +0100 @@ -332,11 +332,10 @@ install-data-local: @$(NORMAL_INSTALL) - if [ <code>which chkconfig</code> ] ; then \ + if [ /sbin/chkconfig ] ; then \ sed 's,\(^keytouchinit=\)\(.*\),\1$(bindir)/keytouch-init,' keytouch-init.sh \ > $(DESTDIR)/etc/init.d/keytouch-init.sh; \ chmod +x $(DESTDIR)/etc/init.d/keytouch-init.sh; \ - chkconfig --add keytouch-init.sh; \ elif [ <code>which rc-update</code> ] ; then \ echo "$(bindir)/keytouch-init" > $(DESTDIR)/etc/init.d/keytouch-init.sh; \ chmod +x $(DESTDIR)/etc/init.d/keytouch-init.sh; \ --- keytouch-2.3.0-beta-4/keytouch-acpid/Makefile.am.false1 2007-01-11 16:13:41.000000000 +0100 +++ keytouch-2.3.0-beta-4/keytouch-acpid/Makefile.am 2007-01-11 16:15:37.000000000 +0100 @@ -19,11 +19,10 @@ install-data-local: @$(NORMAL_INSTALL) - if [ <code>which chkconfig</code> ] ; then \ + if [ /sbin/chkconfig ] ; then \ sed 's,\(^keytouchacpid=\)\(.*\),\1"$(bindir)/keytouch-acpid \&",' keytouch-acpid.sh \ > $(DESTDIR)/etc/init.d/keytouch-acpid.sh; \ chmod +x $(DESTDIR)/etc/init.d/keytouch-acpid.sh; \ - chkconfig --add keytouch-acpid.sh; \ elif [ <code>which rc-update</code> ] ; then \ echo "$(bindir)/keytouch-acpid &" > $(DESTDIR)/etc/init.d/keytouch-acpid.sh; \ chmod +x $(DESTDIR)/etc/init.d/keytouch-acpid.sh; \ --- keytouch-2.3.0-beta-4/keytouch-acpid/Makefile.in.false1 2007-01-11 16:13:41.000000000 +0100 +++ keytouch-2.3.0-beta-4/keytouch-acpid/Makefile.in 2007-01-11 16:15:11.000000000 +0100 @@ -332,11 +332,10 @@ install-data-local: @$(NORMAL_INSTALL) - if [ <code>which chkconfig</code> ] ; then \ + if [ /sbin/chkconfig ] ; then \ sed 's,\(^keytouchacpid=\)\(.*\),\1"$(bindir)/keytouch-acpid \&",' keytouch-acpid.sh \ > $(DESTDIR)/etc/init.d/keytouch-acpid.sh; \ chmod +x $(DESTDIR)/etc/init.d/keytouch-acpid.sh; \ - chkconfig --add keytouch-acpid.sh; \ elif [ <code>which rc-update</code> ] ; then \ echo "$(bindir)/keytouch-acpid &" > $(DESTDIR)/etc/init.d/keytouch-acpid.sh; \ chmod +x $(DESTDIR)/etc/init.d/keytouch-acpid.sh; \
3. Re-launch the build.
third Use Case: Expert patching
!## TO DO ##
- 2. Create the patch to fix
--- mdsplib-0.11/Makefile~ 2007-06-08 11:31:18.000000000 +0200 +++ mdsplib-0.11/Makefile 2007-06-08 11:31:18.000000000 +0200 @@ -5,16 +5,15 @@ libdir = $(prefix)/lib includedir = $(prefix)/include -library: libmetar.a +library: libmetar.so.0 all: dmetar library -libmetar.a: src/antoi.o src/charcmp.o src/dcdmetar.o src/dcdmtrmk.o src/fracpart.o src/prtdmetr.o src/stspack2.o src/stspack3.o - ar ruv libmetar.a src/antoi.o src/charcmp.o src/dcdmetar.o src/dcdmtrmk.o src/fracpart.o src/prtdmetr.o src/stspack2.o src/stspack3.o - ranlib libmetar.a +libmetar.so.0: src/antoi.o src/charcmp.o src/dcdmetar.o src/dcdmtrmk.o src/fracpart.o src/prtdmetr.o src/stspack2.o src/stspack3.o + gcc -shared -o libmetar.so.0 -Wl,-soname,libmetar.so.0 src/antoi.o src/charcmp.o src/dcdmetar.o src/dcdmtrmk.o src/fracpart.o src/prtdmetr.o src/stspack2.o src/stspack3.o -dmetar: src/drvmetar.o libmetar.a - $(CC) $(CFLAGS) -o dmetar src/drvmetar.o libmetar.a $(LIBS) +dmetar: src/drvmetar.o libmetar.so.0 + $(CC) $(CFLAGS) -o dmetar src/drvmetar.o -L. -lmetar $(LIBS) src/antoi.o: src/antoi.c src/local.h $(CC) $(CFLAGS) -c src/antoi.c -o src/antoi.o @@ -44,14 +43,13 @@ $(CC) $(CFLAGS) -c src/stspack3.c -o src/stspack3.o clean: - rm -f src/*.o dmetar libmetar.a + rm -f src/*.o dmetar libmetar.so.0 install: library mkdir -p $(DESTDIR)$(includedir) cp metar.h $(DESTDIR)$(includedir)/ chmod 0644 $(DESTDIR)$(includedir)/metar.h mkdir -p $(DESTDIR)$(libdir) - cp libmetar.a $(DESTDIR)$(libdir)/ - chmod 0644 $(DESTDIR)$(libdir)/libmetar.a - ranlib $(DESTDIR)$(libdir)/libmetar.a - + cp libmetar.so.0 $(DESTDIR)$(libdir)/ + chmod 0755 $(DESTDIR)$(libdir)/libmetar.so.0 + ln -s libmetar.so.0 $(DESTDIR)$(libdir)/libmetar.so