From c8eb62e8f0fbee4bf188cb9e5427b55273135677 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Tue, 22 Jul 2025 17:23:13 +0800 Subject: [PATCH] Add real pkg-config integration --- src/SPC/builder/LibraryBase.php | 40 ++++++---- src/SPC/util/ConfigValidator.php | 3 + src/SPC/util/PkgConfigUtil.php | 69 +++++++++++++++++ src/SPC/util/SPCConfigUtil.php | 112 ++++++++++++++++----------- tests/SPC/util/SPCConfigUtilTest.php | 2 +- 5 files changed, 165 insertions(+), 61 deletions(-) create mode 100644 src/SPC/util/PkgConfigUtil.php diff --git a/src/SPC/builder/LibraryBase.php b/src/SPC/builder/LibraryBase.php index b97b5929..8d07f2e5 100644 --- a/src/SPC/builder/LibraryBase.php +++ b/src/SPC/builder/LibraryBase.php @@ -182,20 +182,7 @@ abstract class LibraryBase return LIB_STATUS_INSTALL_FAILED; } } - foreach ($this->getStaticLibs() as $name) { - if (!file_exists(BUILD_LIB_PATH . "/{$name}")) { - $this->tryInstall($lock, true); - return LIB_STATUS_OK; - } - } - foreach ($this->getHeaders() as $name) { - if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) { - $this->tryInstall($lock, true); - return LIB_STATUS_OK; - } - } - // pkg-config is treated specially. If it is pkg-config, check if the pkg-config binary exists - if (static::NAME === 'pkg-config' && !file_exists(BUILD_ROOT_PATH . '/bin/pkg-config')) { + if (!$this->isLibraryInstalled()) { $this->tryInstall($lock, true); return LIB_STATUS_OK; } @@ -397,4 +384,29 @@ abstract class LibraryBase } } } + + protected function isLibraryInstalled(): bool + { + foreach (Config::getLib(static::NAME, 'static-libs', []) as $name) { + if (!file_exists(BUILD_LIB_PATH . "/{$name}")) { + return false; + } + } + foreach (Config::getLib(static::NAME, 'headers', []) as $name) { + if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) { + return false; + } + } + foreach (Config::getLib(static::NAME, 'pkg-configs', []) as $name) { + if (!file_exists(BUILD_LIB_PATH . "/pkgconfig/{$name}.pc")) { + return false; + } + } + foreach (Config::getLib(static::NAME, 'bin', []) as $name) { + if (!file_exists(BUILD_BIN_PATH . "/{$name}")) { + return false; + } + } + return true; + } } diff --git a/src/SPC/util/ConfigValidator.php b/src/SPC/util/ConfigValidator.php index dbc8ce23..b908efce 100644 --- a/src/SPC/util/ConfigValidator.php +++ b/src/SPC/util/ConfigValidator.php @@ -90,6 +90,9 @@ class ConfigValidator if (isset($lib['static-libs' . $suffix]) && !is_list_array($lib['static-libs' . $suffix])) { throw new ValidationException("lib {$name} static-libs must be a list"); } + if (isset($lib['pkg-configs' . $suffix]) && !is_list_array($lib['pkg-configs' . $suffix])) { + throw new ValidationException("lib {$name} pkg-configs must be a list"); + } } // check if frameworks is a list array if (isset($lib['frameworks']) && !is_list_array($lib['frameworks'])) { diff --git a/src/SPC/util/PkgConfigUtil.php b/src/SPC/util/PkgConfigUtil.php new file mode 100644 index 00000000..84c2c3c6 --- /dev/null +++ b/src/SPC/util/PkgConfigUtil.php @@ -0,0 +1,69 @@ +builder = $builder; // BuilderProvider::makeBuilderByInput($input ?? new ArgvInput()); @@ -54,14 +54,17 @@ class SPCConfigUtil } ob_get_clean(); $ldflags = $this->getLdflagsString(); - $libs = $this->getLibsString($libraries, $with_dependencies); + $libs = $this->getLibsString($libraries); if (SPCTarget::getTargetOS() === 'Darwin') { $libs .= " {$this->getFrameworksString($extensions)}"; } - $cflags = $this->getIncludesString(); + $cflags = $this->getIncludesString($libraries); + $libs = trim("-lc {$libs}"); // embed - $libs = trim("-lphp -lc {$libs}"); + if ($this->link_php) { + $libs = "-lphp {$libs}"; + } $extra_env = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS'); if (is_string($extra_env)) { $libs .= ' ' . trim($extra_env, '"'); @@ -81,18 +84,33 @@ class SPCConfigUtil ]; } - private function getIncludesString(): string + private function getIncludesString(array $libraries): string { $base = BUILD_INCLUDE_PATH; - $php_embed_includes = [ - "-I{$base}", - "-I{$base}/php", - "-I{$base}/php/main", - "-I{$base}/php/TSRM", - "-I{$base}/php/Zend", - "-I{$base}/php/ext", - ]; - return implode(' ', $php_embed_includes); + $includes = ["-I{$base}"]; + + // link with libphp + if ($this->link_php) { + $includes = [ + ...$includes, + "-I{$base}/php", + "-I{$base}/php/main", + "-I{$base}/php/TSRM", + "-I{$base}/php/Zend", + "-I{$base}/php/ext", + ]; + } + + // parse pkg-configs + foreach ($libraries as $library) { + $pc_cflags = implode(' ', Config::getLib($library, 'pkg-configs', [])); + if ($pc_cflags !== '') { + $pc_cflags = PkgConfigUtil::getCflags($pc_cflags); + $includes[] = $pc_cflags; + } + } + $includes = array_unique($includes); + return implode(' ', $includes); } private function getLdflagsString(): string @@ -100,51 +118,53 @@ class SPCConfigUtil return '-L' . BUILD_LIB_PATH; } - private function getLibsString(array $libraries, bool $withDependencies = false): string + private function getLibsString(array $libraries): string { $short_name = []; - foreach (array_reverse($libraries) as $library) { + $frameworks = []; + + foreach ($libraries as $library) { + // convert all static-libs to short names $libs = Config::getLib($library, 'static-libs', []); foreach ($libs as $lib) { - if ($withDependencies) { - $noExt = str_replace('.a', '', $lib); - $requiredLibs = []; - $pkgconfFile = BUILD_LIB_PATH . "/pkgconfig/{$noExt}.pc"; - if (file_exists($pkgconfFile)) { - $lines = file($pkgconfFile); - foreach ($lines as $value) { - if (str_starts_with($value, 'Libs')) { - $items = explode(' ', $value); - foreach ($items as $item) { - $item = trim($item); - if (str_starts_with($item, '-l')) { - $requiredLibs[] = $item; - } - } - } - } - } else { - $requiredLibs[] = $this->getShortLibName($lib); - } - foreach ($requiredLibs as $requiredLib) { - if (!in_array($requiredLib, $short_name)) { - $short_name[] = $requiredLib; - } - } - } else { - $short_name[] = $this->getShortLibName($lib); + // check file existence + if (!file_exists(BUILD_LIB_PATH . "/{$lib}")) { + throw new WrongUsageException("Library file '{$lib}' for lib [{$library}] does not exist in '" . BUILD_LIB_PATH . "'. Please build it first."); + } + $short_name[] = $this->getShortLibName($lib); + } + // add frameworks for macOS + if (SPCTarget::getTargetOS() === 'Darwin') { + $frameworks = array_merge($frameworks, Config::getLib($library, 'frameworks', [])); + } + // add pkg-configs libs + $pkg_configs = Config::getLib($library, 'pkg-configs', []); + foreach ($pkg_configs as $pkg_config) { + if (!file_exists(BUILD_LIB_PATH . "/pkgconfig/{$pkg_config}.pc")) { + throw new WrongUsageException("pkg-config file '{$pkg_config}.pc' for lib [{$library}] does not exist in '" . BUILD_LIB_PATH . "/pkgconfig'. Please build it first."); } } - if (PHP_OS_FAMILY !== 'Darwin') { - continue; + $pkg_configs = implode(' ', $pkg_configs); + if ($pkg_configs !== '') { + $pc_libs = PkgConfigUtil::getLibsArray($pkg_configs); + $short_name = [...$short_name, ...$pc_libs]; } - foreach (Config::getLib($library, 'frameworks', []) as $fw) { + } + + // post-process + $short_name = array_unique(array_reverse($short_name)); + $frameworks = array_unique(array_reverse($frameworks)); + + // process frameworks to short_name + if (SPCTarget::getTargetOS() === 'Darwin') { + foreach ($frameworks as $fw) { $ks = '-framework ' . $fw; if (!in_array($ks, $short_name)) { $short_name[] = $ks; } } } + if (in_array('imap', $libraries) && SPCTarget::getLibc() === 'glibc') { $short_name[] = '-lcrypt'; } diff --git a/tests/SPC/util/SPCConfigUtilTest.php b/tests/SPC/util/SPCConfigUtilTest.php index ebd2f92a..c37f01f3 100644 --- a/tests/SPC/util/SPCConfigUtilTest.php +++ b/tests/SPC/util/SPCConfigUtilTest.php @@ -54,7 +54,7 @@ class SPCConfigUtilTest extends TestCase $this->assertStringContainsString('-lphp', $result['libs']); // has cpp - $result = (new SPCConfigUtil())->config(['swoole']); + $result = (new SPCConfigUtil())->config(['rar']); $this->assertStringContainsString(PHP_OS_FAMILY === 'Darwin' ? '-lc++' : '-lstdc++', $result['libs']); // has mimalloc.o in lib dir