This document describes how CMake-based PHP build system in this repository works and how it can be used.
- 1. Directory structure
- 2. Build system diagram
- 3. Build requirements
- 4. CMake generators for building PHP
- 5. Build types
- 6. CMake minimum version for PHP
- 7. Interface libraries
- 8. PHP CMake modules
- 9. Custom CMake properties
- 10. PHP extensions
- 11. PHP SAPI (Server API) modules
- 12. Generated files
- 13. Performance
- 14. Testing
- 15. Windows notes
CMake-based PHP build system is a collection of various files across the php-src repository:
π <php-src>
ββπ cmake # CMake-based PHP build system files
ββπ modules # Project-specific CMake modules
ββπ PHP # PHP utility CMake modules
ββπ Find*.cmake # Find modules that support the find_package()
ββπ platforms # Platform-specific configuration
ββπ presets # Presets included in CMakePresets.json
ββπ scripts # Various CMake command-line scripts
ββπ toolchains # CMake toolchain files
ββπ *.cmake # Various CMake configurations and files
ββπ ext
ββπ date
ββπ lib
ββπ CMakeLists.txt # Timelib's CMake file
ββπ mbstring
ββπ libmbfl
ββπ CMakeLists.txt # libmbfl's CMake file
ββπ standard
ββπ cmake # Extension's CMake-related files
ββπ config.h.in # Extension's configuration header template
ββπ CMakeLists.txt # Extension's CMake file
ββπ zlib
ββπ cmake
ββπ CMakeLists.txt
ββπ php_iconv.def # Module-definition for building DLL on Windows
ββπ main
ββπ cmake # CMake-related files for main binding
ββπ php_config.h.in # PHP main configuration header template
ββπ CMakeLists.txt # CMake file for main binding
ββπ internal_functions.c.in # Common template for internal functions files
ββπ pear
ββπ cmake # CMake-related files for PEAR
ββπ CMakeLists.txt # CMake file for PEAR
ββπ sapi
ββπ fpm
ββπ cmake # SAPI's CMake-related files
ββπ config.h.in # SAPI's configuration header template
ββπ CMakeLists.txt # CMake file for PHP SAPI module
ββπ scripts
ββπ CMakeLists.txt # CMake file for creating scripts
ββπ win32 # Windows build files
ββπ build # Windows build files
ββπ wsyslog.mc # Message template file for win32/wsyslog.h
ββπ CMakeLists.txt # CMake file for Windows build
ββπ Zend
ββπ cmake # CMake-related files for Zend Engine
ββπ zend_config.h.in # Zend Engine configuration header template
ββπ CMakeLists.txt # CMake file for Zend Engine
ββπ CMakeLists.txt # Root CMake file
ββπ CMakePresets.json # Main CMake presets file
ββπ CMakeUserPresets.json # Git ignored local CMake presets overrides
The following diagram briefly displays, how PHP libraries (in terms of a build system) are linked together:
Before you can build PHP using CMake, 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 on *nix
systems. For instance, to install the libxml2
library, you would look for the
libxml2-dev
(or libxml2-devel
) package.
Required:
- cmake
- gcc
- g++
- libsqlite3
Optional (if not found on the system, build system tries to download it):
- libxml2
Additionally required when building from Git repository source code:
- bison
Optional when building from Git repository source code:
- re2c
When PHP is built, the development libraries are no longer required to be
installed and only libraries without development files are needed to run newly
built PHP. In example of ext/libxml
extension, the libxml2
package is needed
without the libxml2-dev
and so on.
When using CMake to build PHP, you have the flexibility to choose from various
build systems through the concept of generators. CMake generators determine
the type of project files or build scripts that CMake generates from the
CMakeLists.txt
files.
The Unix Makefiles generator is the most commonly used for building projects on
Unix-like systems. It generates traditional Makefile
that can be processed by
the make
command. To use the Unix Makefiles generator, you simply specify it
as an argument when running CMake in your build directory.
To generate the Makefile
for building PHP, create a new directory (often
called build
or cmake-build
) and navigate to it using the terminal. Then,
execute the following CMake command:
cmake -G "Unix Makefiles" /path/to/php-src
Replace /path/to/php-src
with the actual path to the PHP source code on your
system (in case build directory is the same as the source directory, use .
).
CMake will process the CMakeLists.txt
file in the source directory and
generate the Makefile
in the current build directory.
After the Makefiles are generated, you can build PHP binaries and libraries by running:
cmake --build <build-directory> -j
If you want to speed up the build process, you can use the -j
option to enable
parallel builds, taking advantage of multiple CPU cores.
Note
On some systems, the -j
option requires argument. 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.
cmake --build <build-directory> -j $(nproc)
The cmake --build
is equivalent to running the make
command:
make -j $(nproc) # Number of CPUs you want to utilize.
Ninja is another build system supported by CMake and is known for its fast build times due to its minimalistic design. To use the Ninja generator, you need to have Ninja installed on your system. Most package managers on Unix systems offer Ninja as a package, so you can install it easily.
To generate Ninja build files for building PHP, navigate to your build directory in the terminal and run the following CMake command:
cmake -G "Ninja" /path/to/php-src
Again, replace /path/to/php/src
with the actual path to the PHP source code.
CMake will generate the Ninja build files in the current directory.
To build PHP with Ninja, execute the following command:
cmake --build <build-directory>
Which is equivalent to running ninja
command. Ninja will then handle the build
process based on the CMake configuration. Ninja by default enables parallel
build.
CMake build types dictate compiler and linker flags, as well as the behavior governing the compilation of source code based on the targeted deployment type. Several common build types are pre-configured and readily available:
- Debug
- Release
- MinSizeRel
- RelWithDebInfo
- DebugAssertions (custom PHP build type)
The selection of a build type varies depending on the generator in use.
For single configuration generators, such as Unix Makefiles
and Ninja
, the
build type is designated during the configuration phase using the cache variable
CMAKE_BUILD_TYPE
:
cmake -DCMAKE_BUILD_TYPE=Debug -S ../php-src -B build-directory
Multi configuration generators, like Ninja Multi-Config
and Visual Studio,
employ the --config
build option during the build phase:
cmake -G "Ninja Multi-Config" -S ../php-src -B build-directory
cmake --build build-directory --config Debug -j
Alternatively, multi configuration generators can specify build type in the
CMake presets JSON file using the configuration
field:
"buildPresets": [
{
"...": "...",
"configuration": "Debug"
}
],
The minimum required version of CMake is defined in the top project file
CMakeLists.txt
using the cmake_minimum_required()
. Picking the minimum
required CMake version is a compromise between CMake functionalities and CMake
version available on the operating system.
- 3.17
CMAKE_CURRENT_FUNCTION_LIST_DIR
variable
- 3.19
check_compiler_flag()
,check_source_compiles()
,check_source_runs()
to generalize thecheck_<LANG>_...()
CMakePresets.json
for sharing build configurations
- 3.20
CMAKE_C_BYTE_ORDER
, otherwise manual check should be done"version": 2
inCMakePresets.json
Intl::Intl
IMPORTED target with CMake's FindIntl module
- 3.21
"version": 3
inCMakePresets.json
(for theinstallDir
field)
- 3.22
- Full condition syntax in
cmake_dependent_option()
- Full condition syntax in
- 3.23
target_sources(FILE_SET)
, otherwiseinstall(FILES)
should be used when installing files to their destinations"version": 4
inCMakePresets.json
(for theinclude
field)
- 3.24
CMAKE_COLOR_DIAGNOSTICS
CMAKE_COMPILE_WARNING_AS_ERROR
, otherwise INTERFACE library should be usedif(PATH_EQUAL)
- 3.25
block()
command- New
try_run
signature cmake_language()
keywordGET_MESSAGE_LOG_LEVEL
return()
keywordPROPAGATE
- 3.27
COMPILE_ONLY
generator expressionINSTALL_PREFIX
generator expression ininstall(CODE)
$<LIST:SORT,list[,...]>
generator expression<PACKAGENAME>_ROOT
variables in addition to<PackageName>_ROOT
- 3.29
CMAKE_LINKER_TYPE
if(IS_EXECUTABLE)
- 3.31
add_custom_command()
keywordCODEGEN
Currently, the CMake minimum version is set to 3.25 without looking at CMake available version on the current systems out there. This will be updated more properly in the future.
CMake versions scheme across the systems is available at pkgs.org.
Tip
While the CMake version on some systems may be outdated, there are various
options available to install the latest version. For instance, on Ubuntu, the
most recent CMake version can be installed using snap
or through the
APT repository.
-
The
php_config
(aliasedPHP::config
) holds compilation and link properties, such as flags, definitions, libraries and include directories. All targets that need global PHP compile or link properties should link to this target.It is analogous to a global configuration class, where configuration is set during the configuration phase and then linked to targets that need the configuration.
It can be linked to a given target:
target_link_libraries(target_name PRIVATE PHP::config)
-
The
php_sapi
(aliasedPHP::sapi
) ties all target objects and configuration together. Only PHP SAPI targets should link to it.target_link_libraries(<some-php-sapi-target> PRIVATE PHP::sapi)
See also https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html for a high-level overview of the CMake build system concepts.
All PHP global CMake utility modules are located in the cmake/modules/PHP
directory.
A new module can be added by creating a new CMake file
cmake/modules/PHP/NewModule.cmake
which can be then included in the CMake
files:
include(PHP/NewModule)
Additional CMake modules or other files that are used only inside a certain
subdirectory (extension, SAPI, Zend Engine...) are located in the cmake
directories where needed:
ext/<extension>/cmake/*.cmake
- CMake modules related to extensionsapi/<sapi>/cmake/*.cmake
- CMake modules related to SAPIZend/cmake/*.cmake
- CMake modules related to Zend Engine
A list of PHP CMake modules:
- PHP/AddCustomCommand
- PHP/CheckAttribute
- PHP/CheckBuiltin
- PHP/CheckCompilerFlag
- PHP/ConfigureFile
- PHP/Install
- PHP/SearchLibraries
- PHP/Set
- PHP/SystemExtensions
-
PHP_ALL_EXTENSIONS
Global property with a list of all PHP extensions in the
ext
directory. -
PHP_ALL_SAPIS
Global property with a list of all PHP SAPIs in the
sapi
directory. -
PHP_ALWAYS_ENABLED_EXTENSIONS
Global property with a list of always enabled PHP extensions which can be considered part of the core PHP.
-
PHP_CLI
Target property that designates CMake target of PHP SAPI or extension as CLI-based (usable in a CLI environment). When enabled on a PHP SAPI target, such SAPI will have the
main/internal_functions_cli.c
object instead ofmain/internal_functions.c
and objects of enabled CLI-based PHP extensions that were built statically.When this property is enabled on a PHP extension target, extension will be only listed in the generated
main/internal_functions_cli.c
file. Other extensions will be listed also in themain/internal_functions.c
file. CLI-based extensions will only be enabled on CLI-based SAPIs.Examples of CLI-based SAPIs are
cgi
,cli
,phpdbg
, andembed
. Examples of CLI-based extensions arepcntl
andreadline
.For example, to mark some PHP SAPI as CLI-based, set
PHP_CLI
property to truthy value:set_target_properties(php_sapi_cli PROPERTIES PHP_CLI TRUE)
Basic generator expressions are also supported:
set_target_properties( php_ext_some_extension PROPERTIES PHP_CLI $<IF:$<PLATFORM_ID:Windows>,FALSE,TRUE> )
-
PHP_EXTENSION_<extension>_DEPS
Global property set by the
PHP/Extensions
module. -
PHP_EXTENSIONS
Global property with a list of all enabled PHP extensions for the current configuration. Extensions are sorted by their dependencies (extensions added with CMake command
add_dependencies()
). -
PHP_SAPIS
Global property with a list of all enabled PHP SAPIs for the current configuration.
-
PHP_THREAD_SAFETY
Target property set by the
PHP/ThreadSafety
module on thePHP::config
target, when thread safety is enabled. -
PHP_ZEND_EXTENSION
See the
PHP/Extensions
module.
PHP has several ways to install PHP extensions:
-
Statically linked to PHP
This is the default way. Extension is built together with PHP SAPI and no enabling is needed in the
php.ini
configuration. -
Shared modules
This installs the extension as dynamically loadable library. Extension to be visible in the PHP SAPI (see
php -m
) needs to be also manually enabled in thephp.ini
configuration:extension=php_extension_lowercase_name
This will load the PHP extension module file (shared object) located in the extension directory (the
extension_dir
INI directive). File can have.so
extension on *nix systems,.dll
on Windows, and possibly other extensions such as.sl
on certain HP-UX systems, or.dylib
on macOS.
The following extensions are always enabled and are part of the overall PHP engine source code:
ext/date
ext/hash
ext/json
ext/pcre
ext/random
ext/reflection
ext/spl
ext/standard
PHP extensions ecosystem also consists of the PECL
extensions. These can be installed with a separate tool pecl
:
pecl install php_extension_name
PECL tool is a simple shell script wrapper around the PHP code as part of the pear-core repository.
Note
pecl
command-line script is also being replaced with a new tool
PIE.
To build PHP extensions with CMake, a CMakeLists.txt
file needs to be added to
the extension's source directory.
Example of CMakeLists.txt
for PHP extensions can be found in the
ext/skeleton
directory.
PHP works through the concept of SAPI modules located in the sapi
directory.
When running PHP on the command line, the cli SAPI module is used:
/sapi/cli/php -v
There are other SAPI modules located in the ecosystem:
- frankenphp
- ngx-php
- ...
During the build process, there are several files generated, some of which are also tracked in the Git repository for a smoother workflow:
π <php-src>
ββπ ext
ββπ date
ββπ lib
ββπ timelib_config.h # Datetime library configuration header
ββπ mbstring
ββπ libmbfl
ββπ config.h # The libmbfl configuration header
ββπ tokenizer
ββπ tokenizer_data_stub.php # Generated by `ext/tokenizer/tokenizer_data_gen.php`
ββπ tokenizer_data.c # Generated token types data file
ββπ main
ββπ internal_functions*.c # Generated files with all internal functions
ββπ config.w32.h # Main configuration header for Windows
ββπ debug_gdb_scripts.c # Generated by `scripts/gdb/debug_gdb_scripts_gen.php`
ββπ php_config.h # Main configuration header for *nix systems
ββπ php_version.h # Generated by release managers using `configure`
ββπ scripts
ββπ php-config # PHP configuration helper script
ββπ phpize # Build configurator for PHP extensions
ββπ win32 # Windows build files
ββπ cp_enc_map.c # Generated from win32/cp_enc_map_gen.c
ββπ wsyslog.h # Generated by message compiler (mc.exe or windmc)
ββπ Zend
ββπ zend_config.h # Zend Engine configuration header on *nix systems
ββπ zend_config.w32.h # Zend Engine configuration header on Windows
ββπ zend_vm_execute.h # Generated by `Zend/zend_vm_gen.php`
ββπ zend_vm_opcodes.c # Generated by `Zend/zend_vm_gen.php`
ββπ zend_vm_opcodes.h # Generated by `Zend/zend_vm_gen.php`
So-called parser files are generated with
bison tool from *.y
source files to C
source code and header files.
Lexer files are generated with re2c tool from *.l
and
*.re
source files to C source code and header files.
To use bison
and re2c
in CMake the FindBison
and FindRE2C
modules
provide bison_target()
and re2c_target()
commands.
FindBison is a
CMake built-in module, while FindRE2C
is manually created at
cmake/modules/FindRE2C
.
Files related to bison
and re2c
:
π <php-src>
ββπ cmake
ββπ modules
ββπ FindRE2C.cmake # re2c CMake find module, bison is found via
# CMake built-in find module
ββπ Requirements.cmake # Minimum bison and re2c settings
ββπ ext
ββπ date
ββπ lib
ββπ parse_date.c # Generated with re2c 0.15.3
ββπ parse_iso_intervals.c # Generated with re2c 0.15.3
ββπ ffi
ββπ ffi_parser.c # Generated by https://github.com/dstogov/llk
ββπ json
ββπ json_parser.tab.c # Generated with bison
ββπ json_parser.tab.h # Generated with bison
ββπ json_parser.y # Parser source
ββπ json_scanner.c # Generated with re2c
ββπ json_scanner.re # Lexer source
ββπ php_json_scanner_defs.h # Generated with re2c
ββπ pdo
ββπ pdo_sql_parser.c # Generated with re2c
ββπ pdo_sql_parser.re # Source for re2c
ββπ pdo_mysql
ββπ mysql_sql_parser.c # Generated with re2c
ββπ mysql_sql_parser.re # Source for re2c
ββπ pdo_pgsql
ββπ pgsql_sql_parser.c # Generated with re2c
ββπ pgsql_sql_parser.re # Source for re2c
ββπ pdo_sqlite
ββπ sqlite_sql_parser.c # Generated with re2c
ββπ sqlite_sql_parser.re # Source for re2c
ββπ phar
ββπ phar_path_check.c # Generated with re2c
ββπ phar_path_check.re # Source for re2c
ββπ standard
ββπ url_scanner_ex.c # Generated with re2c
ββπ url_scanner_ex.re # Source for re2c
ββπ var_unserializer.c # Generated with re2c
ββπ var_unserializer.re # Source for re2c
ββπ sapi
ββπ phpdbg
ββπ phpdbg_lexer.c # Generated with re2c
ββπ phpdbg_lexer.l # Source for re2c
ββπ phpdbg_parser.c # Generated with bison
ββπ phpdbg_parser.h # Generated with bison
ββπ phpdbg_parser.y # Source for bison
ββπ phpdbg_parser.output # Generated with bison
ββπ Zend
ββπ zend_ini_parser.c # Generated with bison
ββπ zend_ini_parser.h # Generated with bison
ββπ zend_ini_parser.output # Generated with bison
ββπ zend_ini_parser.y # Parser source
ββπ zend_ini_scanner.c # Generated with re2c
ββπ zend_ini_scanner.l # Lexer source
ββπ zend_ini_scanner_defs.h # Generated with re2c
ββπ zend_language_parser.c # Generated with bison
ββπ zend_language_parser.h # Generated with bison
ββπ zend_language_parser.output # Generated with bison
ββπ zend_language_parser.y # Parser source
ββπ zend_language_scanner_defs.h # Generated with re2c
ββπ zend_language_scanner.c # Generated with re2c
ββπ zend_language_scanner.l # Lexer source
When building PHP from the released archives (php-*.tar.gz
) from
php.net these files are already included in
the tarball itself so re2c
and bison
are not required.
These are generated automatically when building PHP from the Git repository.
To re-generate these files manually apart from the main build itself, a separate
CMake target php_generate_files
can be used:
cmake -S <source-dir> -B <build-dir>
cmake --build <build-dir> -t php_generate_files
When CMake is doing configuration phase, the profiling options can be used to do build system performance analysis of CMake files.
cmake --profiling-output ./profile.json --profiling-format google-trace ../php-src
PHP source code tests (*.phpt
files) are written in PHP and are executed with
run-tests.php
script from the very beginning of the PHP development. When
building PHP with Autotools the tests are usually run by:
make TEST_PHP_ARGS=-j10 test
CMake ships with a ctest
utility that can run PHP tests in a similar way.
To enable testing the enable_testing()
is added to the CMakeLists.txt
file
and the tests are added with add_test()
.
To run the tests using CMake on the command line:
ctest --progress --verbose
The --progress
option displays a progress if there are more tests, and
--verbose
option outputs additional info to the stdout. In PHP case the
--verbose
is needed so the output of the run-tests.php
script is displayed.
CMake testing also supports presets so configuration can be coded and shared
using the CMakePresets.json
file and its testPresets
field.
ctest --preset all-enabled
Module-definition (.def) files are added to certain php-src folders where linker needs them when building DLL.
In CMake they can be simply added to the target sources:
target_sources(php_extension_name php_extension_name.def)