How to create a deb/rpm package for Linux (using rpmdev/mock, dpkg-buildpackage/debuild/pbuilder and fpm)

RPM and DEB are binary software packing and distribution formats used by RedHat/Debian-based distributions respectively. Both take the package source from upstream (usually a tarball) and set of scripts/rules to build binary package.

RPM

  • rpmbuild@man builds RPM packages. It takes a spec file with steps/snippets and a build tree with files and generates rpm (and optionally srpm) from source in a tarball. Use rpmdev-newspec to create a new spec file template, rpmdev-setuptree/rpmdev-wipetree to create/wipe a RPM build tree.
## install
$ sudo yum group info "Development Tools" (includes gcc, rpmdevtools, mock)

## using
rpmbuild -bSTAGE|-tSTAGE [ rpmbuild-options ] FILE ...
'-bSTAGE,-tSTAGE' build up to a stage, '-b' uses spec file givem where '-t' uses spec inside tarball
'-ba' build binary and source packages (after doing the %prep, %build, and %install stages)
'-bb' build a binary package (after doing the %prep, %build, and %install stages)
'-bp' executes the "%prep" stage from the spec file
'-bc' do the "%build" stage from the spec file (after doing the %prep stage)
'-bi' do the "%install" stage from the spec file (after doing the %prep and %build stages)
'-bl' do a "list check", the "%files" section from the spec file is macro expanded
'-bs' build just the source package
'--showrc' show the rpm macros used in spec files

## build
$(myusername) rpmdev-setuptree
$ cd ~/rpmbuild/SOURCES
$ wget http://ftp.gnu.org/gnu/hello/hello-2.8.tar.gz
$ cd ~/rpmbuild/SPECS
$ rpmdev-newspec hello

# since it uses i18 files then add 'gettext' as a build requirement, use '%find_lang %{name}' to file files in '%install' and found files in '%files -f %{name}.lang'
# since it also uses info files then use 'install-info' in '%post/%preun' to install/uninstall from system
$ cat hello.spec
Name:           hello
Version:        2.8
Release:        1%{?dist}
Summary:        The "Hello World" program from GNU
License:        GPLv3+
URL:            http://ftp.gnu.org/gnu/%{name}
Source0:        http://ftp.gnu.org/gnu/%{name}/%{name}-%{version}.tar.gz
BuildRequires: gettext    
Requires(post): info
Requires(preun): info
%description 
The "Hello World" program, done with all bells and whistles of a proper FOSS project, 
including configuration, build, internationalization, help files, etc.
%prep
%setup -q
%build
%configure
make %{?_smp_mflags}
%install
%make_install
%find_lang %{name}
rm -f %{buildroot}/%{_infodir}/dir
%post
/sbin/install-info %{_infodir}/%{name}.info %{_infodir}/dir || :
%preun
if [ $1 = 0 ] ; then
/sbin/install-info --delete %{_infodir}/%{name}.info %{_infodir}/dir || :
fi
%files -f %{name}.lang
%doc AUTHORS ChangeLog COPYING NEWS README THANKS TODO
%{_mandir}/man1/hello.1.gz
%{_infodir}/%{name}.info.gz
%{_bindir}/hello
%changelog
* Tue Sep 06 2011 The Coon of Ty  2.8-1
- Initial version of the package

$ rpmbuild -ba hello.spec
...
~/rpmbuild/SRPMS/hello-2.8-1.el7.centos.src.rpm

## lint (check confirmance)
$ rpmlint hello.spec ../SRPMS/hello* ../RPMS/*/hello*
...
$ rpmlint -I description-line-too-long 
Your description lines must not exceed 80 characters. If a line is exceeding this number, cut it to fit in two lines.

from How to create a GNU Hello RPM package
see also Packaging Guidelines

  • mock takes a srpm and creates a chroot build environment ensuring that build requirements are correct. You can build against a different environment.
$ sudo usermod -a -G mock myusername
$ newgrp mock

# build against given env chooted in /var/lib/mock/epel-6-x86_64/root
$ export MOCK_CONFIG=/etc/mock/epel-6-x86_64.cfg
$ /usr/bin/mock -r $MOCK_CONFIG --verbose --rebuild ../SRPMS/*.src.rpm
$ ls /var/lib/mock/epel-6-x86_64/result
build.log                hello-2.8-1.el6.x86_64.rpm            root.log
hello-2.8-1.el6.src.rpm  hello-debuginfo-2.8-1.el6.x86_64.rpm  state.log
$ /usr/bin/mock -r $MOCK_CONFIG --clean

# if you build requires packages not in repo then
$ /usr/bin/mock -r $MOCK_CONFIG --init
$ /usr/bin/mock -r $MOCK_CONFIG --install PACKAGE_NAME_OR_PATH_TO_RPM
$ /usr/bin/mock -r $MOCK_CONFIG --no-clean /PATH/TO/SRPM
$ /usr/bin/mock -r $MOCK_CONFIG --copyin /PATH/TO/SRPM /tmp
$ mock -r $MOCK_CONFIG --shell
$ cd ; rpmbuild --rebuild /tmp/SRPM_NAME

## speedup
$ cat /etc/mock/site-defaults.cfg
config_opts['macros']['%_smp_mflags'] = "-j17"

from Using Mock to test package builds

DEB

## install
$ sudo apt-get install dh-make debhelper devscripts fakeroot

## usage
dpkg-buildpackage [...]
'-F' (default) builds binaries and sources
'-g,-G' source and arch-indep/specific build
'-b,-B' binary-only, no-source/arch-specific/arch-indep
'-S' source-only
'-tc,-nc' clean/dont-clean source tress when finished
'-us,-uc' unsigned source package / changes file.

## example
# fetch source
$ mkdir hello ; cd $_
$ wget http://ftp.gnu.org/gnu/hello/hello-2.6.tar.gz
$ tar zxf hello-*.tar.gz ; cd hello-*

# create control files with 'dh_make'
# note: dont re-run dh_make, it isnt idempotent
$ dh_make -s -y --copyright gpl -f ../hello-*.tar.gz
$ cd debian
$ rm -f rm *.ex *.EX README.Debian info docs
$ tree
.
├── changelog
├── compat
├── control
├── copyright
├── README.source
├── rules
└── source
    └── format

# update changelog
$ dch -i "Added README.Debian"
# edit control file, adding any extra 'Build-Depends'; note that 'Depends' are automatically added
$ cat control
Source: hello
Section: unknown
Priority: optional
Maintainer: Rui Coelho 
Build-Depends: debhelper (>= 9), autotools-dev
Standards-Version: 3.9.5
Homepage: 
#Vcs-Git: git://anonscm.debian.org/collab-maint/hello.git
#Vcs-Browser: http://anonscm.debian.org/?p=collab-maint/hello.git;a=summary

Package: hello
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: 

# build
# uses rules file to call Makefile
$ cd .. ; dpkg-buildpackage -rfakeroot

# check for errors and view content
$ cd .. ; ls
hello-2.6  hello_2.6-1_amd64.changes  hello_2.6-1_amd64.deb  hello_2.6-1.debian.tar.xz  hello_2.6-1.dsc  hello_2.6.orig.tar.gz  hello-2.6.tar.gz
# '.dsc' is source code content sumamry, used when unpacking source with 'dpkg-source'
# '.debian.tar.xz' is the 'debian' directory, each update is stored as quilt patch in 'debian/patches'
# '.deb' is complete binary, use 'dpkg' to install/remove
# '.changes' describes changes made, partly generated from changelog and '.dsc'
$ lintian hello_*.dsc
$ lintian hello_*.deb
$ lesspipe hello_*.deb

# install
$ sudo dpkg --install hello_*.deb
$ /usr/bin/hello
Hello, world!
$ sudo apt-get remove hello

# re-create package from scratch, uses '.dsc,.orig,.debian'
$ dpkg-source -x *.dsc
$ cd hello* ; dpkg-buildpackage -rfakeroot

see also Debian New Maintainers’ Guide, Debian Binary Package Building HOWTO, The Debian Administrator’s Handbook and Packaging New Software @ ubuntu

  • debuild@man is a wrapper around dpkg-buildpackage + lintian (with configuration in_ /etc/devscripts.conf_ or ~/.devscripts), used to package new upstream releases. Use uupdate to upgrade a source code package from an upstream revision.
## usage
debuild [debuild options] binary|binary-arch|binary-indep|clean ...

## example
$ cd hello ; wget http://ftp.gnu.org/gnu/hello/hello-2.7.tar.gz
$ cd hello-2.6 ; uupdate -u hello-2.7.tar.gz
$ cd ../hello-2.7; debuild -rfakeroot
$ ls ../*2.7*
hello_2.7-0ubuntu1_amd64.build    hello_2.7-0ubuntu1.debian.tar.xz  hello-2.7.tar.gz
hello_2.7-0ubuntu1_amd64.changes  hello_2.7-0ubuntu1.dsc
hello_2.7-0ubuntu1_amd64.deb      hello_2.7.orig.tar.gz
$ debuild clean

## another example (package w/o make)
$ mkdir myapp-0.1 ; cd myapp*
$ echo -e '#!/bin/bashnecho Hello World' > hello.sh
$ dh_make -s --indep --createorig
$ rm debian/*.ex
# specify files to install
$ echo hello.sh /usr/bin > debian/install
# modify source format, since we arent using quilt packages
$ echo "1.0" > debian/source/format
# add any extra 'Depends' in 'debian/control'
$ cat debian/control
# build
$ debuild -us -uc
$ ls ../*deb
../myapp_0.1-1_all.deb
# test
$ sudo dpkg -i myapp_0.1-1_all.deb
$ /usr/bin/hello.sh 
Hello World
$ sudo dpkg -r myapp
  • pbuilder@man used for creating and maintaining chroot environment and building Debian package in the chroot environment. Use pdebuild@man as pbuilder + dpkg-buildpackage (build in the chroot).
## install
# sudo apt-get install pbuilder

## example
# create/update a base '/var/cache/pbuilder/base.tgz' with chroot env, 
$ sudo pbuilder clean
# note: use '--distribution sid' to switch distro
$ sudo pbuilder create
$ sudo pbuilder --update

# rebuild package
$ sudo pbuilder --build hello_2.7-0ubuntu1.dsc
$ ls /var/cache/pbuilder/result/
hello_2.7-0ubuntu1_amd64.changes  hello_2.7-0ubuntu1.debian.tar.xz  hello_2.7.orig.tar.gz
hello_2.7-0ubuntu1_amd64.deb      hello_2.7-0ubuntu1.dsc

# sign '.dsc' and '.changes'
$ cd /var/cache/pbuilder/result/
$ debsign hello_2.7-0ubuntu1_amd64.changes

# log into chroot env
$ sudo pbuilder --login --save-after-login

# to get sources/.dsc either
$ cat /etc/apt/sources.list
deb-src http://archive.ubuntu.com/ubuntu <ubuntu_version> main restricted universe multiverse
# or use dget from 'http://packages.ubuntu.com/' or 'http://packages.debian.org/'
$ dget http://ftp.de.debian.org/debian/pool/main/h/hello/hello_2.9-2.dsc
$ sudo pbuilder --build hello_2.9-2.dsc
$ ls /var/cache/pbuilder/result/hello_2.9*

# using pdebuild to debuild in chroot
$ apt-get source hello
$ cd hello-2.9 ; pdebuild

from Pbuilder Howto

  • Command/Tools hierarchy
    • debian/rules maintainer script for the package building
    • dpkg-buildpackage core of the package building tool
    • debuild dpkg-buildpackage + lintian (build under the sanitized environment variables)
    • pbuilder core of the Debian chroot environment tool
    • pdebuild pbuilder + dpkg-buildpackage (build in the chroot)
    • cowbuilder speed up the pbuilder execution
    • git-pbuilder the easy-to-use commandline syntax for pdebuild (used by gbp buildpackage)
    • gbp manage the Debian source under the git repo
    • gbp buildpackage pbuilder + dpkg-buildpackage + gbp

FPM

  • fpm@github “Effing Package Management” builds packages for multiple platforms (deb, rpm, etc) with great ease and sanity.
## install
$ sudo apt-get install ruby-dev gcc | sudo yum install ruby-devel gcc
$ sudo gem install fpm

## usage
fpm -s <source type> -t <target type> [list of sources]...
'-n name, -v version' package name and version
'-C chdir' change directory before searching files
'--prefix prefix' path to prefix when building  
'-d,--depends dep>version' dependencies
'-a,--architecture arch' architecture, usually 'uname -m'
'--inputs file' file with newline-separated list of files and dirs
'--before/after-install/remove/upgrade file' script to run at given stage 

## examples
# create noarch deb from dir content
$ echo -e '#!/bin/bashnecho Hello World' hello.sh
$ fpm -s dir -t deb -a all -n hello -v 1.0 --prefix /usr/bin hello.sh
$ dpkg -c *.deb
... ./usr/bin/hello.sh
$ sudo dpkg -i *.deb
$ . /usr/bin/hello.sh
Hello World
$ sudo dpkg -r hello

# create deb from python modulo using easy_install 
$ fpm -s python -t deb django
$ dpkg -c *.deb
... ./usr/local/lib/python2.7/dist-packages/django

# convert a local python package source
$ fpm -s python -t deb myproj/setup.py

# build gnu hello
$ wget http://ftp.gnu.org/gnu/hello/hello-2.9.tar.gz
$ tar -zxf hello-*.tar.gz
$ cd hello* ; ./configure --prefix=/usr ; make ; make install DESTDIR=/tmp/installdir
$ cd .. ; fpm -s dir -t deb -n hello -v 2.9 -C /tmp/installdir usr
$ dpkg -c hello*.deb
... ./usr/bin/hello

from fpm@wiki

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s