Compiling PHP 7.1 on Ubuntu 14.04 x86_64
After going thru all of troubles with compiling PHP, which was actually a fun process that took around 4 days in total,
i wanted to share the how-to guide, for myself to remember and for other to have a reference.
This guide is not for beginners, so i will keep the story short by providing minimum guidance.
There are many different reasons why u would want to compile PHP from sources i.e.
Curious how it works
Your distribution doesnt support needed version, neither there 3rd party distro available
Getting the last bits of performance by compiling it your way
Beta testing against latest PHP features
Building your own/company’s distribution, because everyone has their special requirements
Improving your software CI /CD
Learning new stuff altogether
The goal
Our goal is to have our own stable redistributable package (deb) of custom PHP build, the steps would be
Read docs
Meet dependencies
Fetch PHP
Compile PHP
Compile/phpize extension
Generate INI files
Build deb package
Benchmarking
Read docs
The is alot of googling involved in the process, and to make life easier i references all the info at the end of article .
Meet dependencies
Grab a Ubuntu 64 distro, and lets begin
update-alternatives --set editor /usr/bin/vim.basic
apt-get -y install \
libt1-dev \
libgmp-dev \
libcurl4-openssl-dev \
bison \
libxslt-dev \
libxml2-dev \
libxpm-dev \
libmcrypt-dev \
pkg-config \
libbz2-dev \
libpng-dev \
libfreetype6-dev \
libgmp3-dev \
libmysqlclient-dev \
libwebp-dev \
libjpeg-dev \
build-essential \
libtool \
software-properties-common \
libssl-dev \
autoconf \
git-core \
zlib1g-dev \
libc-bin \
cmake
apt-get install pkg-config
add-apt-repository ppa:ubuntu-toolchain-r/test -y
apt-get update
apt-get install gcc-4.8 g++-4.8 -y
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 60 --slave /usr/bin/g++ g++ /usr/bin/g++-4.8
This installs my set of required packages for building [1].
If u need more, for example you need webp support for GD extension for imagewebp function then according to GD documentation
you will need to install
As of PHP 7.0.0 --with-webp-dir=DIR has to be added
apt-get install libwebp-dev
--with-webp-dir=/usr
And so on for other extensions not included in this guide.
Second, we will use gcc 4.8 according to this gcc roadman , its fresh enough.
Fetch PHP
Lets compile latest released version atm. 7.0.12
To make it more challenging and be real-world case, lets compile it with some extensions
with one static extension redis
with one shared extension via phpize amqp
and our amqp extension has another dependency rabbitmq-c or librabbitmq-dev
cd ~
PHP_VERSION = "7.0.12"
PHP_FULLVERSION = "php- ${ PHP_VERSION } "
PHP_SOURCES_FILE = " ${ PHP_FULLVERSION } .tar.gz"
PHP_SOURCES_LOCATION = "http://de1.php.net/get/ ${ PHP_SOURCES_FILE } /from/this/mirror"
PHP_EXT_REDIS_VERSION = "3.0.0"
wget -O $PHP_SOURCES_FILE $PHP_SOURCES_LOCATION
tar zxf $PHP_SOURCES_FILE
cd $PHP_FULLVERSION
cd ext
wget https://pecl.php.net/get/redis-${ PHP_EXT_REDIS_VERSION } .tgz
tar zxf redis-${ PHP_EXT_REDIS_VERSION } .tgz
mv redis-${ PHP_EXT_REDIS_VERSION } redis
cd ..
Compile PHP
Here we need to make alot of decisions, here is the end-result
PHP_ZEND_API = "20151012"
PHP_INSTALL_ROOT = "/usr/local/php7"
PHP_PREFIX_DIR = " ${ PHP_INSTALL_ROOT } / ${ PHP_VERSION } "
PHP_DATAROOT = " ${ PHP_PREFIX_DIR } /share"
PHP_LIB_DIR = " ${ PHP_PREFIX_DIR } /lib"
PHP_BIN_DIR = " ${ PHP_PREFIX_DIR } /bin"
PHP_EXTENSIONS_DIR = " ${ PHP_LIB_DIR } / ${ PHP_ZEND_API } "
PHP_INITD_DIR = " ${ PHP_PREFIX_DIR } /init.d"
PHP_INI_CLI_DIR = " ${ PHP_PREFIX_DIR } /etc"
PHP_INI_CLI_FILE = " ${ PHP_INI_CLI_DIR } /php.ini"
PHP_INI_CLI_SCAN_DIR = " ${ PHP_INI_CLI_DIR } /conf.d"
PHP_INI_CLI_EXTENSIONS = " ${ PHP_INI_CLI_SCAN_DIR } /extension.ini"
PHP_INI_CLI_ZENDEXTENSIONS = " ${ PHP_INI_CLI_SCAN_DIR } /zend_extension.ini"
PHP_INI_FPM_DIR = " ${ PHP_INI_CLI_DIR } "
PHP_INI_FPM_FILE = " ${ PHP_INI_FPM_DIR } /php-fpm.conf"
PHP_INI_FPM_SCAN_DIR = " ${ PHP_INI_FPM_DIR } /php-fpm.d"
PHP_AMQP_RABBITMQ_LIBDIR = " ${ PHP_LIB_DIR } /rabbitmq-shared"
export LANG = "en"
export LC_ALL = "en_US.UTF-8"
export CHOST = "x86_64-linux-gnu"
SAFE_CFLAGS_1 = "-m64 -march=corei7 -mfpmath=sse -minline-all-stringops -pipe -fstack-protector -Wformat -Werror=format-security"
SAFE_CFLAGS_2 = "-fPIC -fPIE -fno-strict-aliasing -fsigned-char -std=gnu99 -mpc64 --param=ssp-buffer-size=4"
CFLAGS = " \" -O3 ${ SAFE_CFLAGS_1 } ${ SAFE_CFLAGS_2 } " \"
CXXFLAGS = $CFLAGS
CMAKE_CFLAGS = "-m64 -march=corei7 -O2 -fPIC"
We are building
for intel 64bit cpu with -march=corei7
[19] gcc flag
for (debian amd64) or (gnu x86_64) which are the same (see cat /usr/share/dpkg/cputable
)
everything is installed under /usr/local/php7
we generate code for a 64-bit environment only [19]
we enable some optimizations like -mfpmath=sse -minline-all-stringops
(-mfpmath=sse is default for x86-64 compiler [19])
-fstack-protector --param=ssp-buffer-size=4
is hardening option [30] for GCC<4.9
, that emit extra code to check for buffer overflows
-fno-strict-aliasing
is performance option passed through to the link stage (34) (35), disables -O2 and -O3 -fstrict-aliasing
(36)
-std=gnu99
enables C99 standard (23) (25), i.e. C code already contains long long int
data types; also C99 brings inline functions and optimizations
-fPIC -fPIE
is similar to -fpic -fpie
(37) (38) Generate position-independent code (PIC) & executable
-fsigned-char
Let the type char be signed, like signed char. (22)
-mpc64
rounds the significands of results of floating-point operations to 53 bits (double precision) vs 64bits (default)
We also define CMAKE_CFLAGS
flags for later on.
Now as we have PHP sources, lets deal our extension dependency librabbitmq
.
First we need to check if we dont have it installed pkg-config librabbitmq --libs
.
Because (26) GCC first searches for libraries in /usr/local/lib
, then in /usr/lib
.
Following that, it searches for libraries in the directories specified by the -L parameter, in the order specified on the command line.
If we do have librabbitmq
library installed, GCC will link agains it ignoring our library.
#REMOVE ANY PREINSTALLED PACKAGE (check `pkg-config librabbitmq --libs`)
RABBITMQ_C_VERSION = "0.8.0"
RABBITMQ_C_SOURCES_LOCATION = "https://github.com/alanxz/rabbitmq-c/archive/v ${ RABBITMQ_C_VERSION } .tar.gz"
rm -rf rabbitmq-c
wget -O rabbitmq-c.tar.gz ${ RABBITMQ_C_SOURCES_LOCATION }
tar zxf rabbitmq-c.tar.gz
rm -f rabbitmq-c.tar.gz
mv rabbitmq-c-${ RABBITMQ_C_VERSION } / rabbitmq-c
cd rabbitmq-c
rm -rf build && mkdir build && cd build
rm -f CMakeCache.txt
cmake -DCMAKE_C_FLAGS = " $CMAKE_CFLAGS " \
-DCMAKE_INSTALL_PREFIX = ${ PHP_AMQP_RABBITMQ_LIBDIR } \
-DBUILD_EXAMPLES = OFF \
-DBUILD_STATIC_LIBS = OFF \
-DBUILD_TESTS = OFF \
-DBUILD_TOOLS = OFF \
-DENABLE_SSL_SUPPORT = OFF \
-DBUILD_SHARED_LIBS = ON \
-DBUILD_API_DOCS = OFF ..
mkdir -p ${ PHP_LIB_DIR }
cmake --build . --target install --clean-first
cd ../..
This will install our librabbitmq under our PHP root for linking later on.
rm -f configure && ./buildconf --force
CONFIGURE_STRING = " " \
"--prefix= ${ PHP_PREFIX_DIR } " \
"--bindir= ${ PHP_BIN_DIR } " \
"--sbindir= ${ PHP_PREFIX_DIR } /sbin " \
"--sysconfdir= ${ PHP_PREFIX_DIR } /etc " \
"--sharedstatedir= ${ PHP_PREFIX_DIR } /com " \
"--localstatedir= ${ PHP_PREFIX_DIR } /var " \
"--libdir= ${ PHP_LIB_DIR } " \
"--includedir= ${ PHP_PREFIX_DIR } /include " \
"--datarootdir= ${ PHP_DATAROOT } " \
"--infodir= ${ PHP_DATAROOT } /info " \
"--localedir= ${ PHP_DATAROOT } /locale " \
"--mandir= ${ PHP_DATAROOT } /man " \
"--docdir= ${ PHP_DATAROOT } /doc " \
"--with-config-file-path= ${ PHP_INI_CLI_DIR } " \
"--with-config-file-scan-dir= ${ PHP_INI_CLI_SCAN_DIR } " \
"--disable-all " \
"--without-pear " \
"--enable-cli " \
"--disable-cgi " \
"--disable-phpdbg " \
"--disable-debug " \
"--disable-rpath " \
"--with-layout=GNU " \
"--enable-fpm " \
"--enable-pdo " \
"--with-mysql-sock=/var/run/mysqld/mysqld.sock " \
"--with-mysqli=mysqlnd " \
"--with-pdo-mysql=mysqlnd " \
"--enable-mysqlnd " \
"--with-pic " \
"--with-pcre-regex " \
"--with-jpeg-dir=/usr " \
"--with-png-dir=/usr " \
"--with-xpm-dir=/usr " \
"--with-freetype-dir=/usr " \
"--enable-gd-native-ttf " \
"--enable-gd-jis-conv " \
"--disable-static " \
"--with-readline " \
"--with-fpm-user=www-data " \
"--with-fpm-group=www-data " \
"--enable-dom=shared " \
"--enable-mbstring=shared " \
"--enable-redis=shared " \
"--enable-opcache=shared " \
"--with-mhash=/usr " \
"--enable-phar=shared " \
"--enable-fileinfo=shared " \
"--with-gd=shared " \
"--enable-session " \
"--enable-hash " \
"--enable-json " \
"--enable-filter " \
"--enable-libxml " \
"--enable-ctype " \
"--enable-tokenizer " \
"--enable-pcntl " \
"--enable-posix " \
"--enable-xml " \
"--enable-xmlwriter " \
"--enable-bcmath " \
"--enable-simplexml " \
"--enable-exif " \
"--enable-shared " \
"--with-iconv " \
"--with-zlib " \
"--with-zlib-dir=/usr " \
"--with-libedit=/usr " \
"--with-curl " \
"--with-openssl=yes " \
"--with-pdo_mysql " \
"--build ${ CHOST } " \
"--host ${ CHOST } " \
"CC=gcc CFLAGS= $CFLAGS CHOST= $CHOST CXXFLAGS= $CFLAGS "
CFG_CMD = "./configure ${ CONFIGURE_STRING } "
eval $CFG_CMD ;
make -j ` cat /proc/cpuinfo | grep processor | wc -l `
make install
\c p php.ini-production ${ PHP_INI_CLI_FILE }
Few notes on configure flags
readline is required for correct cli work
xml is required for utf8_decode & utf8_encode
most of the libraries are compiled-in, while only few are built as shared (i.e. --with-gd=shared
)
What extensions you wish to build as shared is up to you, here is a mine sizes reference table
##AVG Shared Extension sizes##
# 3.8M sqlite3.so
# 3.5M fileinfo.so
# 2.8M mbstring.so
# 1.7M redis.so
# 1.3M gd.so
# 1.1M phar.so
# 989K dom.so
# 923K opcache.so
# 835K zip.so
# 525K openssl.so
# 436K amqp.so
# 368K sockets.so
# 358K bcmath.so
# 300K curl.so
# 246K gmp.so
# 224K simplexml.so
# 193K exif.so
# 186K zlib.so
# 173K iconv.so
# 139K pdo_sqlite.so
# 122K xmlwriter.so
# 110K xmlreader.so
# 99K bz2.so
# 98K posix.so
# 95K pcntl.so
# 72K tokenizer.so
# 50K ctype.so
In the end we get our php installed under PHP_INSTALL_ROOT
.
$ /usr/local/php7/7.0.12/bin/php -n --ini
Configuration File ( php.ini) Path: /usr/local/php7/7.0.12/etc
Loaded Configuration File: ( none)
Scan for additional .ini files in : ( none)
Additional .ini files parsed: ( none)
$ /usr/local/php7/7.0.12/sbin/php-fpm -v
PHP 7.0.12 ( fpm-fcgi) ( built: Oct 13 2016 11:38:14)
Copyright ( c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright ( c) 1998-2016 Zend Technologies
with Zend OPcache v7.0.12, Copyright ( c) 1999-2016, by Zend Technologies
$ /usr/local/php7/7.0.12/bin/php -n -m
[ PHP Modules]
bcmath
Core
ctype
curl
date
exif
filter
hash
iconv
json
libxml
mysqli
mysqlnd
openssl
pcntl
pcre
PDO
pdo_mysql
posix
readline
Reflection
session
SimpleXML
SPL
standard
tokenizer
xml
xmlwriter
zlib
[ Zend Modules]
Compile/phpize extension
Now lets build amqp
extension, we already built librabbitmq-dev - An AMQP client library written in C - Dev Files
AMQP_VERSION = "1.7.1"
wget https://pecl.php.net/get/amqp-${ AMQP_VERSION } .tgz
rm -rf amqp
tar zxf amqp-${ AMQP_VERSION } .tgz
mv amqp-${ AMQP_VERSION } amqp
cd amqp
export PKG_CONFIG_PATH = " ${ PHP_AMQP_RABBITMQ_LIBDIR } /lib/ ${ CHOST } /pkgconfig/"
export LD_LIBRARY_PATH = " ${ PHP_AMQP_RABBITMQ_LIBDIR } /lib/"
/usr/bin/pkg-config librabbitmq --libs
make clean && rm -f configure
${ PHP_BIN_DIR } /phpize .
CFG_CMD = "./configure \
--with-php-config= ${ PHP_BIN_DIR } /php-config \
--with-amqp \
--with-pic \
--with-librabbitmq-dir=yes \
CFLAGS= ${ CFLAGS } \
CHOST= \" ${ CHOST } \" "
eval $CFG_CMD
make -j ` cat /proc/cpuinfo | grep processor | wc -l `
make install
${ PHP_BIN_DIR } /php -n -dextension = ${ PHP_EXTENSIONS_DIR } /amqp.so --ri amqp
cd ..
This should build amqp.so
that is linked agains our librabbitmq
ldd ${ PHP_EXTENSIONS_DIR } /amqp.so |grep librabbitmq
#2: librabbitmq.so.4 => /usr/local/php7/7.0.12/lib/rabbitmq-shared/lib/x86_64-linux-gnu/librabbitmq.so.4 (0x00007f910d263000)
Generate INI files
We are still missing the default .ini files, lets fix that, and make sure all extensions have linked correctly
ldd ${ PHP_EXTENSIONS_DIR } /* | grep 'not found'
mkdir -p " ${ PHP_INI_CLI_SCAN_DIR } "
rm -f ${ PHP_EXTENSIONS_DIR } /* a
find \
${ PHP_EXTENSIONS_DIR } /* so \
'!' -name 'opcache.so' \
| sort \
| sed "s| $PHP_EXTENSIONS_DIR |extension= $PHP_EXTENSIONS_DIR |" \
> " ${ PHP_INI_CLI_EXTENSIONS } "
find \
${ PHP_EXTENSIONS_DIR } /opcache.so \
| sed "s| $PHP_EXTENSIONS_DIR |zend_extension= $PHP_EXTENSIONS_DIR |" \
> " ${ PHP_INI_CLI_ZENDEXTENSIONS } "
\c p ${ PHP_INI_FPM_FILE } .default ${ PHP_INI_FPM_FILE }
mkdir -p " ${ PHP_INITD_DIR } "
cp sapi/fpm/init.d.php-fpm ${ PHP_INITD_DIR } /php-fpm
chmod +x ${ PHP_INITD_DIR } /php-fpm
Build deb package
To share our package, lets create a simplest deb package (27) (28)
#curl uses system libcurl and openssl which are provided by `libcurl4-openssl-dev` (libcurl3)
#zlib is provided by `zlib1g-dev`
#iconv is provided by `libc-bin` (`/usr/bin/iconv`)
#libxml is provided by `libxml2-dev`
PHP_PACKAGE_DEB_NAME = "mypackage-php-name"
PHP_PACKAGE_MAINTAINER = "Sergei Shilko <contact@sshilko.com>"
cd ${ PHP_PREFIX_DIR } /..
mkdir -p ${ PHP_FULLVERSION } /DEBIAN
mkdir -p ${ PHP_FULLVERSION } /${ PHP_PREFIX_DIR } /
cp -rf ${ PHP_PREFIX_DIR } ${ PHP_FULLVERSION } /${ PHP_PREFIX_DIR } /..
echo "Package: ${ PHP_PACKAGE_DEB_NAME }
Version: ${ PHP_VERSION }
Section: base
Priority: optional
Architecture: amd64
Depends: libcurl3, zlib1g (>= 1.0.9), libc-bin, libxml2 (>= 2.6.0)
Maintainer: ${ PHP_PACKAGE_MAINTAINER }
Description: PHP" > ${ PHP_FULLVERSION } /DEBIAN/control
dpkg-deb --build ${ PHP_FULLVERSION }
mv ${ PHP_FULLVERSION } .deb ${ PHP_PACKAGE_DEB_NAME } -` date +"%F" ` .deb
rm -rf ${ PHP_FULLVERSION }
Benchmarking
Compare 7.0.10-2+deb.sury.org~trusty+1 vs our compiled 7.0.12 under ±5K RPM with NewRelic
Linux 3.13.0-74-generic x86_64
New Relic agent 2.2.0.125
Intel(R) Xeon(R) CPU E5-2680 v2 @ 2.80GHz 4 cores 7.3 GB RAM
#/usr/bin/php-config7.0
#7.0.10-2+deb.sury.org~trusty+1
--configure-options [ --includedir = /usr/include --mandir = /usr/share/man --infodir = /usr/share/info --libdir = /usr/lib/x86_64-linux-gnu --libexecdir = /usr/lib/x86_64-linux-gnu --disable-maintainer-mode --disable-dependency-tracking --prefix = /usr --enable-cli --disable-cgi --disable-phpdbg --with-config-file-path = /etc/php/7.0/cli --with-config-file-scan-dir = /etc/php/7.0/cli/conf.d --build = x86_64-linux-gnu --host = x86_64-linux-gnu --config-cache --cache-file = /build/php7.0-DVHBcL/php7.0-7.0.10/config.cache --libdir = ${ prefix } /lib/php --libexecdir = ${ prefix } /lib/php --datadir = ${ prefix } /share/php/7.0 --program-suffix = 7.0 --sysconfdir = /etc --localstatedir = /var --mandir = /usr/share/man --disable-all --disable-debug --disable-rpath --disable-static --with-pic --with-layout = GNU --without-pear --enable-filter --with-openssl = yes --with-pcre-regex = /usr --enable-hash --with-mhash = /usr --enable-libxml --enable-session --with-system-tzdata --with-zlib = /usr --with-zlib-dir = /usr --enable-dtrace --enable-pcntl --with-libedit = shared,/usr build_alias = x86_64-linux-gnu host_alias = x86_64-linux-gnu CFLAGS = -g -O2 -fPIE -fstack-protector --param = ssp-buffer-size= 4 -Wformat -Werror = format-security -O2 -Wall -pedantic -fsigned-char -fno-strict-aliasing -g ]
Compiling PHP 7.1.11 on Debian 9 x86_64
To be able to compile for debian 9 stretch minor changes need to be made
Update locale on clean image
apt-get install --no-install-recommends --no-upgrade -y locales libc-l10n
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen
Dependency changes:
libxslt-dev replaced with libxslt1-dev
libmysqlclient-dev replaced with default-libmysqlclient-dev
libpng12-dev replaced with libpng-dev
libedit-dev depencency added
libt1-dev removed
Build tools required, need to be installed manually
GCC 6 is the default one in debian 9, no need to install it
Building PHP
Building PHP extensions
Getting into the Zend Execution engine
PHP’s OPCache extension review
Compiling And Installing PHP7 On Ubuntu
How to install latest gcc on Ubuntu LTS (12.04, 14.04, 16.04)
How to compile php7 on ubuntu 14.04
PHP.NET - Installation on Unix systems
PHP.NET - List of core configure options
PHP.NET - Life cycle of an extension
PHP Extensions - What and Why by Derick Rethans
Symfony Polyfill
GCC - clang: a C language family frontend for LLVM
GCC - Using Hardening Options
GCC - Stack Smashing Protector, and _FORTIFY_SOURCE
GCC - Clang vs GCC (GNU Compiler Collection)
GCC - Safe CFLAGS - Find CPU-specific options
GCC - Options That Control Optimization
GCC - Intel 386 and AMD x86-64 Options
GCC - Options for Code Generation Conventions
GCC - Environment Variables Affecting GCC
GCC - Options Controlling C Dialect
GCC - Status of C99 features in GCC (-std=gnu89 is default until gcc5 with -std=gnu11)
GCC - best “general purpose” set of flags - Ben Eastaugh and Chris Sternal-Johnson
GCC - 5 Release Series Changes, New Features, and Fixes
GCC - Shared libraries with GCC on Linux
DEB - dpkg-architecture - set and determine the architecture for package building
DEB - Debian Policy Manual - Package maintainer scripts and installation procedure
DEB - Maintainer scripts
DEB - Hardening
DEB - How to make a “Basic” .deb
Pattern library
XKCD
COMPILER OPTIMIZATION ORCHEST TION FOR PEAK PERFORMANCE
Про C++ алиасинг, ловкие оптимизации и подлые баги
GCC - Options That Control Optimization
Position Independent Executable
GCC - Options for Code Generation Conventions