1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
|
RPM package building guide
Sam Isaacson (sbi@nbcs.rutgers.edu)
--------------------------------------------------------------------------------
Introduction to rpm package building
Rpm packages are usually built with a "spec file," which is a collection of text and shell scripts that build, install and describe a software program. Here is a typical spec file:
Summary: Rc shell from Plan 9
Name: rc
Version: 1.6
Release: 1
Group: System Environment/Shells
Copyright: BSD-type
Source: rc-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-root
Requires: readline
BuildRequires: readline-devel
%description
rc is a command interpreter and programming language similar to sh(1).
It is based on the AT&T Plan 9 shell of the same name. The shell
offers a C-like syntax (much more so than the C shell), and a powerful
mechanism for manipulating variables. It is reasonably small and
reasonably fast, especially when compared to contemporary shells. Its
use is intended to be interactive, but the language lends itself well
to scripts.
[from the man page]
%prep
%setup -q
%build
LD="/usr/ccs/bin/ld -L/usr/local/lib -R/usr/local/lib" \
LDFLAGS="-L/usr/local/lib -R/usr/local/lib" ./configure --with-history \
--with-readline
make
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/usr/local
make install prefix=$RPM_BUILD_ROOT/usr/local sysconfdir=$RPM_BUILD_ROOT/etc
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,bin,bin)
%doc COPYING AUTHORS EXAMPLES README RELDATE ChangeLog
/usr/local/bin/rc
/usr/local/bin/-
/usr/local/bin/--
/usr/local/bin/-p
/usr/local/bin/--p
/usr/local/man/man1/rc.1
/usr/local/man/man1/history.1
The spec file is split into several sections, which will be examined individually.
In order to write spec files, it is important to understand rpm's dependency checking, macros and directory structure. When you build a package, rpm creates a list of the shared libraries that it includes and a list of the shared libraries to which it is linked. RPM records the shared libraries that the package provides, along with the package name itself and anything manually specified in the spec file, along with version information. Similarly, RPM records the required shared libraries and manually specified requires. In rc, readline, libc.so.1, libcurses.so.1, libdl.so.1 and libreadline.so.4 are required, and rc (version 1.6) is provided. Readline and libreadline.so.4 are both provided by the readline package; the rest are provided by the operating system.
"Build requires" -- packages required at build time -- can be specified in a spec file. When it is built, rc needs readline-devel, as it has the header files for the readline library.
Rpm has a simple macro system. Macros can be defined like so:
%define foo bar
%{foo}
is preprocessed to become "bar". Rpm has logical constructs: %if/%else/%endif, %ifos, and %ifarch (cf. openssl.spec).
Finally, rpm has a system of directories for package building:
prefix/src/redhat/RPMS/sparc
prefix/src/redhat/RPMS/sparc64
prefix/src/redhat/RPMS/sparcv9
.
.
.
prefix/src/redhat/SRPMS
prefix/src/redhat/SOURCES ($RPM_SOURCE_DIR)
prefix/src/redhat/BUILD ($RPM_BUILD_DIR)
prefix/src/redhat/SPECS
RPM expects to find your source in SOURCES; it will unpack and compile the source code in BUILD. RPM expects to find the files that the package will install in $RPM_BUILD_ROOT, which rc has set to %{_tmppath}/rc-root ("%{_tmppath}" is a macro set by rpm which expands to the name of a directory for temporary files).
The Preamble
The preamble from rc.spec is:
Summary: Rc shell from Plan 9
Name: rc
Version: 1.6
Release: 1
Group: System Environment/Shells
Copyright: BSD-type
Source: rc-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-root
Requires: readline
It describes the package name, version, etc. Name, Version, Release, Group, Copyright (or License), and Summary are required. Other fields, such as URL and Packager, are optional. Name, Version and Release define macros called %{name}, %{version} and %{release} respectively.
Generally, source filenames match the expansion of "%{name}-%{version}.tar.gz". The %{version} macro makes maintaining the package much easier; its use is highly recommended. If the source field has an URL, rpm automatically downloads the source and places it in $RPM_SOURCE_DIR. You can specify multiple sources with Source0, Source1, etc.
The %description
This section is parsed separately from the preamble, but can be thought of as another field. Generally, one can steal the introduction from a README or man page to get a good description.
The %prep section
%prep
%setup -q
The %prep section is where the source is prepared, usually in $RPM_BUILD_DIR. Rpm provides the %setup and %patch primitives which automatically untar and patch your source. %setup expects it to untar into a directory called %{name}-%{version}; otherwise you have to pass it the -n switch, which renames the directory. The important %setup switches are:
-n <name> (name of build directory)
-c (creates top-level build directory)
-D (don't delete top-level build directory)
-T (don't unpack Source0)
-a <n> (unpack Source number n, after cd'ing to build directory)
-a <n> (unpack Source number n, before cd'ing to build directory)
-q (unpack silently)
To unpack several sources into the same directory, you need to have something like the following in %prep:
%setup -q
%setup -D -T -a 1
%setup -D -T -a 2
That unpacks source 0, then cds into %{name}-%{version} and unpacks source 1 and 2. As for patches, you have the following switches:
-P < n > (use Patch number n)
-p, -b, -E (see patch(1))
While %prep appears to be all macros, don't be fooled -- %prep, %clean, %build, %install, %pre, %post, etc. are all shell scripts.
You might need to install GNU tar and put it on your PATH before Sun tar when building packages with extremely long filenames (the GNOME software in particular requires gnutar).
The %build section
%build
LD="/usr/ccs/bin/ld -L/usr/local/lib -R/usr/local/lib" \
LDFLAGS="-L/usr/local/lib -R/usr/local/lib" ./configure --with-history \
--with-readline
make
The %build section is where the actual compiling takes place. Rpm has a %configure macro, which is broken by design (it takes the directories from prefix/lib/rpm/macros, so it might misplace your files; and it only works with GNU configure). With GNU configure, you probably want to configure and build the sources like so:
automake # if you patched Makefile.am
autoconf # if you patched configure.in
LD="/usr/ccs/bin/ld -L/usr/local/lib -R/usr/local/lib" \
LDFLAGS="-L/usr/local/lib -R/usr/local/lib" CPPFLAGS="-I/usr/local/include" \
./configure --prefix=/usr/local --sysconfdir=/etc
make
Unfortunately, GNU configure may not use $LD and $LDFLAGS together -- sometimes it does, and sometimes it doesn't. It is more reliable to pass everything into configure (especially because it increases the chance that your specfile will work on someone else's machine). If you're compiling C++ for X, add CXX="g++ -fpermissive" (Sun's include files aren't ANSI C++).
As for imake (with Sun's cc, not gcc), try:
xmkmf -a
make CCOPTIONS="-I/usr/local/include" LINTOPTS="" \
EXTRA_LDOPTIONS="-L/usr/local/lib -R/usr/local/lib"
Using imake and gcc is left as an exercise to the reader.
Don't specify the prefix as $RPM_BUILD_ROOT/usr/local; many programs hardcode file locations at the configure or make stage.
The %install section
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/usr/local
make install prefix=$RPM_BUILD_ROOT/usr/local sysconfdir=$RPM_BUILD_ROOT/etc
The %install section is where the files get "installed" into your build root. You can build rpms without a build root, but this practice is highly deprecated and insecure (more on this later). Always begin the %install section with something along the lines of
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/usr/local
Sometimes, you can get away with just adding
make install prefix=$RPM_BUILD_ROOT/usr/local
Usually, it's a little hairier. If your program puts files in /etc, you have to tell make install (if you use make install). If a program hardcodes file locations at the make install stage, the best solution is to massage the output of make -n install. Truly devious programs such as qmail, which compile their own installer, make require patches to install correctly.
Other scripts
%clean
rm -rf $RPM_BUILD_ROOT
Generally, the only other script you need is %clean, which gets executed after the build (just clean out $RPM_BUILD_ROOT). You also get %pre (preinstall), %post (postinstall), %preun (preuninstall), %postun (postuninstall), %verifyscript (executed with rpm -V), and triggers (read the documentation included with rpm).
The %files section
%files
%defattr(-,bin,bin)
%doc COPYING AUTHORS EXAMPLES README RELDATE ChangeLog
/usr/local/bin/rc
/usr/local/bin/-
/usr/local/bin/--
/usr/local/bin/-p
/usr/local/bin/--p
/usr/local/man/man1/rc.1
/usr/local/man/man1/history.1
The %files section is where you list all the files in the package. You have a few commands at your disposal: %doc (marks documentation), %attr (marks attibutes of a file - mode [- means don't change mode], user, group), %defattr (default attributes), %verify (see Maximum RPM), %config (marks configuration files), %dir and %docdir.
If a filename in the %files list corresponds to a directory, the package owns the directory as well as all the files in it; so don't put /usr/bin in your %files list. Be careful with globbing and directories; if you list a file twice, rpm will not build your package. Also, some symlinks (absolute ones) cause rpm to complain bitterly; avoid unintentionally grabbing them.
Methods for generating file lists
Unfortunately, generating file lists isn't always easy. Assuming that you didn't have to parse the output of make -n install yourself to write the %install section, try doing something sneaky like:
$ ./configure --prefix=/usr/local --sysconfdir=/etc
$ make
$ mkdir -p sandbox/usr/local/
$ make install prefix=`pwd`/sandbox/usr/local/ sysconfdir=`pwd`/etc
$ for i in `find sandbox -type f` ; do # check to ensure that no files
> strings $i | grep sandbox && echo $i # "know" that they were installed
> done # in the build root
Check out the Makefile. Some packages use prefix; others use PREFIX, DESTDIR, or something different. Sometimes, you don't need to add the "usr/local" part. This is, incidentally, a good reason not to build packages as root&emdash;if you accidentally install the software on your system (instead of in an empty directory), you cannot test your package as easily.
Using the rudimentary genspec.pl script (or find(1)), you can use this directory to generate a file list. After you get a list, you may wish to replace long lists of files with globs. For instance:
/usr/local/lib/locale/cs/LC_MESSAGES/rpm.mo
/usr/local/lib/locale/de/LC_MESSAGES/rpm.mo
/usr/local/lib/locale/fi/LC_MESSAGES/rpm.mo
/usr/local/lib/locale/fr/LC_MESSAGES/rpm.mo
/usr/local/lib/locale/ja/LC_MESSAGES/rpm.mo
/usr/local/lib/locale/pl/LC_MESSAGES/rpm.mo
/usr/local/lib/locale/pt_BR/LC_MESSAGES/rpm.mo
/usr/local/lib/locale/ru/LC_MESSAGES/rpm.mo
/usr/local/lib/locale/sk/LC_MESSAGES/rpm.mo
/usr/local/lib/locale/sk/LC_MESSAGES/popt.mo
/usr/local/lib/locale/sl/LC_MESSAGES/rpm.mo
/usr/local/lib/locale/sr/LC_MESSAGES/rpm.mo
/usr/local/lib/locale/sv/LC_MESSAGES/rpm.mo
/usr/local/lib/locale/tr/LC_MESSAGES/rpm.mo
/usr/local/lib/locale/ro/LC_MESSAGES/popt.mo
becomes
/usr/local/lib/locale/*/LC_MESSAGES/*.mo
This makes packages more maintainable. If Spanish translations were added, the glob would catch them; otherwise, you would have to add /usr/local/lib/local/es/LC_MESSAGES/rpm.mo to the file list. You have to be careful, however, that the globs catch only the files or directories you want.
Sometimes, it may be appropriate to generate a file list on the fly. The perl package does this:
%build
sh Configure -de -Dprefix=/usr/local -Dcpp='/opt/SUNWspro/bin/cc -E' \
-Dcc='/opt/SUNWspro/bin/cc' \
-Dinstallprefix="$RPM_BUILD_ROOT/usr/local" \
-Dldflags='-L/usr/local/lib -R/usr/local/lib' -Dusethreads
make
make test
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/usr/local
make install
# clean up files which know about the build root
for fn in .packlist Config.pm ; do
afn="$RPM_BUILD_ROOT/usr/local/lib/perl5/%{version}/%{perl_arch}/$fn"
chmod 0644 $afn
mv $afn $afn.TEMP
sed "s#$RPM_BUILD_ROOT##g" < $afn.TEMP > $afn
rm -f $afn.TEMP
done
chmod 0444 \
$RPM_BUILD_ROOT/usr/local/lib/perl5/%{version}/%{perl_arch}/Config.pm
find $RPM_BUILD_ROOT -type f \( -name \*.h -o -name \*.a \) -print \
| sed "s#^$RPM_BUILD_ROOT/*#/#" > DEVEL-LIST
find $RPM_BUILD_ROOT -type f ! \( -name \*.h -o -name \*.a \) -print \
| sed "s#^$RPM_BUILD_ROOT/*#/#" > REGULAR-LIST
%files -f REGULAR-LIST
%doc Copying Artistic README
%files devel -f DEVEL-LIST
Subpackages
If you want to make more than one package out of a single source tree, you have to use subpackages. Here is an example of spec file with subpackages:
Name: readline
Version: 4.1
Copyright: GPL
Group: System Environment/Libraries
Summary: GNU readline
Release: 1
Source: readline-4.1.tar.gz
Provides: libhistory.so
Provides: libreadline.so
BuildRoot: %{_tmppath}/%{name}-root
%description
GNU readline is a library that enables history, completion, and
emacs/vi-like motion functionality in a program linked with it.
%package devel
Summary: Readline header files, static libraries
Group: Development/Libraries
Requires: readline = 4.1
%description devel
This package contains the header files and static libraries for
readline. Install this package if you want to write or compile a
program that needs readline.
%prep
%setup -q
%build
autoconf
LDFLAGS="-L/usr/local/lib -R/usr/local/lib" ./configure \
--prefix=/usr/local --enable-shared
make
make shared
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/usr/local
make install prefix=$RPM_BUILD_ROOT/usr/local
make install-shared prefix=$RPM_BUILD_ROOT/usr/local
%clean
rm -rf $RPM_BUILD_ROOT
%post
ln -s /usr/local//lib/libhistory.so.4 /usr/local/lib/libhistory.so
ln -s /usr/local//lib/libreadline.so.4 /usr/local/lib/libreadline.so
if [ -x /usr/local/bin/install-info ] ; then
/usr/local/bin/install-info --info-dir=/usr/local/info \
/usr/local/info/rluserman.info
/usr/local/bin/install-info --info-dir=/usr/local/info \
/usr/local/info/history.info
fi
%preun
rm /usr/local/lib/libhistory.so
rm /usr/local/lib/libreadline.so
if [ -x /usr/local/bin/install-info ] ; then
/usr/local/bin/install-info --delete --info-dir=/usr/local/info \
/usr/local/info/rluserman.info
/usr/local/bin/install-info --delete --info-dir=/usr/local/info \
/usr/local/info/history.info
fi
%files
%defattr(-,bin,bin)
%doc COPYING
/usr/local/lib/libhistory.so.4
/usr/local/lib/libreadline.so.4
/usr/local/info/readline.info
/usr/local/info/rluserman.info
/usr/local/info/history.info
/usr/local/man/man3/readline.3
%files devel
%defattr(-,bin,bin)
/usr/local/include/readline
/usr/local/lib/libreadline.a
/usr/local/lib/libhistory.a
This creates two packages: readline and readline-devel. (If you just want devel, replace %package devel with %package -n devel and %files with %files -n devel).
Style and Security
Don't build packages as root; edit prefix/lib/rpm/macros so you can build in your home directory. If you build as root, you run the risk of accidentally installing files on your system. Instead of using chown in %install, use %attr in %files.
Be careful when building on a multiuser system; the buildroot, if it is in a globally-writable directory, is a big security hole.
Don't use the %config directive. It might break packages that a user is upgrading; instead, at the end of %install, write
for i in `find $RPM_BUILD_ROOT/etc -type f` ; do
mv $i $i.rpm
done
and warn the user in %post.
Don't make the user set his or her LD_LIBRARY_PATH. Instead, use -R.
If you need to patch configure, patch configure.in instead.
Don't interactively involve the user at build or compile time.
Try to split your packages into static library/header "development" packages and shared library packages.
If you are building GNU replacements for tools packaged with Solaris (e.g. fileutils, grep, tar), put them in /usr/local/gnu instead of /usr/local. Avoid putting any binaries in /usr/local/bin that conflict with any in /usr/ccs/bin, /usr/bin, etc.
Use %{_tmppath} instead of /free/tmp or /var/tmp—it is more portable.
More information
Go to rpm.org for more information. Unfortunately, rpm is extremely poorly documented. Maximum RPM is out of date; the most authoritative source on rpm is rpm's source, which is kind of messy. Any one of the redhat mirrors has source rpms; run them through rpm2cpio and take a look at the specfiles (which are unfortunately Redhat-specific).
--------------------------------------------------------------------------------
$Id: guide.html,v 1.1.1.1 2001/12/14 20:38:47 sbi Exp $
|
|