From Fedora Project Wiki

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