Building RPM packages with rpmbuild

Jun 29, 2015 • packagecloud

TL;DR

This post will walk you through creating a RPM package from a simple hello world C program using rpmbuild.

Setup

On an RPM-based system, install the following programs:

$ sudo yum install rpm-build

Additionally, you can create an .rpmmacros file in your home directory, which rpmbuild can use for determining various settings. A helpful setting is %_topdir which tells rpmbuild the base directory of where it will find the necessary components for building your package.

A common setting is to put %_topdir in your home directory, like this:

%_topdir    $HOME/rpmbuild

When you run rpmbuild the first time, it’ll construct a series of directoriesr under %_topdir, or you can create them yourself:

$ mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}

Our “Hello World” program

For this example, we’ll be packaging a simple hello world program. You should begin by creating a directory for the source file and Makefile:

$ mkdir helloworld-1.0

Next, you should create the files that comprise helloworld. In this case it consists of the source file main.c and a Makefile.

The main.c is:

#include <stdio.h>
int main (int argc, char *argv[]) {
  printf("Hi\n");
  return 0;
}

And the Makefile:

DESTDIR ?=
PREFIX ?= /usr/local

helloworld:
  gcc main.c -o helloworld

install: helloworld
  install -m 0755 -d $(DESTDIR)$(PREFIX)/bin
  install -m 0755 helloworld $(DESTDIR)$(PREFIX)/bin

Building and Testing

Let’s make sure everything works before we package it up. Run make to build the program. This should create a helloworld binary you can execute by running ./helloworld.

You should see “Hi” printed to the screen.

Then, try installing the binary: run sudo make install and now helloworld should be in /usr/local/bin.

Remove our test binary before continuing:

$ sudo rm -rf /usr/local/bin/helloworld

Packaging our program

Now that we’ve verified everything works, let’s create an RPM package for it.

1. tar up the source files

$ tar -czvf helloworld-1.0.tar.gz helloworld-1.0/

The name of the tarball will be used in the next step.

2. Prepare the source directory

The build tool rpmbuild can use source from a URL or from a local file. In our example, we’ll be using our tarball on our local system.

rpmbuild will look in the SOURCES directory under %_topdir. If you used the .rpmmacros from above, your directory would be: ~/rpmbuild/SOURCES.

So, following that example, copy the source tar ball into the SOURCES directory, so rpmbuild can find it:

$ cp helloworld-1.0.tar.gz ~/rpmbuild/SOURCES

3. Create a SPEC file

A SPEC file describes to rpmbuild how to build and package the software. In this case, it’s a C program that must be compiled and copied into the appropriate directory for installation.

Note the setting for Source0 below. This can be a URL, if desired. In this example, we are simply using a file.

Name:           helloworld
Version:        1.0
Release:        1%{?dist}
Summary:        A hello world program

License:        GPLv3+
URL:            https://blog.packagecloud.io
Source0:        helloworld-1.0.tar.gz

Requires(post): info
Requires(preun): info

%description
A helloworld program from the packagecloud.io blog!

%prep
%setup

%build
make PREFIX=/usr %{?_smp_mflags}

%install
make PREFIX=/usr DESTDIR=%{?buildroot} install

%clean
rm -rf %{buildroot}

%files
%{_bindir}/helloworld

4. Build the RPM with rpmbuild

You can now run the rpmbuild tool to create an RPM package from this SPEC file:

$ rpmbuild -ba helloworld.spec

The -ba flag will build the source RPM (.src.rpm) and binary RPM (.rpm) packages.

You’ll see a bunch of output giving useful debug messages that you can use to tweak your SPEC file, should the build fail. You’ll notice toward the bottom of the output, rpmbuild will list the files it is creating:

Wrote: /home/joe/rpmbuild/SRPMS/helloworld-1.0-1.el6.src.rpm
Wrote: /home/joe/rpmbuild/RPMS/x86_64/helloworld-1.0-1.el6.x86_64.rpm

You can now copy the SRPM and RPM out of the output directory and into your YUM repository.

Sign your package with GPG using rpmsign or rpm --sign

Refer to our other post on this topic: GPG signing RPM packages and YUM repositories.

Conclusion

Creating RPM packages gives you full control over how a program is compiled and the specific version being packaged. Uploading those packages to packagecloud lets you easily distribute them to all of your machines, exactly how you built it.

Never miss an update!

Subscribe to our RSS feed