1- Introduction

This document details how to compile a full-featured Apache/PHP distribution.

The main difference with other tutorials you may find on the web is that
this document not only explains how to compile Apache and PHP, but also every
library they depend on.

This is a 'black-box' approach, where the software
comes with everything it needs to run. It is longer and more complex to build such
a package but, once it is done, it brings a lot of benefits, such as an independance
between your software package and the libraries
that may be already installed on the target host. Such a 'self-contained'
approach makes it easier to build and deploy functionaly-identical packages on several
different platforms/OS. Of course, the software still uses the low-level system
libraries, but the dependencies are
limited to the bare minimum, minimizing the risk of inconsistent behaviors
between different hosts/platforms.

To complement this 'black-box' approach, we install the software in a
private directory. This makes the package much simpler to manage, deploy ,
upgrade, or remove (compared to the traditional method
of installing everything under /usr/local/[bin|lib|...]).

Actually, we don't
include everything needed by the package, as we only include everything we can compile. The exceptions are the Oracle client
library and the JAVA JRE, which are considered as 'external dependencies'
and accessed through symbolic links (so that they can be physically
located anywhere on the target host).

1.1- Intended audience

This document can be used in two different ways :

In order to apply the procedure and build the software exactly as it is
described here, you don't need to be an expert : it just requires some basic
Unix and shell knowledge, such as being able to download the source
packages, uncompress/untar them, and create files with a text editor.
At this level, the procedure given below can be splitted in two main steps :

you first set a limited number of environment variables (essentially
the installation directory)

and the rest is just a suite of copy/paste operations from the
document to your shell environment.

Now, if you want to understand everything that is explained below, or if
you want to adapt it to your specific needs, it requires of course more
knowledge on different domains, mostly :

and even, when it is really needed, the ability to understand and
adapt some C source code.

So, to resume, most people will just apply the procedure described below, which is
relatively easy and just requires a basic working knowledge of the Unix shell
environment.

Don't hesitate to post comments on issues you may have with this
document. Each time you do it, you help me and the community in general to make
the procedure more robust and reliable. I promise to reply to every question
asked in comments. Please prefer comments to direct mail as they may benefit
others.

1.2- Getting the source packages

Some readers sometimes ask why I don't provide copies of the source
packages I list below. Sure, it would be easier for you, but I won't do it as you
MUST get your source packages from a repository you can
trust ! You don't know me, you CANNOT trust source code downloaded from my
site (except
for my own projects).

So, once again, always get your
software from a trusted source and, when a package signature is provided, check it to make sure you get the official
code.

I insist on this because compiling some modified source code or installing
some corrupted precompiled binaries would give hackers everything they need to take
control on your site (remember that most software we compile here runs as root).

In every chapter below, you will find a link to the best place to get the
source package from.

So, remain a good, slightly paranoid, system administrator, and everything
will go well.

1.3-
Modules & extensions

We are building Apache 2.0, PHP 4, and PHP 5 with the following modules and extensions :

We also include the mm
session handler support in PHP 4 & 5 (to be activated via the
'session_handler=mm' directive in php.ini)

1.4-
Software dependencies

The following diagrams display the dependencies between the software packages
and libraries we are integrating together (split as two diagrams for clarity) :

Note that we use the GD bundled library provided in the PHP source packages (4 &
5).

The package also includes the tools you need to build
additional PHP extensions from the PECL library (http://pecl.php.net).
Although these tools (autoconf & m4) are available in every Linux
distribution, it is not the case
on older/traditional/proprietary Unix. In order to remain consistent for every OS we support
in this set of documents, the package includes an embedded version of m4 and autoconf.
The phpize scripts are also modified to use this version.

1.5-
Notational conventions

Everything written in black on grey can be copy/pasted without any
modification.

Everything written in red and between '<' and '>'
symbols must be replaced by a value corresponding to your environment.

Example : when
you read this :

dir=<$BASE>

you must replace <$BASE> with your base directory
path in the line you will actually type.

but, when you read this :

dir=$BASE

you
copy and paste it asis into your environment.

Everything related to
Oracle is printed in green and can be safely ignored if you don't need Oracle connectivity.

1.6- Context

Host model :

VMWare virtual host (on a Dell R900)

OS :

Linux 2.6.18-92.el5

Hardware platform :

x86_64 (64 bits)

RedHat release :

Red Hat Enterprise Linux Server release 5.2 (Tikanga)

Shell :

/bin/bash

1.7- Pre-requisite

These products are mandatory but the versions are not. They are just the versions I
used, so they are known to work.

Product

Version

gcc (C & C++)

4.1.2-42.el5

glibc (32 & 64), glibc-devel (32 & 64), glibc-headers

2.5-24

Gettext

0.14.6-4.el5

Flex

2.5.4a-41.fc6

GNU make

3.81-3.el5

pam-devel

0.99.6.2-3.27.el5

JDK

1.5

rpm-build

4.4.2-48.el5

Oracle client

10.2.0.2

2- Building

2.1-
Environment

First, we define the install (target) base directory :

export BASE=<your install directory>

We ensure that the LIBPATH, LD_LIBRARY_PATH, and SHLIB_PATH variables are unset.
Only one of them applies to this system but it is simpler to unset them all on every OS :

unset LIBPATH LD_LIBRARY_PATH SHLIB_PATH || :

If the shell
environment contains aliases for cp, rm, or mv, it is time
to unalias them :

unalias cp rm mv 2>/dev/null || :

Then, we set the variables we use for every software and on every OS. Among
others, we ensure that /usr/local/bin is in the PATH :

C compilers can have such different behaviors that I strongly recommend
using gcc. If you are using another compiler, please don't ask for support.

Now, we set the 'make' commands we'll use in the rest of the document. The
difference between $MK
and $SMK is that, in $MK, you can add options for a 'parallel make' ('-j', '-l',
...). $SMK stands for 'serial make' and is used where parallel builds are known
to fail.

Mileage varies concerning parallel make. Depending on your host
characteristics and the options you set, the build can be faster or slower.
Personnally, after trying several options, I did not observe a noticeable gain
in performance and reverted to the default 'serial' make.

Please note that parallel make can
cause compilation failures. So, if you get unexpected results
while parallel make options are active, the first thing to do is to retry the same
without the parallel options. For more info about parallel make, see the
GNU make manual.

GNU make is mandatory as several packages absolutely require it. On
Linux, GNU make is native but, on non-Linux systems, you are strongly advised
not to use the native make.

2.2- Creating the target structure

Every libraries and auxiliary software are installed under
'$BASE/libs'. There, each software/library is assigned a subdirectory, and we populate a 'common'
subdirectory with symbolic links to the actual file locations. Using a common directory
populated with symbolic links brings several benefits :

it allows to set a single path in the dynamic lib search
path (LDFLAGS).

it also allows to set a single path in the include file search path
(CPPFLAGS)

Putting a common 'bin' subdirectory as first element of the PATH ensures
that anything we put in this directory will be found first when searching
for an executable command.

Last, it allows to set a single path in the PKG_CONFIG_PATH variable.

Thanks to this directory structure, we don't have to modify the environment
variables each time we add a library to the package. It becomes also easier to
ensure that the compilation will reference our embedded library/include files,
and not the ones that can be installed elsewhere in the system.

First, we create the base common directory.

mkdir -p $BLIB/common

Then, we create the script to
populate the common directories. Every
time we build and install a library we need to reference in a subsequent build, we
call this
script to register the files in the common structure. This is done through the
$LINKDIR environment variable.

Note that the
link creation script is not in the common PATH. This way, it cannot conflict
with another file.

#-- Declare the variable we use to
call the script

export LINKDIR=$BLIB/common/linkdir

#-- Create the script

echo 'cd $BLIB/$1

#-- If second argument is unset, default to the last element of target dir (usual
case)

cdir=$2
[ -z "$2" ] && cdir=`basename "$1"`

#-- Directory to contain links. Create common subdirectory when needed

2.3- External dependencies

We use symbolic links for the external dependencies (Oracle & Java). This
way, the package can easily run on a target host where Oracle and/or Java are
installed in different locations. On the target host, we just need to create
two symbolic links at the same locations and the Oracle and Java dependencies will be
resolved through them.

I clearly prefer this solution to the ones based on the LD_LIBRARY_PATH/SHLIB_PATH/LIBPATH
environment variables.
This is consistent with the 'black-box' philosophy described above, as I
consider that I cannot
really control the environment my software will run in.

2.3.1- Oracle

As explained above, we create a symbolic link from
$BLIB/oracle_home to the Oracle install directory.

We also create a second indirection
level because the Oracle library hardware path is recorded with the version
suffix (libclntsh.so.9.0, libclntsh.so.10.1,...). So, at runtime, the software
expects the same library version it was compiled with. As we want to be able to
run with any Oracle library, even if it is not the one we used at compile time,
we need to create a redirection from this 'versioned' library file to the actual
'not-versioned' file.

Note that the package is
never guaranteed to run with an Oracle client of a version different than the
one it was compiled with. For instance, if you compile with an Oracle 10
library, it won't run with an Oracle 9 version (because of a symbol added in
version 10). When it is possible, the best solution is always to compile and run
a software with the same library versions.

2.3.2- Java

The external Java SDK will be accessed through a symbolic link.

ln -s $JAVA_HOME $BLIB/java_home

At
runtime, we just need a Java Runtime Environment, not the whole JDK.

2.5- Compiling the OpenSSL library (0.9.8j)

The OpenSSL library provides the mechanisms to establish SSL-secured connections. It is
used by Apache to handle HTTPS connections, by PHP to provide SSL features, and
also by OpenLDAP, curl, MySql and
PostgreSql.

As the configuration process ignores the LDFLAGS environment
variable, we modify several variables related to the way
the libraries will be linked.

2.10- Compiling Apache (2.0.63)

As our LDAP library is not in the
standard location, we need to build and install the apr and apr-util libraries
first, splitting the compile process in 3 steps.

We also modifiy the
apr-config script because we want apxs to link with the hardcoded library search
path.

Fix : if the host contains an older version of the zlib headers in
/usr/include, it may contain a file named zutil.h. In this case, the Apache
configure phase wrongly states that it can include this file. But, this header
file is incompatible with the version of zlib embedded in the package, and the
compilation fails. In order to force this header file not to
be used, we un-define the corresponding symbol in the auto-generated Apache header file.

#-- Build apr

cd srclib/apr
./configure --prefix=$BAP --enable-threads $CF_OPTS

$MK
$MK install

#-- Insert the hardcoded library search path and the common include directory in apr-config

2.13- Compiling mod_jk (tomcat-connectors 1.2.27)

The configure script prepends a '-Wl,' prefix to every elements of '$LDFLAGS'
that don't already start with this string. This is correct except for the '-L'
option, as it prevents the libgcc_s library to be located in $BLIB/common/lib. To fix this problem, after the configure phase, we reset the '-L'
option where it has been modified.

2.17- Compiling mod_auth_cas (1.0.8)

mod_auth_cas allows to authenticate against a
CAS (Central
Authentication Service) server. This module is not the only way for an
application to authenticate users against a CAS server, but it is probably one
of the easiest to use, as it transparently redirects the requests, silently
validates tickets, and automatically provides an authenticated '$REMOTE_USER' to
the application.

From the source directory given above, download the
mod_auth_cas.c and mod_auth_cas.h files.

Then, compile it through apxs :

$BAP/bin/apxs -c -i -A mod_auth_cas.c

In this configuration, mod_auth_cas is inactive. To activate it,
uncomment the corresponding LoadModule line in httpd.conf.

2.20- Compiling the jpeg library (v6b)

the ltconfig script included in the libjpeg source package is too old and does not
recognize
64 bit architectures. As there is no way to pass a given host type to ltconfig, we
force it in the config.guess script :

echo 'echo "i686-pc-linux-gnu"; exit 0' >config.guess

The 'make install' process is unable to create the
directories where the files must be installed. So, we create them by ourselves
before installing.

2.21- Compiling the freetype library (2.3.7)

The freetype compile process wrongly states that gcc needs a '-ansi' option
to compile in ansi mode. This option is not needed, and it even causes the
compilation to fail in several environments (at least HP-UX 11.00 and AIX). So,
we remove this option from the compile string.

When curl needs to validate a certificate, it will use
the content of the $BLIB/curl/share/ca-bundle.crt file. By default, the file does not
exist, and the certificate chain is considered as empty. This file can be
created and filled with the CA certificates you want curl to consider as trusted
(in PEM format).

#-- Fait pointer le CA bundle de curl vers les
certificats RATP

ln -s ../../../comp/data/cacerts.pem $BLIB/curl/share/ca-bundle.crt

2.24- Compiling modsecurity (2.5.7)

You need to register (free) to access the download area of the Breach
security labs site. Once you get there, the source package you need is
named modsecurity-apache_x.x.x.tar.gz. You don't need to dowload the
'modsecurity-core-rules' file, as the rules are already included in the
source package.

2.25- Compiling the ncurses library (5.7)

We force the Makefiles to link through gcc, instead of calling
ld directly, because we want our LDFLAGS to be used and it may
contain options which are not supported by ld (as the options starting with
-Wl).

2.26- Compiling gettext (0.17)

The '-with-included-gettext' configure option forces to build and use the
included libintl library, even if one is present in the system libraries. This
removes a dependency to the system libraries and ensures that we use the same
libintl code across all platforms.

Note that, even if we try to disable NLS localization everywhere it is
possible, some software don't allow it to be disabled. That's why we still need
to embed a libintl library.

2.27- Compiling the Subversion modules (1.5.4)

The Apache modules we build here allow to use Apache as a gateway to access a
Subversion repository. You will find more information about configuring Apache
as a Subversion gateway in
this document.

Subversion requires the apr-util Apache library to be compiled with
the Berkeley DB.

We need to tell configure how to find the required libraries at link time (by
initializing the LIBS variable). If we don't, the configure phase fails when the
libintl (gettext) library is not installed in /usr/lib. This is probably a bug
in the configure script. Because of this problem, we also need to add '-lintl'
to the list of libraries we link with.

2.28- Compiling the readline library (6.0)

The default Makefile renames the object files with a '.so' suffix instead of the
usual '.o'. Some OS (like AIX) cannot link files ending with '.so'. So, we
modify the Makefile in shlib to keep the '.o' suffix.

We also need to force the commands used to link the shared libraries, as the
Makefile uses ld by default, and we can have some gcc-specific options in
CPPFLAGS/LDFLAGS. So, we force it to link using 'gcc -shared'.

2.31- Compiling the GnuTLS library (2.6.4)

We don't compile the C++ library as we don't need it and it
creates an unnecessary dependency on the libstdc++ library.

The installation
fails (on AIX) with an 'Arg list too long' message when trying to install in the
'doc' subdirectory. As we don't need to build nor install the documentation, the
easiest solution is to remove the 'doc' subdirectory from the list of
subdirectories in the Makefile :

2.33- Compiling the IMAP library (2007d)

The IMAP distribution does not provide a way to build a shared client library.
So, we do it our way : we build the static library first, with the '-fPIC'
compile flag to generate relocatable objects, and, then, we build the shared
library from these objects.

We need a libc-client.a file in
$BLIB/imap/lib because the PHP configure process specifically tests for the
presence of this file. But we don't really need the static library as we'll only use
the dynamic one. So, we create $BLIB/imap/lib/libc-client.a as a symbolic link
to the dynamic library.

2.35- Compiling the PostgreSQL client library (8.3.5)

The configure process does not use CPPFLAGS and LDFLAGS correctly (the problem
only appears when the libraries are not installed in the usual locations). As a
workaround, we feed it with modified values for some other environment
variables.

2.36- Compiling mm (1.4.2)

mm's Makefile does not use the LDFLAGS variable, but we need this flag
to be set in the link command, to specify an hardware library search path. Bug
reported as Feature
request #122, supposed to be fixed in version 1.4.2 but the fix doesn't work
as it assumes that libtool automatically includes the content of the $LDFLAGS
variable, which is not the case. So, we still need to patch the Makefile.in file
manually before compiling.

2.38- Compiling GNU awk (3.1.6)

GNU awk is needed on HP-UX because, on this system, the default
awk command
does not support more than 3,000 bytes per line. It may seem much but, during its compile phase,
bison needs more ! So, we need to replace awk with gawk which has no limitation on the line
length.

This is really needed on HP-UX only but, in order to remain consistent on the different
OS we are building on, we use gawk on every OS.

2.40- Compiling the mcrypt library (2.5.8)

Download the
Libmcrypt package, not Mcrypt, as we just need the library.

We also need to define two replacement malloc()/realloc() functions. If we
don't define them, the resulting libraries cannot be loaded on environments
(like AIX) which don't provide 'GNU compatible' allocation functions. Actually,
the configure tests for these functions to be 'GNU compatible'. When they are
determined not to be compatible, every call to malloc()/realloc() is replaced by
a call to rpl_malloc()/rpl_realloc() and the package is supposed to define these
two functions. Unfortunately, mcrypt does not provide these functions. So, on
systems lacking 'GNU compatible' allocation routines, when you try to load the
mcrypt shared library, the load fails with an 'unresolved symbol' error.

To
work around this issue, we define both 'GNU compatible' functions at the end of
one of the mcrypt source files, so that they are compiled and included in the
library.

The 'GNU compatibility' of allocation functions is defined by
the fact that they return a valid pointer, even when given null input arguments.
So, we wrap these features around the usual allocation routines :

2.41- Compiling PHP 4 (4.4.9)

During installation, PHP inserts a 'LoadModule' line in httpd.conf. As
we cannot have both PHP modules loaded at the same time, we decide that the
default PHP module will be PHP 5. So, we comment out the PHP 4 LoadModule
directive in the httpd.conf configuration file.

We modify the 'phpize'
script so that it uses the embedded autoconf and m4. We also force the shell to
be a korn-compatible shell (on some OS, /bin/sh is a Bourne-only shell, which
does not work with phpize).

2.42- Compiling PHP 5 (5.2.6)

As with PHP 4, we modify the 'phpize' script so that it uses the
embedded autoconf and m4. We also force the shell to be a korn-compatible shell
(on some OS, /bin/sh is a Bourne-only shell, which does not work with phpize).

First, we set the Oracle-related configure options :

PHP 5 does not support Oracle below version 9. So,
if the Oracle library we have in $ORACLE_HOME is version 8, we cannot compile
the Oracle-related extensions. Note that, on HP-UX 11.00, Oracle does not
provide a version greater than 8i.

we detect if the Oracle client library is an
instantclient and set the the configure options accordingly.

2.46- Compiling the APC PHP extension (3.0.19)

APC stands for 'Alternative PHP Cache'. It is a free, open, and robust
framework for caching and optimizing PHP intermediate code. Depending on the
application, it can provide really dramatic improvements in terms of response
time and CPU/memory usage.

APC provides a basic administration
interface. Here, we install it in the 'lib/php' subdirectory of both PHP trees.
Here are the steps to activate it on a target machine :

Considering that PHP and the APC extension are active,

Copy the apc.php file from the 'lib/php' subdirectory to a directory
accessible by a web browser.

Edit apc.php and modify the line starting with
'defaults('ADMIN_PASSWORD',' to set a password of your choice (mandatory to
access protected features).

Open apc.php through your browser.

We also pre-configure the php.ini file with a basic set of APC parameters.

If you are interested in PHP performance improvement, you may also have a look
at a number of other software like
XCache,
eAccelerator,
memcache,
PHK,
Automap... All these
PHP extensions can be compiled and installed in a similar way as what we show
here.

3- Cleanup

Now that the compilation phase is over, we can remove the tools we don't need
in the runtime environment :

/bin/rm -rf \
$BLIB/bison \
$BLIB/gawk

Note that we keep autoconf and m4, to be able to compile and install
additional PECL extensions.

4- Configuring

Here, we give only a minimal configuration, which allows to start Apache
and to run a simple test. When the package is installed on a target
machine, it must be configured in a much more serious way. This aspect is
beyond the scope of this document but you can find a lot of resources on the
Internet.

Warning : Running this package with the default configuration in a
production environment can lead to serious security problems !!
You have been warned

4.1-
System

Check that a www user exists in /etc/passwd. If not, create it
with passwd='*' and login shell = /bin/false.

Add a www group in /etc/group if it does not already exist.

4.2-
Apache

Edit $BAP/conf/httpd.conf :

Change the line starting with 'User' to 'User www'

Change the line containing 'Group #-1' to 'Group www'.

Add this line anywhere in the file :

AddType application/x-httpd-php .php

Add the following
line at the end of $BAP/bin/envvars :

unset LD_LIBRARY_PATH LIBPATH SHLIB_PATH

4.3- Oracle client

Edit the file '$BAP/bin/envvars' and add the environment
variables needed by the Oracle client :

ORACLE_HOME=<$BASE>/libs/oracle_home
export ORACLE_HOME

#-- TNS_ADMIN is optional, set only if you don't use the default
#-- location ($ORACLE_HOME/network/admin)

TNS_ADMIN=<directory>
export TNS_ADMIN

4.4-
PHP

For each PHP version, edit $BASE/php{4,5}/php.ini :

extension_dir = "<$BASE>/php<4|5>/lib/php/extensions"

5- Checking

5.1- Starting Apache/PHP

Now, start Apache with
:

<$BASE>/apache/bin/apachectl
start

This should not display any message.

5.2- Checking the PHP modules

Here, we just check that the PHP shared modules can be loaded without error.

It can be useful to run this test when you install your package to a new host, as it allows to check that every shared lib
dependencies are satisfied.

The syntax we use here must be compatible with
PHP versions 4 & 5. That's why we cannot use scandir().

By default, you will
be testing PHP 5, as it is the one we chose to activate in the Apache
configuration. In order to test PHP 4, comment out the PHP5
line in $BAP/conf/httpd.conf, uncomment the PHP4 line, restart Apache, and
reload the page from your browser.

Create a new file named $BAP/htdocs/test.php with the following content :