Compare commits

...

2 Commits
v3 ... ttt

Author SHA1 Message Date
crazywhalecc
a3003d363e ttt 2026-04-02 08:43:31 +08:00
crazywhalecc
b5b4e8f622 ttt 2026-04-02 08:43:26 +08:00
11 changed files with 1522 additions and 1390 deletions

57
TODO.md Normal file
View File

@@ -0,0 +1,57 @@
# v3 TODO List
Tracking items identified during the v2 → v3 migration audit.
---
## Commands
- [ ] Implement `craft` command (drives full build from `craft.yml`; should be easier with v3 vendor/registry mode)
- [ ] Migrate `micro:combine` command (combine `micro.sfx` with PHP code + INI injection)
- [ ] Implement `dump-extensions` command (extract required extensions from `composer.json` / `composer.lock`)
- [ ] Design and implement v3 dev toolchain commands (WIP — needs design decision):
- [ ] `dev:extensions` / equivalent listing command
- [ ] `dev:php-version`, `dev:ext-version`, `dev:lib-version`
- [ ] Doc generation commands (`dev:gen-ext-docs`, `dev:gen-ext-dep-docs`, `dev:gen-lib-dep-docs`) — pending v3 doc design
---
## Source Patches (SourcePatcher → Artifact migration)
The following v2 `SourcePatcher` hooks are not yet migrated to v3 `src/Package/Artifact/` classes:
- [ ] Migrate `patchSQLSRVWin32` — removes `/sdl` compile flag to prevent Zend build failure on Windows
- [ ] Migrate `patchSQLSRVPhp85` — fixes `pdo_sqlsrv` directory layout for PHP 8.5
- [ ] Migrate `patchYamlWin32` — patches `config.w32` `_a.lib` detection logic for the `yaml` extension
- [ ] Migrate `patchImagickWith84` — applies PHP 8.4 compatibility patch for `imagick` based on version detection
---
## Extension Package Classes (Unix)
Extensions that had non-trivial v2 build logic and are missing a v3 `src/Package/Extension/` class:
- [x] `gettext` — macOS: fix `config.m4` bracket syntax for cross-version compatibility + append frameworks to linker flags (critical for macOS linking; this is a Unix-side gap, not Windows-only)
---
## Windows Extensions (Early Stage)
Windows extension support is still in early stage. The following extensions had Windows-specific configure args or patches in v2 and are pending v3 Windows implementation:
- [ ] `amqp` — Windows configure args
- [ ] `com_dotnet` — Windows-only extension
- [ ] `dom` — remove `dllmain.c` from `config.w32`
- [ ] `ev` — fix `PHP_EV_SHARED` in `config.w32`
- [ ] `gmssl` — add `CHECK_LIB("gmssl.lib")` to `config.w32`
- [ ] `intl` — fix `PHP_INTL_SHARED` in `config.w32`
- [ ] `lz4` — Windows configure args
- [ ] `mbregex` — Windows configure args
- [ ] `sqlsrv` / `pdo_sqlsrv` — complex conditional build logic (independent `sqlsrv` without `pdo_sqlsrv`)
- [ ] `xml` — remove `dllmain.c` from `config.w32`; handles `soap`, `xmlreader`, `xmlwriter`, `simplexml`
---
## Documentation
- [ ] Write v3 user documentation (currently zero v3 docs)

View File

@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Package\Extension;
use Package\Target\php;
use StaticPHP\Attribute\Package\BeforeStage;
use StaticPHP\Attribute\Package\Extension;
use StaticPHP\Attribute\PatchDescription;
use StaticPHP\Package\PackageInstaller;
use StaticPHP\Runtime\SystemTarget;
use StaticPHP\Util\FileSystem;
#[Extension('gettext')]
class gettext
{
#[BeforeStage('php', [php::class, 'buildconfForUnix'], 'ext-gettext')]
#[PatchDescription('Patch gettext extension config.m4 to fix library detection on macOS')]
public function patchBeforeBuildconf(PackageInstaller $installer): void
{
spc_skip_unless(SystemTarget::getTargetOS() === 'Darwin', 'gettext extension patch is only needed on macOS');
$php_src = $installer->getTargetPackage('php')->getSourceDir();
FileSystem::replaceFileStr(
"{$php_src}/ext/gettext/config.m4",
['AC_CHECK_LIB($GETTEXT_CHECK_IN_LIB', 'AC_CHECK_LIB([$GETTEXT_CHECK_IN_LIB'],
['AC_CHECK_LIB(intl', 'AC_CHECK_LIB([intl'] // new php versions use a bracket
);
}
}

View File

@@ -8,6 +8,7 @@ use StaticPHP\Attribute\Package\BuildFor;
use StaticPHP\Attribute\Package\Library; use StaticPHP\Attribute\Package\Library;
use StaticPHP\Package\LibraryPackage; use StaticPHP\Package\LibraryPackage;
use StaticPHP\Runtime\Executor\UnixAutoconfExecutor; use StaticPHP\Runtime\Executor\UnixAutoconfExecutor;
use StaticPHP\Util\FileSystem;
#[Library('idn2')] #[Library('idn2')]
class idn2 class idn2
@@ -29,5 +30,10 @@ class idn2
->make(); ->make();
$lib->patchPkgconfPrefix(['libidn2.pc']); $lib->patchPkgconfPrefix(['libidn2.pc']);
$lib->patchLaDependencyPrefix(); $lib->patchLaDependencyPrefix();
// libunistring is in Libs.private of libidn2.pc, but CMake's pkg_check_modules
// does not follow Libs.private for static linking. Promote it to Libs so that
// consumers linking static binaries (e.g. the curl exe) can resolve _uc_* / _u32_* symbols.
$libidn2_pc = BUILD_ROOT_PATH . '/lib/pkgconfig/libidn2.pc';
FileSystem::replaceFileStr($libidn2_pc, 'Libs: -L${libdir} -lidn2', 'Libs: -L${libdir} -lidn2 -lunistring');
} }
} }

View File

@@ -10,6 +10,7 @@ use StaticPHP\Package\LibraryPackage;
use StaticPHP\Package\PackageInstaller; use StaticPHP\Package\PackageInstaller;
use StaticPHP\Runtime\Executor\UnixAutoconfExecutor; use StaticPHP\Runtime\Executor\UnixAutoconfExecutor;
use StaticPHP\Runtime\SystemTarget; use StaticPHP\Runtime\SystemTarget;
use StaticPHP\Util\FileSystem;
use StaticPHP\Util\SPCConfigUtil; use StaticPHP\Util\SPCConfigUtil;
#[Library('krb5')] #[Library('krb5')]
@@ -59,5 +60,11 @@ class krb5
'mit-krb5.pc', 'mit-krb5.pc',
'gssrpc.pc', 'gssrpc.pc',
]); ]);
// libkrb5support is in Libs.private of mit-krb5.pc, but CMake's pkg_check_modules
// does not follow Libs.private for static linking. Promote it to Libs so that
// consumers linking static binaries (e.g. the curl exe) can resolve _k5_* symbols.
$mit_krb5_pc = BUILD_ROOT_PATH . '/lib/pkgconfig/mit-krb5.pc';
FileSystem::replaceFileStr($mit_krb5_pc, 'Libs.private: -lkrb5support', 'Libs.private:');
FileSystem::replaceFileStr($mit_krb5_pc, '-lcom_err', '-lcom_err -lkrb5support');
} }
} }

View File

@@ -29,7 +29,6 @@ class nghttp2
->build(); ->build();
FileSystem::replaceFileStr($lib->getIncludeDir() . '\nghttp2\nghttp2.h', '#ifdef NGHTTP2_STATICLIB', '#if 1'); FileSystem::replaceFileStr($lib->getIncludeDir() . '\nghttp2\nghttp2.h', '#ifdef NGHTTP2_STATICLIB', '#if 1');
} }
#[BuildFor('Linux')] #[BuildFor('Linux')]

View File

@@ -81,16 +81,19 @@ class curl
->addConfigureArgs( ->addConfigureArgs(
'-DBUILD_CURL_EXE=ON', '-DBUILD_CURL_EXE=ON',
'-DBUILD_LIBCURL_DOCS=OFF', '-DBUILD_LIBCURL_DOCS=OFF',
'-DCURL_USE_PKGCONFIG=ON',
) )
->build(); ->build();
// patch pkgconf // patch pkgconf
$lib->patchPkgconfPrefix(['libcurl.pc']); $lib->patchPkgconfPrefix(['libcurl.pc']);
// curl's CMake embeds krb5 link flags directly without following Requires.private chain, // Ensure -lkrb5support is present in libcurl.pc for downstream consumers.
// so -lkrb5support (from mit-krb5.pc Libs.private) is missing from libcurl.pc. // krb5.php already promotes it to Libs in mit-krb5.pc before the build, so
// CMake should have embedded it; this is a safety fallback if it was missed.
$pc_path = "{$lib->getLibDir()}/pkgconfig/libcurl.pc"; $pc_path = "{$lib->getLibDir()}/pkgconfig/libcurl.pc";
if (str_contains(FileSystem::readFile($pc_path), '-lgssapi_krb5')) { $pc_content = FileSystem::readFile($pc_path);
FileSystem::replaceFileRegex($pc_path, '/-lcom_err$/m', '-lcom_err -lkrb5support'); if (str_contains($pc_content, '-lgssapi_krb5') && !str_contains($pc_content, '-lkrb5support')) {
FileSystem::replaceFileRegex($pc_path, '/-lcom_err\b/', '-lcom_err -lkrb5support');
} }
shell()->cd("{$lib->getLibDir()}/cmake/CURL/") shell()->cd("{$lib->getLibDir()}/cmake/CURL/")
->exec("sed -ie 's|\"/lib/libcurl.a\"|\"{$lib->getLibDir()}/libcurl.a\"|g' CURLTargets-release.cmake"); ->exec("sed -ie 's|\"/lib/libcurl.a\"|\"{$lib->getLibDir()}/libcurl.a\"|g' CURLTargets-release.cmake");

View File

@@ -328,7 +328,7 @@ trait windows
while ($i < count($lines)) { while ($i < count($lines)) {
$line = $lines[$i]; $line = $lines[$i];
// Check if this is the embed lib target dependency line (contains the lib name and $(BUILD_DIR)\$(PHPLIB)) // Check if this is the embed lib target dependency line (contains the lib name and $(BUILD_DIR)\$(PHPLIB))
if (str_contains($line, "\$(BUILD_DIR)\\{$embed_lib}:") && str_contains($line, '$(BUILD_DIR)\\$(PHPLIB)')) { if (str_contains($line, "\$(BUILD_DIR)\\{$embed_lib}:") && str_contains($line, '$(BUILD_DIR)\$(PHPLIB)')) {
// Replace the dependency line // Replace the dependency line
// Original: $(BUILD_DIR)\php8embed.lib: $(DEPS_EMBED) $(EMBED_GLOBAL_OBJS) $(BUILD_DIR)\$(PHPLIB) $(BUILD_DIR)\php8embed.lib.res $(BUILD_DIR)\php8embed.lib.manifest // Original: $(BUILD_DIR)\php8embed.lib: $(DEPS_EMBED) $(EMBED_GLOBAL_OBJS) $(BUILD_DIR)\$(PHPLIB) $(BUILD_DIR)\php8embed.lib.res $(BUILD_DIR)\php8embed.lib.manifest
// New: $(BUILD_DIR)\php8embed.lib: $(DEPS_EMBED) $(EMBED_GLOBAL_OBJS) $(PHP_GLOBAL_OBJS) $(STATIC_EXT_OBJS) $(ASM_OBJS) $(BUILD_DIR)\php8embed.lib.res $(BUILD_DIR)\php8embed.lib.manifest // New: $(BUILD_DIR)\php8embed.lib: $(DEPS_EMBED) $(EMBED_GLOBAL_OBJS) $(PHP_GLOBAL_OBJS) $(STATIC_EXT_OBJS) $(ASM_OBJS) $(BUILD_DIR)\php8embed.lib.res $(BUILD_DIR)\php8embed.lib.manifest
@@ -347,7 +347,7 @@ trait windows
if ($i < count($lines) && str_contains($lines[$i], '$(MAKE_LIB)')) { if ($i < count($lines) && str_contains($lines[$i], '$(MAKE_LIB)')) {
$cmd_line = $lines[$i]; $cmd_line = $lines[$i];
// Remove $(BUILD_DIR)\$(PHPLIB) from the command (note the backslash) // Remove $(BUILD_DIR)\$(PHPLIB) from the command (note the backslash)
$cmd_line = str_replace(' $(BUILD_DIR)\\$(PHPLIB)', '', $cmd_line); $cmd_line = str_replace(' $(BUILD_DIR)\$(PHPLIB)', '', $cmd_line);
// Add PHP_GLOBAL_OBJS_RESP and STATIC_EXT_OBJS_RESP after EMBED_GLOBAL_OBJS_RESP // Add PHP_GLOBAL_OBJS_RESP and STATIC_EXT_OBJS_RESP after EMBED_GLOBAL_OBJS_RESP
$cmd_line = str_replace( $cmd_line = str_replace(
'$(EMBED_GLOBAL_OBJS_RESP)', '$(EMBED_GLOBAL_OBJS_RESP)',
@@ -418,84 +418,6 @@ trait windows
$this->installPhpHeadersForWindows($package, $installer); $this->installPhpHeadersForWindows($package, $installer);
} }
/**
* Install PHP headers to buildroot/include for embed SAPI development.
* This mirrors the 'make install-headers' behavior on Unix.
*/
private function installPhpHeadersForWindows(TargetPackage $package, PackageInstaller $installer): void
{
InteractiveTerm::setMessage('Installing PHP headers for embed SAPI');
$source_dir = $package->getSourceDir();
$include_dir = $package->getIncludeDir();
$php_include_dir = "{$include_dir}\\php";
// Create directory structure
FileSystem::createDir("{$php_include_dir}\\main");
FileSystem::createDir("{$php_include_dir}\\Zend");
FileSystem::createDir("{$php_include_dir}\\TSRM");
FileSystem::createDir("{$php_include_dir}\\sapi\\embed");
// Copy main/*.h
foreach (glob("{$source_dir}\\main\\*.h") as $h) {
FileSystem::copy($h, "{$php_include_dir}\\main\\" . basename($h));
}
// Copy Zend/*.h
foreach (glob("{$source_dir}\\Zend\\*.h") as $h) {
$target = "{$php_include_dir}\\Zend\\" . basename($h);
FileSystem::copy($h, $target);
// Fix GCC-specific #warning directive not supported by MSVC
if (basename($h) === 'zend_atomic.h') {
FileSystem::replaceFileStr($target, '#warning No atomics support detected. Please open an issue with platform details.', '#pragma message("No atomics support detected. Please open an issue with platform details.")');
}
}
// Copy TSRM/*.h
foreach (glob("{$source_dir}\\TSRM\\*.h") as $h) {
FileSystem::copy($h, "{$php_include_dir}\\TSRM\\" . basename($h));
}
// Copy embed SAPI header
FileSystem::copy("{$source_dir}\\sapi\\embed\\php_embed.h", "{$php_include_dir}\\sapi\\embed\\php_embed.h");
// Copy generated config.h (config.w32.h on Windows) to php_config.h
$rel_type = 'Release';
$ts = $package->getBuildOption('enable-zts', false) ? '_TS' : '';
$build_dir = "{$source_dir}\\x64\\{$rel_type}{$ts}";
// Always copy config.w32.h from source (it's used for both build and headers)
if (file_exists("{$source_dir}\\main\\config.w32.h")) {
FileSystem::copy("{$source_dir}\\main\\config.w32.h", "{$php_include_dir}\\main\\php_config.h");
}
// Windows: zend_config.w32.h must be copied as zend_config.h for Zend headers to work
if (file_exists("{$source_dir}\\Zend\\zend_config.w32.h")) {
FileSystem::copy("{$source_dir}\\Zend\\zend_config.w32.h", "{$php_include_dir}\\Zend\\zend_config.h");
}
// Copy extension headers for enabled extensions
foreach ($installer->getResolvedPackages(PhpExtensionPackage::class) as $ext) {
$ext_name = $ext->getExtensionName();
$ext_dir = "{$source_dir}\\ext\\{$ext_name}";
if (is_dir($ext_dir)) {
$target_ext_dir = "{$php_include_dir}\\ext\\{$ext_name}";
FileSystem::createDir($target_ext_dir);
foreach (glob("{$ext_dir}\\*.h") as $h) {
FileSystem::copy($h, "{$target_ext_dir}\\" . basename($h));
}
// Also copy any arginfo headers
foreach (glob("{$ext_dir}\\*_arginfo.h") as $h) {
if (!file_exists("{$target_ext_dir}\\" . basename($h))) {
FileSystem::copy($h, "{$target_ext_dir}\\" . basename($h));
}
}
}
}
$package->setOutput('PHP headers path for embed SAPI', $php_include_dir);
}
#[BuildFor('Windows')] #[BuildFor('Windows')]
public function buildWin(TargetPackage $package): void public function buildWin(TargetPackage $package): void
{ {
@@ -780,4 +702,82 @@ C_CODE;
} }
} }
} }
/**
* Install PHP headers to buildroot/include for embed SAPI development.
* This mirrors the 'make install-headers' behavior on Unix.
*/
private function installPhpHeadersForWindows(TargetPackage $package, PackageInstaller $installer): void
{
InteractiveTerm::setMessage('Installing PHP headers for embed SAPI');
$source_dir = $package->getSourceDir();
$include_dir = $package->getIncludeDir();
$php_include_dir = "{$include_dir}\\php";
// Create directory structure
FileSystem::createDir("{$php_include_dir}\\main");
FileSystem::createDir("{$php_include_dir}\\Zend");
FileSystem::createDir("{$php_include_dir}\\TSRM");
FileSystem::createDir("{$php_include_dir}\\sapi\\embed");
// Copy main/*.h
foreach (glob("{$source_dir}\\main\\*.h") as $h) {
FileSystem::copy($h, "{$php_include_dir}\\main\\" . basename($h));
}
// Copy Zend/*.h
foreach (glob("{$source_dir}\\Zend\\*.h") as $h) {
$target = "{$php_include_dir}\\Zend\\" . basename($h);
FileSystem::copy($h, $target);
// Fix GCC-specific #warning directive not supported by MSVC
if (basename($h) === 'zend_atomic.h') {
FileSystem::replaceFileStr($target, '#warning No atomics support detected. Please open an issue with platform details.', '#pragma message("No atomics support detected. Please open an issue with platform details.")');
}
}
// Copy TSRM/*.h
foreach (glob("{$source_dir}\\TSRM\\*.h") as $h) {
FileSystem::copy($h, "{$php_include_dir}\\TSRM\\" . basename($h));
}
// Copy embed SAPI header
FileSystem::copy("{$source_dir}\\sapi\\embed\\php_embed.h", "{$php_include_dir}\\sapi\\embed\\php_embed.h");
// Copy generated config.h (config.w32.h on Windows) to php_config.h
$rel_type = 'Release';
$ts = $package->getBuildOption('enable-zts', false) ? '_TS' : '';
$build_dir = "{$source_dir}\\x64\\{$rel_type}{$ts}";
// Always copy config.w32.h from source (it's used for both build and headers)
if (file_exists("{$source_dir}\\main\\config.w32.h")) {
FileSystem::copy("{$source_dir}\\main\\config.w32.h", "{$php_include_dir}\\main\\php_config.h");
}
// Windows: zend_config.w32.h must be copied as zend_config.h for Zend headers to work
if (file_exists("{$source_dir}\\Zend\\zend_config.w32.h")) {
FileSystem::copy("{$source_dir}\\Zend\\zend_config.w32.h", "{$php_include_dir}\\Zend\\zend_config.h");
}
// Copy extension headers for enabled extensions
foreach ($installer->getResolvedPackages(PhpExtensionPackage::class) as $ext) {
$ext_name = $ext->getExtensionName();
$ext_dir = "{$source_dir}\\ext\\{$ext_name}";
if (is_dir($ext_dir)) {
$target_ext_dir = "{$php_include_dir}\\ext\\{$ext_name}";
FileSystem::createDir($target_ext_dir);
foreach (glob("{$ext_dir}\\*.h") as $h) {
FileSystem::copy($h, "{$target_ext_dir}\\" . basename($h));
}
// Also copy any arginfo headers
foreach (glob("{$ext_dir}\\*_arginfo.h") as $h) {
if (!file_exists("{$target_ext_dir}\\" . basename($h))) {
FileSystem::copy($h, "{$target_ext_dir}\\" . basename($h));
}
}
}
}
$package->setOutput('PHP headers path for embed SAPI', $php_include_dir);
}
} }

View File

@@ -10,10 +10,13 @@ use StaticPHP\Exception\SPCInternalException;
use StaticPHP\Package\LibraryPackage; use StaticPHP\Package\LibraryPackage;
use StaticPHP\Package\PackageBuilder; use StaticPHP\Package\PackageBuilder;
use StaticPHP\Package\PackageInstaller; use StaticPHP\Package\PackageInstaller;
use StaticPHP\Package\TargetPackage;
use StaticPHP\Runtime\Shell\UnixShell; use StaticPHP\Runtime\Shell\UnixShell;
use StaticPHP\Runtime\SystemTarget;
use StaticPHP\Util\FileSystem; use StaticPHP\Util\FileSystem;
use StaticPHP\Util\InteractiveTerm; use StaticPHP\Util\InteractiveTerm;
use StaticPHP\Util\PkgConfigUtil; use StaticPHP\Util\PkgConfigUtil;
use StaticPHP\Util\SPCConfigUtil;
use ZM\Logger\ConsoleColor; use ZM\Logger\ConsoleColor;
/** /**
@@ -214,7 +217,7 @@ class UnixCMakeExecutor extends Executor
*/ */
private function getDefaultCMakeArgs(): array private function getDefaultCMakeArgs(): array
{ {
return $this->custom_default_args ?? [ $args = $this->custom_default_args ?? [
'-DCMAKE_BUILD_TYPE=Release', '-DCMAKE_BUILD_TYPE=Release',
"-DCMAKE_INSTALL_PREFIX={$this->package->getBuildRootPath()}", "-DCMAKE_INSTALL_PREFIX={$this->package->getBuildRootPath()}",
'-DCMAKE_INSTALL_BINDIR=bin', '-DCMAKE_INSTALL_BINDIR=bin',
@@ -224,6 +227,20 @@ class UnixCMakeExecutor extends Executor
'-DBUILD_SHARED_LIBS=OFF', '-DBUILD_SHARED_LIBS=OFF',
"-DCMAKE_TOOLCHAIN_FILE={$this->makeCmakeToolchainFile()}", "-DCMAKE_TOOLCHAIN_FILE={$this->makeCmakeToolchainFile()}",
]; ];
// EXE linker flags: base system libs + framework flags for target packages
$exeLinkerFlags = SystemTarget::getRuntimeLibs();
if ($this->package instanceof TargetPackage) {
$resolvedNames = array_keys($this->installer->getResolvedPackages());
$resolvedNames[] = $this->package->getName();
$fwFlags = SPCConfigUtil::getFrameworksString($resolvedNames);
if ($fwFlags !== '') {
$exeLinkerFlags .= " {$fwFlags}";
}
}
$args[] = "-DCMAKE_EXE_LINKER_FLAGS=\"{$exeLinkerFlags}\"";
return $args;
} }
/** /**
@@ -274,13 +291,13 @@ SET(CMAKE_PREFIX_PATH "{$root}")
SET(CMAKE_INSTALL_PREFIX "{$root}") SET(CMAKE_INSTALL_PREFIX "{$root}")
SET(CMAKE_INSTALL_LIBDIR "lib") SET(CMAKE_INSTALL_LIBDIR "lib")
set(PKG_CONFIG_EXECUTABLE "{$pkgConfigExecutable}") set(PKG_CONFIG_EXECUTABLE "{$pkgConfigExecutable}" CACHE FILEPATH "pkg-config executable" FORCE)
set(PKG_CONFIG_ARGN "--static" CACHE STRING "Extra arguments for pkg-config" FORCE) set(PKG_CONFIG_ARGN "--static" CACHE STRING "Extra arguments for pkg-config" FORCE)
set(ENV{PKG_CONFIG_PATH} "{$root}/lib/pkgconfig")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_EXE_LINKER_FLAGS "-ldl -lpthread -lm -lutil")
CMAKE; CMAKE;
// Whoops, linux may need CMAKE_AR sometimes // Whoops, linux may need CMAKE_AR sometimes
if (PHP_OS_FAMILY === 'Linux') { if (PHP_OS_FAMILY === 'Linux') {

View File

@@ -57,8 +57,9 @@ class SPCConfigUtil
$libs .= " {$extra_env}"; $libs .= " {$extra_env}";
} }
// package frameworks // package frameworks
if (SystemTarget::getTargetOS() === 'Darwin') { $fwStr = self::getFrameworksString($resolved);
$libs .= " {$this->getFrameworksString($resolved)}"; if ($fwStr !== '') {
$libs .= " {$fwStr}";
} }
// C++ // C++
if ($this->hasCpp($resolved)) { if ($this->hasCpp($resolved)) {
@@ -236,8 +237,9 @@ class SPCConfigUtil
} }
// package frameworks // package frameworks
if (SystemTarget::getTargetOS() === 'Darwin') { $fwStr = self::getFrameworksString($resolved_packages);
$libs .= " {$this->getFrameworksString($resolved_packages)}"; if ($fwStr !== '') {
$libs .= " {$fwStr}";
} }
// C++ // C++
@@ -285,6 +287,31 @@ class SPCConfigUtil
]; ];
} }
/**
* Get the frameworks string for a list of packages.
*
* Returns empty string on non-Darwin platforms.
*
* @param string[] $packages Package names to collect frameworks from
* @return string e.g. "-framework Kerberos -framework CoreFoundation"
*/
public static function getFrameworksString(array $packages): string
{
if (SystemTarget::getTargetOS() !== 'Darwin') {
return '';
}
$list = [];
foreach ($packages as $package) {
foreach (PackageConfig::get($package, 'frameworks', []) as $fw) {
$ks = '-framework ' . $fw;
if (!in_array($ks, $list)) {
$list[] = $ks;
}
}
}
return implode(' ', $list);
}
private function hasCpp(array $packages): bool private function hasCpp(array $packages): bool
{ {
foreach ($packages as $package) { foreach ($packages as $package) {
@@ -492,18 +519,4 @@ class SPCConfigUtil
} }
return $lib; return $lib;
} }
private function getFrameworksString(array $extensions): string
{
$list = [];
foreach ($extensions as $extension) {
foreach (PackageConfig::get($extension, 'frameworks', []) as $fw) {
$ks = '-framework ' . $fw;
if (!in_array($ks, $list)) {
$list[] = $ks;
}
}
}
return implode(' ', $list);
}
} }