RPM Packaging – Building and Deploying your own PHP

by Mike Willbanks on December 21st, 2011

I’ve been building all sorts of RPM’s lately, from vim 7.3 to mirroring the zend server repository and building pecl extensions. In the PHP world, one might ask why not just build it from source? Well, an RPM IS built from source and then distributed to many servers – we can ensure that we have the same packages on each, we can maintain the same versions and if you’ve read my previous post on Pirum you will know that I also like mirroring PEAR packages. It allows us to simply maintain our versions on each server and lower the maintenance in a larger environment. Not to mention, since it is on the inside of our network, it is insanely quick for the downloading of the packages and maintaining our servers.

If you are not doing packaging, I hope this entices you to start doing some packaging. I will show you an example of building a PHP RPM based on the distribution (as it is actually how I ensure this server is versioned appropriately and I don’t lose my build from source scripts). Oh; and I’ve only been promising this blog post for approximately 6+ months – so those expecting it; here it is!

Overview

To build an RPM is fairly easy, however, there are a few tools that you will need in order to build everything out:

  • rpmbuild
  • rpm-devel
  • rpm-libs

Once you have these packages, you have everything that you will need to start building out your package.

The Mechanics of a Package

There are a few parts of an RPM package that you will need to know in order to build it.

  • A Specification File
  • The Source

The specification file will explain to the rpmbuild utility how we are going to be building out the package and the source is what we are going to be building from. Pretty plain and simple, right?

Specification File

When we are building out an RPM specification file, there are many components that we need to think through as well as a large amount of macros. We will be avoiding many of the macros as they do sometimes change the path as to where we may be installing from. Let’s start with the full spec file and we will explain from there. This file needs to be in “/usr/src/redhat/SPECS”.

Name:           php
Version:        5.3.8
Release:        1%{?dist}
Summary:        PHP is a widely-used general-purpose scripting language.

Group:          Development/Languages
License:        PHP License v3.01
URL:            http://www.php.net
Source0:        http://www.php.net/distributions/php-%{version}.tar.gz
BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-buildroot

Obsoletes:      php

%description
PHP is a widely-used general-purpose scripting language that is especially
suited for Web development and can be embedded into HTML.

%prep
%setup -q -n %{name}-%{version}
%build
EXTENSION_DIR=%{_libdir}/php/modules; export EXTENSION_DIR
%configure --with-layout=GNU --with-libdir=lib64 --with-enchant \
--enable-fpm --with-gd --enable-intl --enable-mbstring --enable-pcntl \
--enable-soap --enable-sockets --enable-sqlite-utf8 --enable-zip --with-zlib \
--with-curl --with-jpeg-dir --with-png-dir --with-zlib-dir --with-gettext \
--with-mcrypt --with-mysql=mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd \
--with-pdo-sqlite --with-tidy --with-pear=%{_datadir}/php/pear --disable-debug

make %{?_smp_mflags}

%install
rm -rf %{buildroot}
mkdir -p %{buildroot}%{_initrddir}
install -Dp -m0755 sapi/fpm/init.d.php-fpm.in %{buildroot}%{_initrddir}/php-fpm
%{__make} install INSTALL_ROOT="%{buildroot}"

%clean
rm -rf %{buildroot}

%post
%/sbin/chkconfig --add php-fpm
%/sbin/chkconfig --level 2345 php-fpm on

%preun
if [ "$1" = 0 ] ; then
    /sbin/service php-fpm stop > /dev/null 2>&1
    /sbin/chkconfig --del php-fpm
fi
exit 0

%postun
if [ "$1" -ge 1 ]; then
    /sbin/service php-fpm condrestart > /dev/null 2>&1
fi
exit 0

%files
%defattr(-,root,root,-)
%{_bindir}/*
%{_sbindir}/*
%{_includedir}/*
%{_libdir}/*
%{_mandir}/man1/php*
%{_sysconfdir}/*
%{_datadir}/*
%{_initrddir}/*
%exclude /.channels
%exclude /.depdb
%exclude /.depdblock
%exclude /.filemap
%exclude /.lock

%changelog
* Wed Dec 21 2011 Mike Willbanks  - 5.3.8-1
- Updated to 5.3.8
* Tue Feb 23 2011 Mike Willbanks  - 5.3.5-1
- Initial Package

The Header

Name:           php
Version:        5.3.8
Release:        1%{?dist}
Summary:        PHP is a widely-used general-purpose scripting language.

Group:          Development/Languages
License:        PHP License v3.01
URL:            http://www.php.net
Source0:        http://www.php.net/distributions/php-%{version}.tar.gz
BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-buildroot

Obsoletes:      php

%description
PHP is a widely-used general-purpose scripting language that is especially
suited for Web development and can be embedded into HTML.

There are several items here that are fairly self explanatory. However, I would like to point out 3 of the items just for clarity on what they do. As you can also see we use these as variables inside of themselves.

  • Source0: the base path of this is what the actual file is at /usr/src/redhat/SOURCES. It should also be the same as where you are downloading from.
  • BuildRoot: the directory where all of the RPM building should happen, this is where we actually do a build and install into that directory.
  • Obsoletes: should be used if this package overrides existing packages in the distribution.

Macros

RPM specifications contain several macros that automate much of the behavior for you in order to get everything packaged and up and running. These are a bit out of scope of this blog post, however, you can see the ones that are being utilized from the % sign followed by the actual command. See the Inside the Spec file page to see what some of these mean.

Making the RPM

Now, making an RPM is much like compiling from source code. There are macros that will help you with this such as %prep, %setup, %build and %install. It may seem intimidating at first; but actually far easier than you might originally think.

%prep
%setup -q -n %{name}-%{version}
%build
EXTENSION_DIR=%{_libdir}/php/modules; export EXTENSION_DIR
%configure --with-layout=GNU --with-libdir=lib64 --with-enchant \
--enable-fpm --with-gd --enable-intl --enable-mbstring --enable-pcntl \
--enable-soap --enable-sockets --enable-sqlite-utf8 --enable-zip --with-zlib \
--with-curl --with-jpeg-dir --with-png-dir --with-zlib-dir --with-gettext \
--with-mcrypt --with-mysql=mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd \
--with-pdo-sqlite --with-tidy --with-pear=%{_datadir}/php/pear --disable-debug

make %{?_smp_mflags}

%install
rm -rf %{buildroot}
mkdir -p %{buildroot}%{_initrddir}
install -Dp -m0755 sapi/fpm/init.d.php-fpm.in %{buildroot}%{_initrddir}/php-fpm
%{__make} install INSTALL_ROOT="%{buildroot}"

%clean
rm -rf %{buildroot}

%prep: is really just a container prior to %build and %setup; which contains the very important %setup! The %setup macro is used to unpack the sources. Generally you might not use the options but in our specific case we want to ensure the directory that it is extracted to. The -n creates the name of the build build directory where these sources will be extracted. Don’t worry about the other param.

%build: this parameter is basically anything that that you would like to be executed by sh prior to %configure :) this can also be said for the previous sections. In PHP our extremely important variable is our EXTENSION_DIR which is utilized for where our extensions will be placed. We also have a %configure macro which is a short-hand for essentially running ./configure for our configure from source folks. If you seriously need an explanation of this; well google is your friend.

Then we run the make command (remember we are really just talking with the sh shell at this point in time. Don’t worry again about the macro contained in there… it is really just some semantics for jobs running simultaneously. Now you should be ready for all of the installation!

%install: is simply installing everything to our build root (aka “make install”). Since I am using php-fpm and we have a init.d in file we can simply handle that through the install command. As an additional note; if you want PHP to actually build to an RPM there is a single parameter that YOU MUST utilize: “INSTALL_ROOT”. In many other cases this variable is different or not relevant. However, we must use it for PHP to put it into the build root. Miss this parameter and well; forget about it. And we have %clean which simply cleans up anything in the build root.

RPM Installation

Finally; the good stuff. We need to define how to install this on our systems. This includes %pre, %post, %preun, %postun, %files and %changelog. Which simply defines %pre = pre-install, %post = post-install, %preun = pre-uninstall, %postun = post-uninstall, %files = the files to install and %changelog = what we’ve simply changed. Seriously all of these are fairly self definable if you read the RPM documentation. However, we should talk about %files… Insideo %files, you MUST define every single file that will be built; if you do not… well then your RPM build will fail.

The Source

Whenever you define the source in the specification it MUST live inside of the “/usr/src/redhat/SOURCE” directory. If it does not then it will simply not work. Ensure you’ve downloaded the source prior to executing the build.

Execution

In order to execute the build you need to execute “rpmbuild -ba php.spec” within the “/usr/src/redhat/SPECS” directory. After all of this you should have a RPM inside of the “/usr/src/redhat/RPMS” directory which you can install on the platform architectures that follow the current platform architecture. Hope this article serves you well and happy building!

From PHP

3 Comments
  1. Good post. I’ve been using FPM to build my packages and it is working quite well:

    https://github.com/jordansissel/fpm

  2. mario permalink

    I’m using EPM for this precise purpose. That’s less fiddly details to deal with (in comparison to dpkg-deb or rpm-build), and gives me RPMs and DEBs, or also win32 SFXs. Obviously that’s for bland static packages without configuration/setup magic scripts.

    Wonderous why there is no default pear2rpm, pear2deb, pear2xyz toolchain anyway. (Dreaming up a dual system+userland package distribution system lately, but can’t be bothered…)

  3. Roman permalink

    Hey, can you please help me? I using the following spec file:

    %prep
    %setup -q -n %{name}-%{version}
    %build
    EXTENSION_DIR=%{_libdir}/php/modules; export EXTENSION_DIR
    PATH=”%{_bindir}:$PATH” ; export PATH
    ./configure –with-apxs2=/httpd/bin/apxs \
    –prefix=/opt/apache.org \
    –with-libxml-dir=/opt/apache.org \
    –with-gd \
    –with-pgsql \
    –with-zlib-dir=/lib \
    –with-jpeg-dir=/usr/bin \
    –with-pear=%{_datadir}/php/pear \
    –disable-debug

    make %{?_smp_mflags}

    %install
    rm -rf %{buildroot}
    mkdir -p %{buildroot}%{_initrddir}
    %{__make} install INSTALL_ROOT=”%{buildroot}”

    %clean
    rm -rf %{buildroot}

    but I get the following error on pear install:

    Warning: fopen(tmp/php-5.3.17-oc1-i386-buildroot/opt/apache.org/share/php/pear/.depdb): failed to open stream: No such file or directory in phar:///BUILD/php-5.3.17/pear/install-pear-nozlib.phar/PEAR/DependencyDB.php on line 548

    Warning: fopen(tmp/php-5.3.17-oc1-i386-buildroot/opt/apache.org/share/php/pear/.depdb): failed to open stream: No such file or directory in phar:///BUILD/php-5.3.17/pear/install-pear-nozlib.phar/PEAR/DependencyDB.php on line 548

    Fatal error: Cannot use object of type PEAR_Error as array in phar:///BUILD/php-5.3.17/pear/install-pear-nozlib.phar/PEAR/DependencyDB.php on line 199
    make[1]: *** [install-pear-installer] Error 255
    make: *** [install-pear] Error 2

    Is there anything obvious that I’m missing?
    Thanks!

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS