Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mimalloc support for macOS and Linux #663

Merged
merged 10 commits into from
Mar 23, 2025
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions config/lib.json
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,12 @@
"openssl"
]
},
"mimalloc": {
"source": "mimalloc",
"static-libs-unix": [
"mimalloc.o"
]
},
"ncurses": {
"source": "ncurses",
"static-libs-unix": [
Expand Down
10 changes: 10 additions & 0 deletions config/source.json
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,16 @@
"path": "LICENSE"
}
},
"mimalloc": {
"type": "ghtagtar",
"repo": "microsoft/mimalloc",
"match": "v2.+",
"provide-pre-built": false,
"license": {
"type": "file",
"path": "LICENSE"
}
},
"mongodb": {
"type": "ghrel",
"repo": "mongodb/mongo-php-driver",
Expand Down
9 changes: 7 additions & 2 deletions docs/en/guide/extension-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ and this extension cannot be compiled into php by static linking, so it cannot b

## xdebug

1. Xdebug is a Zend extension. The functions of Xdebug depend on PHP's Zend engine and underlying code.
If you want to statically compile it into PHP, you may need a huge amount of patch code, which is not feasible.
1. Xdebug is only buildable as a shared extension. On Linux, you need to use static-php-cli with SPC_LIBC=glibc and then compile php-xdebug from source with the option `--with-php-config=/path/to/buildroot/bin/php-config`.
2. The macOS platform can compile an xdebug extension under PHP compiled on the same platform,
extract the `xdebug.so` file, and then use the `--no-strip` parameter in static-php-cli to retain the debug symbol table and add the `ffi` extension.
The compiled `./php` binary can be configured and run by specifying the INI, eg `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`.
Expand Down Expand Up @@ -149,3 +148,9 @@ Parallel is only supported on PHP 8.0 ZTS and above.

1. The [SPX extension](https://github.com/NoiseByNorthwest/php-spx) only supports NTS mode.
2. SPX does not support Windows, and the official repository does not support static compilation. static-php-cli uses a [modified version](https://github.com/static-php/php-spx).

## mimalloc

1. This is not technically an extension, but a library.
2. Building with `--with-libs="mimalloc"` on Linux or macOS will override the default allocator.
3. This is experimental for now, but is recommended in threaded environments.
8 changes: 7 additions & 1 deletion docs/zh/guide/extension-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ bin/spc build gd --with-libs=freetype,libjpeg,libavif,libwebp --build-cli

## xdebug

1. Xdebug 是一个 Zend 扩展,Xdebug 的功能依赖于 PHP 的 Zend 引擎和底层代码,如果要将其静态编译到 PHP 中,可能需要巨量的 patch 代码,这是不可行的
1. Xdebug 只能作为共享扩展构建。在 Linux 上,您需要使用带有 `SPC_LIBC=glibc` 的 static-php-cli,然后使用选项 `--with-php-config=/path/to/buildroot/bin/php-config` 从源代码编译 php-xdebug
2. macOS 平台可以通过在相同平台编译的 PHP 下编译一个 xdebug 扩展,并提取其中的 `xdebug.so` 文件,再在 static-php-cli 中使用 `--no-strip` 参数保留调试符号表,同时加入 `ffi` 扩展。
编译的 `./php` 二进制可以通过指定 INI 配置并运行,例如`./php -d 'zend_extension=xdebug.so' your-code.php`。

Expand Down Expand Up @@ -136,3 +136,9 @@ parallel 扩展只支持 PHP 8.0 及以上版本,并只支持 ZTS 构建(`--

1. [SPX 扩展](https://github.com/NoiseByNorthwest/php-spx) 只支持非线程模式。
2. SPX 目前不支持 Windows,且官方仓库也不支持静态编译,static-php-cli 使用了 [修改版本](https://github.com/static-php/php-spx)。

## mimalloc

1. 从技术上讲,这不是扩展,而是一个库。
2. 在 Linux 或 macOS 上使用 `--with-libs="mimalloc"` 进行构建将覆盖默认分配器。
3. 目前,这还处于实验阶段,但建议在线程环境中使用。
11 changes: 9 additions & 2 deletions src/SPC/builder/linux/LinuxBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,13 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
$enable_micro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
$enable_embed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;

$mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : '';
// prepare build php envs
$envs_build_php = SystemUtil::makeEnvVarString([
'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'),
'CPPFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS'),
'LDFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS'),
'LIBS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_LIBS'),
'LIBS' => $mimallocLibs . getenv('SPC_CMD_VAR_PHP_CONFIGURE_LIBS'),
]);

// process micro upx patch if micro sapi enabled
Expand Down Expand Up @@ -310,7 +311,13 @@ protected function buildEmbed(): void
shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install");
FileSystem::replaceFileStr(BUILD_BIN_PATH . '/php-config', 'prefix=""', 'prefix="' . BUILD_ROOT_PATH . '"');
$php_config_str = FileSystem::readFile(BUILD_BIN_PATH . '/php-config');
str_replace('prefix=""', 'prefix="' . BUILD_ROOT_PATH . '"', $php_config_str);
// move mimalloc to the beginning of libs
$php_config_str = preg_replace('/(libs=")(.*?)\s*(' . preg_quote(BUILD_LIB_PATH, '/') . '\/mimalloc\.o)\s*(.*?)"/', '$1$3 $2 $4"', $php_config_str);
// move lstdc++ to the end of libs
$php_config_str = preg_replace('/(libs=")(.*?)\s*(-lstdc\+\+)\s*(.*?)"/', '$1$2 $4 $3"', $php_config_str);
FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str);
}

private function getMakeExtraVars(): array
Expand Down
12 changes: 12 additions & 0 deletions src/SPC/builder/linux/library/mimalloc.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace SPC\builder\linux\library;

class mimalloc extends LinuxLibraryBase
{
use \SPC\builder\unix\library\mimalloc;

public const NAME = 'mimalloc';
}
8 changes: 8 additions & 0 deletions src/SPC/builder/macos/MacOSBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,12 @@ public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;

// prepare build php envs
$mimallocLibs = $this->getLib('mimalloc') !== null ? BUILD_LIB_PATH . '/mimalloc.o ' : '';
$envs_build_php = SystemUtil::makeEnvVarString([
'CFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CFLAGS'),
'CPPFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_CPPFLAGS'),
'LDFLAGS' => getenv('SPC_CMD_VAR_PHP_CONFIGURE_LDFLAGS'),
'LIBS' => $mimallocLibs . getenv('SPC_CMD_VAR_PHP_CONFIGURE_LIBS'),
]);

if ($this->getLib('postgresql')) {
Expand Down Expand Up @@ -298,6 +300,12 @@ protected function buildEmbed(): void
->exec('rm ' . BUILD_ROOT_PATH . '/lib/libphp.a')
->exec('ar rcs ' . BUILD_ROOT_PATH . '/lib/libphp.a *.o')
->exec('rm -Rf ' . BUILD_ROOT_PATH . '/lib/php-o');

$php_config_str = FileSystem::readFile(BUILD_BIN_PATH . '/php-config');
str_replace('prefix=""', 'prefix="' . BUILD_ROOT_PATH . '"', $php_config_str);
// move mimalloc to the beginning of libs
$php_config_str = preg_replace('/(libs=")(.*?)\s*(' . preg_quote(BUILD_LIB_PATH, '/') . '\/mimalloc\.o)\s*(.*?)"/', '$1$3 $2 $4"', $php_config_str);
FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str);
}

private function getMakeExtraVars(): array
Expand Down
12 changes: 12 additions & 0 deletions src/SPC/builder/macos/library/mimalloc.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace SPC\builder\macos\library;

class mimalloc extends MacOSLibraryBase
{
use \SPC\builder\unix\library\mimalloc;

public const NAME = 'mimalloc';
}
8 changes: 6 additions & 2 deletions src/SPC/builder/unix/library/gettext.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@ protected function build(): void
$ldflags = $this->builder->getOption('enable-zts') ? '-lpthread' : '';

shell()->cd($this->source_dir)
->setEnv(['CFLAGS' => "{$this->getLibExtraCFlags()} {$cflags}", 'LDFLAGS' => $this->getLibExtraLdFlags() ?: $ldflags, 'LIBS' => $this->getLibExtraLibs()])
->setEnv([
'CFLAGS' => "{$this->getLibExtraCFlags()} {$cflags}",
'LDFLAGS' => $this->getLibExtraLdFlags() ?: $ldflags,
'LIBS' => $this->getLibExtraLibs(),
])
->execWithEnv(
'./configure ' .
'--enable-static ' .
'--disable-shared ' .
'--disable-java ' .
'--disable-c+ ' .
'--disable-c++ ' .
$zts .
$extra .
'--with-included-gettext ' .
Expand Down
38 changes: 38 additions & 0 deletions src/SPC/builder/unix/library/mimalloc.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace SPC\builder\unix\library;

use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;

trait mimalloc
{
/**
* @throws RuntimeException
* @throws FileSystemException
*/
protected function build(): void
{
$args = '';
if (getenv('SPC_LIBC') === 'musl') {
$args .= '-DMI_LIBC_MUSL=ON ';
}
$args .= '-DMI_BUILD_SHARED=OFF ';
$args .= '-DMI_INSTALL_TOPLEVEL=ON ';
FileSystem::resetDir($this->source_dir . '/build');
shell()->cd($this->source_dir . '/build')
->execWithEnv(
'cmake ' .
'-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' ' .
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
'-DCMAKE_BUILD_TYPE=Release ' .
$args .
'..'
)
->execWithEnv("make -j{$this->builder->concurrency}")
->execWithEnv('make install');
}
}
19 changes: 12 additions & 7 deletions src/SPC/store/Downloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,19 @@ public static function getLatestGithubTarball(string $name, array $source, strin
retry: self::getRetryTime()
), true);

if (($source['prefer-stable'] ?? false) === false) {
$url = $data[0]['tarball_url'];
} else {
$id = 0;
while ($data[$id]['prerelease'] === true) {
++$id;
$url = null;
for ($i = 0; $i < count($data); ++$i) {
if (($data[$i]['prerelease'] ?? false) === true && ($source['prefer-stable'] ?? false)) {
continue;
}
if (!($source['match'] ?? null)) {
$url = $data[$i]['tarball_url'] ?? null;
break;
}
if (preg_match('|' . $source['match'] . '|', $data[$i]['tarball_url'])) {
$url = $data[$i]['tarball_url'];
break;
}
$url = $data[$id]['tarball_url'] ?? null;
}
if (!$url) {
throw new DownloaderException("failed to find {$name} source");
Expand Down
4 changes: 4 additions & 0 deletions src/SPC/util/SPCConfigUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ public function config(array $extensions = [], array $libraries = [], bool $incl
if ($this->builder->hasCpp()) {
$libs .= $this->builder instanceof MacOSBuilder ? ' -lc++' : ' -lstdc++';
}
// mimalloc must come first
if (str_contains($libs, BUILD_LIB_PATH . '/mimalloc.o')) {
$libs = BUILD_LIB_PATH . '/mimalloc.o ' . str_replace(BUILD_LIB_PATH . '/mimalloc.o', '', $libs);
}
return [
'cflags' => $cflags,
'ldflags' => $ldflags,
Expand Down
18 changes: 9 additions & 9 deletions src/globals/test-extensions.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@

/**
* This is GitHub Actions automatic test extension args generator.
* You can edit $extensions, $with_libs and $base_combination.
* You can edit $test_php_version, $test_os, $zts, $no_strip, $upx, $prefer_pre_built, $extensions, $with_libs and $base_combination.
*/

// --------------------------------- edit area ---------------------------------

// test php version
// test php version (8.1 ~ 8.4 available, multiple for matrix)
$test_php_version = [
'8.1',
'8.2',
Expand All @@ -21,10 +21,10 @@

// test os (macos-13, macos-14, ubuntu-latest, windows-latest are available)
$test_os = [
// 'macos-13',
// 'macos-14',
// 'ubuntu-latest',
'windows-latest',
'macos-13',
'macos-14',
'ubuntu-latest',
// 'windows-latest',
];

// whether enable thread safe
Expand All @@ -40,21 +40,21 @@

// If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`).
$extensions = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => 'gd',
'Linux', 'Darwin' => '',
'Windows' => 'bz2,ctype,curl,dom,filter,gd,iconv,mbstring,opcache,openssl,pdo,pdo_sqlite,phar,session,simplexml,sqlite3,tokenizer,xml,xmlwriter,yaml,zip,zlib',
};

// If you want to test lib-suggests feature with extension, add them below (comma separated, example `libwebp,libavif`).
$with_libs = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => 'freetype',
'Linux', 'Darwin' => 'mimalloc',
'Windows' => 'libjpeg,libavif,freetype,libwebp',
};

// Please change your test base combination. We recommend testing with `common`.
// You can use `common`, `bulk`, `minimal` or `none`.
// note: combination is only available for *nix platform. Windows must use `none` combination
$base_combination = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => 'minimal',
'Linux', 'Darwin' => 'bulk',
'Windows' => 'none',
};

Expand Down
Loading