Skip to content

Latest commit

 

History

History
404 lines (309 loc) · 12.5 KB

autotools.md

File metadata and controls

404 lines (309 loc) · 12.5 KB

Autotools-based PHP build system

This is a brief introduction to Autotools and how it is used in the PHP build system.

Index

*nix build system (Linux, macOS, FreeBSD, OpenBSD, Solaris, Haiku, etc.) in PHP uses Autoconf to build a configure shell script that further creates a Makefile to build sources to executable binaries.

# Create Autotools configure script
./buildconf

# Configure PHP build and create Makefile
./configure

# Build PHP
make

The buildconf is a simple shell script wrapper around autoconf and autoheader command-line tools. It checks required Autoconf version, creates configure command-line script and main/php_config.h.in header template.

When running the ./configure, many checks are done based on the host and targeted system. Things like C headers availability, C symbols, required library dependencies, etc.

The configure script creates Makefile where the make command then builds binary files from C source files. You can optionally pass the -j option with the number of simultaneous jobs so the build runs faster in parallel.

make -j 10

When compiling is done, the tests can be run with:

make TEST_PHP_ARGS=-j10 test

Note

Number of simultaneous jobs is often the number of available processor threads of the build machine and can be also automatically calculated using the $(nproc) on Linux, or $(sysctl -n hw.ncpu) on macOS and BSD-based systems.

make -j $(nproc)

PHP *nix build system is pretty much standard GNU Autotools build system with few customizations. It doesn't use Automake and it bundles some 3rd party files for easier installation across various systems out there without requiring installation dependencies. Autotools is a veteran build system present since early C/C++ days. It is used for most Linux ecosystem out there and it might cause issues for C developers today.

1. Directory structure

PHP build system is a collection of various files across the php-src repository:

<php-src>/
 └─ build/
    ├─ ax_*.m4             # https://github.com/autoconf-archive/autoconf-archive
    ├─ config-stubs        # Adds extension and SAPI config*.m4 stubs to configure
    ├─ config.guess        # https://git.savannah.gnu.org/cgit/config.git
    ├─ config.sub          # https://git.savannah.gnu.org/cgit/config.git
    ├─ genif.sh            # Generator for the internal_functions* files
    ├─ libtool.m4          # Forked https://git.savannah.gnu.org/cgit/libtool.git
    ├─ ltmain.sh           # Forked https://git.savannah.gnu.org/cgit/libtool.git
    ├─ Makefile.global     # Root Makefile template when configure is run
    ├─ order_by_dep.awk    # Used by genif.sh
    ├─ php.m4              # PHP Autoconf macros
    ├─ pkg.m4              # https://gitlab.freedesktop.org/pkg-config/pkg-config
    ├─ print_include.awk   # Used by genif.sh
    └─ shtool              # https://www.gnu.org/software/shtool/
 └─ ext/
    └─ bcmath/
       └─ config.m4        # Extension's Autoconf file
    └─ date/
       └─ config0.m4       # Suffix 0 priority adds file before other config.m4 extension files
    └─ mysqlnd/
       └─ config9.m4       # Suffix 9 priority adds file after other config.m4 files
    └─ opcache/
       └─ jit/
          └─ Makefile.frag # Makefile fragment for OPcache Jit
       └─ config.m4        # Autoconf file for OPcache extension
 └─ main/
    ├─ build-defs.h        # Generated by configure
    ├─ build-defs.h.in     # Template for build definitions
    ├─ php_config.h        # Generated by configure
    ├─ php_config.h.in     # Generated header template by buildconf
    └─ php_version.h       # Generated by release managers using `configure`
 ├─ pear/
 └─ sapi/
    └─ cli/
       └─ config.m4        # Autoconf M4 file for SAPI
 ├─ scripts/
 └─ TSRM/
    ├─ threads.m4          # Autoconf macros for pthreads
    └─ tsrm.m4             # Autoconf macros for TSRM
 └─ Zend/
    ├─ Makefile.frag       # Makefile fragment for Zend engine
    └─ Zend.m4             # Autoconf macros for Zend engine
 ├─ buildconf              # Wrapper for autoconf and autoheader tools
 └─ configure.ac           # Autoconf main input file for creating configure script

2. Build system diagram

PHP *nix build system using Autotools

3. Build requirements

Before you can build PHP on Linux and other Unix-like systems, you must first install certain third-party requirements. It's important to note that the names of these requirements may vary depending on your specific system. For the sake of simplicity, we will use generic names here. When building PHP from source, one crucial requirement is a library containing development files. Such libraries are typically packaged under names like libfoo-dev, libfoo-devel, or similar conventions. For instance, to install the libxml2 library, you would look for the libxml2-dev (or libxml2-devel) package.

Required:

  • autoconf
  • make
  • gcc
  • g++
  • pkg-config
  • libxml2
  • libsqlite3

Additionally required when building from Git repository source code:

  • bison
  • re2c

4. The configure command-line options

Configuration can be passed on the command line at the configuration phase:

./configure VAR=VALUE --enable-FEATURE --with-PACKAGE

With Autoconf, there are two main types of command-line options for the configure script (--enable-FEATURE and --with-PACKAGE):

  • --enable-FEATURE[=ARG] and its belonging opposite --disable-FEATURE

    --disable-FEATURE is the same as --enable-FEATURE=no

    These normally don't require 3rd party library or package installed on the system. For some extensions, PHP bundles 3rd party dependencies in the extension itself. For example, bcmath, gd, etc.

  • --with-PACKAGE[=ARG] and its belonging opposite --without-PACKAGE

    --without-PACKAGE is the same as --with-PACKAGE=no

    These require 3rd party package installed on the system. PHP has even some libraries bundled in PHP source code. For example, the PCRE library and similar.

Other custom options that don't follow this pattern are used for adjusting specific features during the build process.

See ./configure --help for all available configuration options and variables.

Some common arguments can be passed to command-line options:

  • To build extension as shared: --enable-EXT=shared or --with-EXT=shared.
  • Some options accept multiple arguments separated by comma (,). For example, passing the library location and similar: --with-EXT=shared,/usr

5. Determining platform

With Autotools there are several shell variables that help determine the platform characteristics such as CPU, operating system and vendor name. When using macros AC_CANONICAL_BUILD, AC_CANONICAL_HOST, and AC_CANONICAL_TARGET in the M4 files, config.sub and config.sub scripts help determine the values of variables build_alias, host_alias, and target_alias.

Users can also manually override these variables for their specific case using the --build, --host, and --target configure options.

In M4 files platform can be then determined using above shell variables in variety of ways:

AS_CASE([$host_alias], [*freebsd*|*openbsd*],
  [AC_MSG_NOTICE([Action that is run only on FreeBSD and OpenBSD systems.])])

6. Common checks

6.1. Testing if program works

There are 3 main Autoconf macros that check if certain test code is successful.

Let's check a simple C program:

#include <stdio.h>

int main(void) {
    printf("Hello World");

    return 0;
}

6.1.1. AC_COMPILE_IFELSE

AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>],
  [printf("Hello World")])],
  [php_cv_func_printf_works=yes],
  [php_cv_func_printf_works=no])

The AC_LANG_PROGRAM macro will expand this into:

#include <stdio.h>

int main(void) {
  printf("Hello World")
  ;
  return 0;
}

The AC_COMPILE_IFELSE will run the compilation step, for example:

gcc -o out -c hello_world.c

6.1.2. AC_LINK_IFELSE

AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>],
  [printf("Hello World")])],
  [php_cv_func_printf_works=yes],
  [php_cv_func_printf_works=no])

This will run compilation and linking:

gcc -o out hello_world.c

6.1.3. AC_RUN_IFELSE

This will compile, link and also run the program to check if the return code is 0.

Issue with AC_RUN_IFELSE is when doing so-called cross-compilation. That is building C software on one platform with purpose of running it on some other platform. In this case the program cannot be run and we cannot be sure of if it is running successfully or not.

AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>],
  [printf("Hello World")])],
  [php_cv_func_printf_works=yes],
  [php_cv_func_printf_works=no],
  [php_cv_func_printf_works=cross-compiling])

This does something like this:

gcc -o out hello_world.c
./out

6.2. Functions

Testing if function exists within the given header.

A common way to check for function is using the AC_CHECK_FUNC or AC_CHECK_FUNCS macros. These check if the linker sees the function in the usual libraries (libc and the ones appended to the LIBS variable). However, many times, functions also have their belonging headers, so it makes sense to check for both using this:

AC_CHECK_HEADER([priv.h], [AC_CHECK_FUNCS([setpflags])])

This first checks if header priv.h exists, and if so, it then checks if linker sees the function. They can be used like this:

#ifdef HAVE_SETPFLAGS
# include <priv.h>
#endif

/* ... */

/* Call setpflags. */
#ifdef HAVE_SETPFLAGS
    setpflags(...)
#else
    /* ... */
#endif

7. GNU Autoconf Archive

To reuse the code there is a community collection of Autoconf macros available at autoconf-archive.

PHP is not using Automake so it includes them like this in configure.ac:

m4_include([build/ax_..._.m4])
m4_include([build/...macro.m4])
# ...

They can be than called and expanded in the m4 code:

AX_MACRO_CALL(...)

When using Automake, these can be automatically included like this:

AC_CONFIG_MACRO_DIR([path/to/m4/dir])

However, the aclocal from Automake is needed for this to work.

8. Parser and lexer files

Parser and lexer files are generated upon the build step (make) with bison and re2c tools based on the targets in Makefile.frag files.

There is also a helper shell script available that generates these files when developing or releasing PHP source code, otherwise they.

./scripts/dev/genfiles

Autotools-based PHP build system files related to bison and re2c:

<php-src>/
 └─ build/
    └─ php.m4                       # Autoconf macros with re2c and bison macros
 └─ ext/
    └─ json/
       └─ Makefile.frag             # Makefile fragment
    └─ pdo/
       └─ Makefile.frag             # Makefile fragment
    └─ phar/
       └─ Makefile.frag             # Makefile fragment
    └─ standard/
       └─ Makefile.frag             # Makefile fragment
 └─ sapi/
    └─ phpdbg/
       └─ Makefile.frag             # Makefile fragment
 └─ scripts/
    └─ dev/
       └─ genfiles                  # Parser and lexer files generator helper
 └─ Zend/
    └─ Makefile.frag                # Part of Makefile related to Zend files
 └─ configure.ac                    # Minimum re2c and bison versions settings

9. See more

Useful resources to learn more about Autoconf and Autotools in general: