SUSE Conversations


Building Simple RPMs of Arbitrary Files



By: mikewillis

August 6, 2010 2:07 pm

Reads:5355

Comments:0

Rating:5.0

Something I’ve found very useful when managing machines running SLED is building my own rpms. If I want to put scripts on to the machines I package them as an rpm and then if I need to update them, I can put the new versions in to the rpm, increase the version number and push that out as an update. This is something I do quite a lot but it took me quite a bit of reading before I was able to work out how to do it. Taking my previous Cool Solutions submission Easy Running of Scripts at Boot and Shutdown as a basis, here’s how I do it.

Firstly, you don’t build rpms as the root user. If you build an rpm as root you run the risk that one mistake in your spec file will write all sorts of things all over your filesystem. At best this will just be annoying to clean up, at worse you overwrite something important and you’re looking at a machine that no longer works properly. I created a user (dimble) specifically for building rpms and build the rpms in their home directory rather than the system default of /usr/src/packages.

The user dimble has a file in their home directory called .rpmmacros which looks like this

%_topdir      /home/dimble/rpmbuild
%_tmppath     /home/dimble/rpmbuild/tmp

You need to create the rpmbuild directory and some directories inside it.

$ cd
$ for i in BUILD RPMS SOURCES SPECS SRPMS tmp;do mkdir -p rpmbuild/${i};done

In this example the package to be built will be called my_maintenance_scripts. So create a directory to hold the files that will be in the package

$ cd ~/rpmbuild/SOURCES
$ mkdir my_maintenance_scripts

Put the files to be packaged in that directory. Now you need to create a spec file. A spec file is like a recipe for how to build the rpm. Create a new file called my_maintenance_scripts.spec in the ~/rpmbuild/SPECS directory and for this example the spec file looks like:

Summary:     My Maintenance Scripts
Name:           my_maintenance_scripts
Version:        1.0
Release:        0
# License is a compulsory field so you have to put something there.
License:        none
Source:         %{name}.tar.gz
# This package doesn't contain any binary files so it's architecture independent, hence
# specify noarch for the BuildArch.
BuildArch:      noarch
BuildRoot:      %{_tmppath}/%{name}-build
# I don't worry too much about the group since now one uses the rpm except me
# There's a list at /usr/share/doc/packages/rpm/GROUPS but you don't have to use one of them
# I just use System/Base by default and only change it if something more suitable occurs to me
Group:          System/Base
Vendor:         Your name here

%description
This package provides my maintenances scripts.

%prep
# the set up macro unpacks the source bundle and changes in to the represented by
# %{name} which in this case would be my_maintenance_scripts. So your source bundle
# needs to have a top level directory inside called my_maintenance _scripts
%setup -n %{name}

%build
# this section is empty for this example as we're not actually building anything

%install
# create directories where the files will be located
mkdir -p $RPM_BUILD_ROOT/etc/init.d/my_maintenance.d/start.d
mkdir -p $RPM_BUILD_ROOT/etc/init.d/my_maintenance.d/stop.d

# put the files in to the relevant directories.
# the argument on -m is the permissions expressed as octal. (See chmod man page for details.)
install -m 700 my_maintenance $RPM_BUILD_ROOT/etc/init.d/
install -m 700 start $RPM_BUILD_ROOT/etc/init.d/my_maintenance.d/
install -m 700 stop $RPM_BUILD_ROOT/etc/init.d/my_maintenance.d/
install -m 700 set_wake_on_lan $RPM_BUILD_ROOT/etc/init.d/my_maintenance.d/start.d
install -m 700 close_cd_tray $RPM_BUILD_ROOT/etc/init.d/my_maintenance.d/stop.d

%post
# the post section is where you can run commands after the rpm is installed.
insserv /etc/init.d/my_maintenance

%clean
rm -rf $RPM_BUILD_ROOT
rm -rf %{_tmppath}/%{name}
rm -rf %{_topdir}/BUILD/%{name}

# list files owned by the package here
%files
%defattr(-,root,root)
/etc/init.d/my_maintenance
/etc/init.d/my_maintenance.d/start
/etc/init.d/my_maintenance.d/stop
/etc/init.d/my_maintenance.d/start.d/set_wake_on_lan
/etc/init.d/my_maintenance.d/stop.d/ close_cd_tray

%changelog
* Sun Aug 01 2010  Your name here
- 1.0 r1 First release

When putting entries in the change log include the version and release number. I’ve seen change logs that only contain dates which can be frustrating. If you’re looking for the .src.rpm from before a certain change was made you want to know which version/release number the change log entry relates to. The .src.rpm filenames don’t contain dates but they do contain version/release numbers. (You can’t guarantee the date stamp on the .src.rpm file reflects the original creation date.)

If you get confused about which files you need to list in the %files section, or the list is going to be very long and you can’t be bothered to type it all in, then leave it blank and try building the rpm. You will get an error which will list all the files that ought to be listed which you can then copy/paste in to the spec file.

Now to build an rpm you need a source bundle. But you don’t have a source bundle, you have a directory containing a bunch of files. So you need to create a source bundle from that. You can either keep creating this every time you change something, or you do what I did and create a script to create the source bundle and then build the rpm. Save this as ~/bin/buildrpm (~/bin gets put in your $PATH automatically if it exists)

#!/bin/bash

if [ -z "$1" ];then
   echo "You didn't specify anything to build";
   exit 1;
fi

# delete older versions of the rpm since there's no point having old 
# versions in there when we still have the src.rpms in the SRPMS dir
find ~/rpmbuild/RPMS -name ${1}-[0-9]\* -exec rm -f {} \;

cd ~/rpmbuild/SOURCES

# if there's a directory containing the source, as there will be
# for all our own packages, then delete any .tar.gz file that may exist
# for the package and create a new one.
if [ -d ${1} ] ;then
   rm -f ${1}.tar.gz;
   tar zcf ${1}.tar.gz ${1};
fi

# build the package
rpmbuild -ba ../SPECS/${1}.spec 

# if there is a directory, then delete the .tar.gz again
if [ -d ${1} ] ;then
   rm -f ${1}.tar.gz;
fi

Then you use it by passing the name of the package as the first argument

$ buildrpm my_maintenance_scripts

When creating your own rpms, I find it’s useful to use a common prefix on the names. This way you can easily spot your own rpms in a list. E.g. the name of this example rpm starts my_. If I wanted to create an rpm containing some images for use as wallpaper I’d call it my_wallpapers. (In practice I actually use a prefix related to the name of my employer.)

There is a lot to know about rpms, far more than I know. This guide really just scratches the surface. This is the point where I feel I should cite some good sources of further information on the subject including what I found helpful when I was first getting to grips with rpms. Sadly an accident with my Firefox profile meant I lost all my bookmarks a while back so I shall just point to http://www.rpm.org/

VN:F [1.9.22_1171]
Rating: 5.0/5 (1 vote cast)
VN:D [1.9.22_1171]
Rating: +1 (from 1 vote)
Building Simple RPMs of Arbitrary Files, 5.0 out of 5 based on 1 rating

Tags: ,
Categories: SUSE Linux Enterprise Desktop, Technical Solutions

Disclaimer: As with everything else at SUSE Conversations, this content is definitely not supported by SUSE (so don't even think of calling Support if you try something and it blows up).  It was contributed by a community member and is published "as is." It seems to have worked for at least one person, and might work for you. But please be sure to test, test, test before you do anything drastic with it.

Comment

RSS