mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-09 01:45:36 +08:00
Add methods to retrieve package sub-dependencies and configuration
This commit is contained in:
@@ -55,6 +55,58 @@ class DependencyResolver
|
|||||||
return $resolved;
|
return $resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all dependencies of a specific package within a resolved package set.
|
||||||
|
* This is useful when you need to get build flags for a specific library and its deps.
|
||||||
|
*
|
||||||
|
* The method will only include dependencies that exist in the resolved set,
|
||||||
|
* which properly handles optional dependencies (suggests) - only those that
|
||||||
|
* were actually resolved will be included.
|
||||||
|
*
|
||||||
|
* @param string $package_name The package to get dependencies for
|
||||||
|
* @param string[] $resolved_packages The resolved package list (from resolve())
|
||||||
|
* @param bool $include_suggests Whether to include suggests that are in resolved set
|
||||||
|
* @return string[] Dependencies of the package (NOT including itself), ordered for building
|
||||||
|
*/
|
||||||
|
public static function getSubDependencies(string $package_name, array $resolved_packages, bool $include_suggests = false): array
|
||||||
|
{
|
||||||
|
// Create a lookup set for O(1) membership check
|
||||||
|
$resolved_set = array_flip($resolved_packages);
|
||||||
|
|
||||||
|
// Verify the target package is in the resolved set
|
||||||
|
if (!isset($resolved_set[$package_name])) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build dependency map from config
|
||||||
|
$dep_map = [];
|
||||||
|
foreach ($resolved_packages as $pkg) {
|
||||||
|
$dep_map[$pkg] = [
|
||||||
|
'depends' => PackageConfig::get($pkg, 'depends', []),
|
||||||
|
'suggests' => PackageConfig::get($pkg, 'suggests', []),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all sub-dependencies recursively (excluding the package itself)
|
||||||
|
$visited = [];
|
||||||
|
$sorted = [];
|
||||||
|
|
||||||
|
// Get dependencies to process for the target package
|
||||||
|
$deps = $dep_map[$package_name]['depends'] ?? [];
|
||||||
|
if ($include_suggests) {
|
||||||
|
$deps = array_merge($deps, $dep_map[$package_name]['suggests'] ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only visit dependencies that are in the resolved set
|
||||||
|
foreach ($deps as $dep) {
|
||||||
|
if (isset($resolved_set[$dep])) {
|
||||||
|
self::visitSubDeps($dep, $dep_map, $resolved_set, $include_suggests, $visited, $sorted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sorted;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a reverse dependency map for the resolved packages.
|
* Build a reverse dependency map for the resolved packages.
|
||||||
* For each package that is depended upon, list which packages depend on it.
|
* For each package that is depended upon, list which packages depend on it.
|
||||||
@@ -89,6 +141,39 @@ class DependencyResolver
|
|||||||
return $why;
|
return $why;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursive helper for getSubDependencies.
|
||||||
|
* Visits dependencies in topological order (dependencies first).
|
||||||
|
*/
|
||||||
|
private static function visitSubDeps(
|
||||||
|
string $pkg_name,
|
||||||
|
array $dep_map,
|
||||||
|
array $resolved_set,
|
||||||
|
bool $include_suggests,
|
||||||
|
array &$visited,
|
||||||
|
array &$sorted
|
||||||
|
): void {
|
||||||
|
if (isset($visited[$pkg_name])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$visited[$pkg_name] = true;
|
||||||
|
|
||||||
|
// Get dependencies to process
|
||||||
|
$deps = $dep_map[$pkg_name]['depends'] ?? [];
|
||||||
|
if ($include_suggests) {
|
||||||
|
$deps = array_merge($deps, $dep_map[$pkg_name]['suggests'] ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only visit dependencies that are in the resolved set
|
||||||
|
foreach ($deps as $dep) {
|
||||||
|
if (isset($resolved_set[$dep])) {
|
||||||
|
self::visitSubDeps($dep, $dep_map, $resolved_set, $include_suggests, $visited, $sorted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$sorted[] = $pkg_name;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visitor pattern implementation for dependency resolution.
|
* Visitor pattern implementation for dependency resolution.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -149,6 +149,117 @@ class SPCConfigUtil
|
|||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get build configuration for a package and its sub-dependencies within a resolved set.
|
||||||
|
*
|
||||||
|
* This is useful when you need to statically link something against a specific
|
||||||
|
* library and all its transitive dependencies. It properly handles optional
|
||||||
|
* dependencies by only including those that were actually resolved.
|
||||||
|
*
|
||||||
|
* @param string $package_name The package to get config for
|
||||||
|
* @param string[] $resolved_packages The full resolved package list
|
||||||
|
* @param bool $include_suggests Whether to include resolved suggests
|
||||||
|
* @return array{
|
||||||
|
* cflags: string,
|
||||||
|
* ldflags: string,
|
||||||
|
* libs: string
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
public function getPackageDepsConfig(string $package_name, array $resolved_packages, bool $include_suggests = false): array
|
||||||
|
{
|
||||||
|
// Get sub-dependencies within the resolved set
|
||||||
|
$sub_deps = DependencyResolver::getSubDependencies($package_name, $resolved_packages, $include_suggests);
|
||||||
|
|
||||||
|
if (empty($sub_deps)) {
|
||||||
|
return [
|
||||||
|
'cflags' => '',
|
||||||
|
'ldflags' => '',
|
||||||
|
'libs' => '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use libs_only_deps mode and no_php for library linking
|
||||||
|
$save_no_php = $this->no_php;
|
||||||
|
$save_libs_only_deps = $this->libs_only_deps;
|
||||||
|
$this->no_php = true;
|
||||||
|
$this->libs_only_deps = true;
|
||||||
|
|
||||||
|
$ret = $this->configWithResolvedPackages($sub_deps);
|
||||||
|
|
||||||
|
$this->no_php = $save_no_php;
|
||||||
|
$this->libs_only_deps = $save_libs_only_deps;
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get configuration using already-resolved packages (skip dependency resolution).
|
||||||
|
*
|
||||||
|
* @param string[] $resolved_packages Already resolved package names in build order
|
||||||
|
* @return array{
|
||||||
|
* cflags: string,
|
||||||
|
* ldflags: string,
|
||||||
|
* libs: string
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
public function configWithResolvedPackages(array $resolved_packages): array
|
||||||
|
{
|
||||||
|
$ldflags = $this->getLdflagsString();
|
||||||
|
$cflags = $this->getIncludesString($resolved_packages);
|
||||||
|
$libs = $this->getLibsString($resolved_packages, !$this->absolute_libs);
|
||||||
|
|
||||||
|
// additional OS-specific libraries (e.g. macOS -lresolv)
|
||||||
|
if ($extra_libs = SystemTarget::getRuntimeLibs()) {
|
||||||
|
$libs .= " {$extra_libs}";
|
||||||
|
}
|
||||||
|
|
||||||
|
$extra_env = getenv('SPC_EXTRA_LIBS');
|
||||||
|
if (is_string($extra_env) && !empty($extra_env)) {
|
||||||
|
$libs .= " {$extra_env}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// package frameworks
|
||||||
|
if (SystemTarget::getTargetOS() === 'Darwin') {
|
||||||
|
$libs .= " {$this->getFrameworksString($resolved_packages)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// C++
|
||||||
|
if ($this->hasCpp($resolved_packages)) {
|
||||||
|
$libcpp = SystemTarget::getTargetOS() === 'Darwin' ? '-lc++' : '-lstdc++';
|
||||||
|
$libs = str_replace($libcpp, '', $libs) . " {$libcpp}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->libs_only_deps) {
|
||||||
|
// mimalloc must come first
|
||||||
|
if (in_array('mimalloc', $resolved_packages) && file_exists(BUILD_LIB_PATH . '/libmimalloc.a')) {
|
||||||
|
$libs = BUILD_LIB_PATH . '/libmimalloc.a ' . str_replace([BUILD_LIB_PATH . '/libmimalloc.a', '-lmimalloc'], ['', ''], $libs);
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
'cflags' => clean_spaces(getenv('CFLAGS') . ' ' . $cflags),
|
||||||
|
'ldflags' => clean_spaces(getenv('LDFLAGS') . ' ' . $ldflags),
|
||||||
|
'libs' => clean_spaces(getenv('LIBS') . ' ' . $libs),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// embed
|
||||||
|
if (!$this->no_php) {
|
||||||
|
$libs = "-lphp {$libs} -lc";
|
||||||
|
}
|
||||||
|
|
||||||
|
$allLibs = getenv('LIBS') . ' ' . $libs;
|
||||||
|
|
||||||
|
// mimalloc must come first
|
||||||
|
if (in_array('mimalloc', $resolved_packages) && file_exists(BUILD_LIB_PATH . '/libmimalloc.a')) {
|
||||||
|
$allLibs = BUILD_LIB_PATH . '/libmimalloc.a ' . str_replace([BUILD_LIB_PATH . '/libmimalloc.a', '-lmimalloc'], ['', ''], $allLibs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'cflags' => clean_spaces(getenv('CFLAGS') . ' ' . $cflags),
|
||||||
|
'ldflags' => clean_spaces(getenv('LDFLAGS') . ' ' . $ldflags),
|
||||||
|
'libs' => clean_spaces($allLibs),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
private function hasCpp(array $packages): bool
|
private function hasCpp(array $packages): bool
|
||||||
{
|
{
|
||||||
foreach ($packages as $package) {
|
foreach ($packages as $package) {
|
||||||
|
|||||||
Reference in New Issue
Block a user