Building from source is fun, but it’s easier to distribute RPMs to a bunch of machines. You may recall that we recently built and installed the OpenTDS libraries and SQSH, a dandy little command line tool for use with MS SQLServer. These built nicely on my RedHat 7.3 system, and I have a bunch of other developer workstations running the same OS. So let’s roll these tools into a couple of RPMS for easy distribution.
First, I must refer you to the excellent RPM HOWTO and its section on building RPMS. You really don’t need me to tell you how to build RPMs, because this HOWTO is great. But sometimes it’s nice to walk through a couple of examples together. We are building these RPMS on a Red Hat 7.3 workstation, using the default build directories provided by Red Hat under /usr/src/redhat.
[usr-3@srv-3 redhat]$ ls -l total 20 drwxrwxr-x 3 root wheel 4096 Apr 17 11:37 BUILD drwxrwxr-x 8 root wheel 4096 Dec 31 2001 RPMS drwxrwxr-x 2 root wheel 4096 Apr 17 12:16 SOURCES drwxrwxr-x 2 root wheel 4096 Apr 17 13:36 SPECS drwxrwxr-x 2 root wheel 4096 Apr 18 2002 SRPMS
Basically, when we build an RPM, we’re scripting a source build and install. We resolve dependencies (or at least build an awareness of them into our package), we define build-time configuration options, and are able to add any additional tasks we desire, such as creating users, setting up rc scripts, adding cron jobs. This allows us to easily deploy a highly customized build/installation of a package.
The Spec File
All of the customizations mentioned above are defined in the spec file, which we will put in /usr/src/redhat/SPECS. The spec file should be named according to the following convention: package name-version number-release number.spec For our freetds RPM, we’re going to create freetds-0.61-1.spec. Our file is going to be very simple, a bare bones build and install. We’ve already tested the build in our article about SQSH, so we can skip that step. Let’s set up our spec file:
The header section:
Summary: FreeTDS libraries for Sybase/MSSQLSVR Name: freetds Version: 0.61 Release: 1 the version of the RPM Copyright: GPL Group: Development/Tools the group the package belongs to in Red Hat installer Source: http://ibiblio.org/pub/Linux/ALPHA/freetds/stable/freetds-0.61.tgz note: this file name must be the same as you're using to build
The description is self-explanatory:
%description Libraries for connecting to Sysbase/MSSQSVR DBs. Used by Perl DBD:Sybase and sqsh.
The following sections define how the package is to be built and installed:
%prep shell commands to be executed before build. also macros are available for this section, see the HOWTO. This would be the place for adding special users, patching the sources, etc. %setup ./configure --prefix=/usr/local/freetds --enable-msdblib --with-tdsvr=7 %build make %install make install %clean
In the files section, you must list all of the files to be included in the package. There is really no way to come up with this list until you have built the package. There is also no easy way to generate the list. Luckily, in the case of freetds, every file is installed under the /usr/local/freetds directory.
Changelog: list changes made with each modification of the RPM.
Here’s a link to the spec file in all its glory.
Building the RPM
Before we can build it, we need to put the tarball in /usr/src/redhat/SOURCES. OK, let’s give it a whirl. We will use -b (build) a(both source RPM and binary RPM)
[root@srv-3 SPECS]# rpm -ba freetds-0.61-1.spec --snipping profuse output from configure and make-- Processing files: freetds-0.61-1 Finding Provides: (using /usr/lib/rpm/find-provides)... Finding Requires: (using /usr/lib/rpm/find-requires)... Provides: libct.so.1 libsybdb.so.3 libtds.so.2 libtdssrv.so.1 PreReq: rpmlib(PayloadFilesHavePrefix) <= 4.0-1 rpmlib(CompressedFileNames) <= 3.0.4-1 Requires(rpmlib): rpmlib(PayloadFilesHavePrefix) <= 4.0-1 rpmlib(CompressedFileNames) <= 3.0.4-1 Requires: ld-linux.so.2 libc.so.6 libncurses.so.5 libnsl.so.1 libreadline.so.4 libsybdb.so.3 libtds.so.2 libtdssrv.so.1 libc.so.6(GLIBC_2.0) libc.so.6(GLIBC_2.1) libc.so.6(GLIBC_2.1.2) libc.so.6(GLIBC_2.1.3) Wrote: /usr/src/redhat/SRPMS/freetds-0.61-1.src.rpm Wrote: /usr/src/redhat/RPMS/i386/freetds-0.61-1.i386.rpm Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.84503 + umask 022 + cd /usr/src/redhat/BUILD + cd freetds-0.61 + exit 0
And we’re cool. The binary RPM will be written in RPMS under the directory corresponding to the build system’s architecture. Check it out:
[root@srv-3 SPECS]# ls -l ../RPMS/i386/ total 2408 -rw-r--r-- 1 root root 2129073 May 22 15:31 freetds-0.61-1.i386.rpm
The source RPM will be located under SRPMS. No architecture specific directories there. Let’s build an RPM for sqsh, then we’ll install them both on another system to make sure they work.
Building Another RPM
Step 1. The spec file.
This one has a slightly more complicated %prep section:
%prep %setup -n sqsh-2.1 export SYBASE=/usr/local/freetds ./configure
We have to use a setup macro because of the way the tarball is named. In our SOURCES dir, we have a tarball called sqsh-2.1-src.tar.gz. But when this is expanded, the resulting directory is called sqsh-2.1, *not* sqsh-2.1-src as rpm would guess. So we use the “-n” setup macro to tell rpm that the build directory will be called sqsh-2.1. Also in this section, we must export the SYBASE environmental variable to get a good build. Step 2. Build it:
Note: this time we’re just going to build the binary RPM.
[root@srv-3 SPECS]# rpm -bb sqsh-2.1.spec --profuse output from configure and make-- Processing files: sqsh-2.1-1 Finding Provides: (using /usr/lib/rpm/find-provides)... Finding Requires: (using /usr/lib/rpm/find-requires)... PreReq: rpmlib(PayloadFilesHavePrefix) <= 4.0-1 rpmlib(CompressedFileNames) <= 3.0.4-1 Requires(rpmlib): rpmlib(PayloadFilesHavePrefix) <= 4.0-1 rpmlib(CompressedFileNames) <= 3.0.4-1 Requires: ld-linux.so.2 libc.so.6 libct.so.1 libdl.so.2 libm.so.6 libtds.so.2 /bin/sh libc.so.6(GLIBC_2.0) libc.so.6(GLIBC_2.1) Wrote: /usr/src/redhat/RPMS/i386/sqsh-2.1-1.i386.rpm Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.91986 + umask 022 + cd /usr/src/redhat/BUILD + cd sqsh-2.1 + exit 0
Looks good. Now let’s install them on some innocent, untouched system.
[root@victim1 src]# rpm -i freetds-0.61-1.i386.rpm [root@victim1 src]# rpm -i sqsh-2.1-1.i386.rpm [root@victim1 src]# sqsh -S mickey -U bb sqsh-2.1 Copyright (C) 1995-2001 Scott C. Gray This is free software with ABSOLUTELY NO WARRANTY For more information type '\warranty' Password: src/tds/login.c: tds_connect: 10.50.90.1:4000: Connection refused Open Client Message Layer 0, Origin 0, Severity 78, Number 41 Server is unavailable or does not exist.
Oops!! How did this happen? I did not take advantage of the power of RPM to tweak my automated install of the freetds package, which requires a configuration similiar to oracle’s tnsnames for every SQLServer you connect to.
Building a Better RPM
Maybe we can rebuild the RPM, this time inserting a default server configuration into the freetds.conf file. I’m going to uninstall the RPMs from our victim system, clean up the build system, and try again. In cleaning up the build system, I will remove the already-existent /usr/local/freetds directory, to make sure that rpm doesn’t use the files from the old install in making the new rpm. I am making a new spec file, freetds-0.61-2.spec, for the new version of the RPM. Here’s what I’ll include in the install section:
%install make install cat >> /usr/local/freetds/etc/freetds.conf << EOF [MICKEY] host = mickey.domain.com port = 1433 tds version = 7.0 EOF
Now we’ll try installing freetds and sqsh again on the victim.
[root@victim1 src]# rpm -i freetds-0.61-2.i386.rpm [root@victim1 src]# rpm -i sqsh-2.1-1.i386.rpm [root@victim1 src]# sqsh -S mickey -U usr-3 sqsh-2.1 Copyright (C) 1995-2001 Scott C. Gray This is free software with ABSOLUTELY NO WARRANTY For more information type '\warranty' Password:  mickey.tempdb.1> select count(*) from sysusers; ----------- 13 (1 row affected)  mickey.tempdb.1>
There’s the joy. I think you’ll agree that rolling your own is a lot of fun when you can put whatever you like inside.