diff --git a/src/StaticPHP/Command/Dev/GenExtTestMatrixCommand.php b/src/StaticPHP/Command/Dev/GenExtTestMatrixCommand.php index 44816863..674c8075 100644 --- a/src/StaticPHP/Command/Dev/GenExtTestMatrixCommand.php +++ b/src/StaticPHP/Command/Dev/GenExtTestMatrixCommand.php @@ -107,7 +107,12 @@ class GenExtTestMatrixCommand extends BaseCommand // Separate into regular and virtual extensions (build-static:false excluded globally) $all_regular = []; $all_virtual = []; + $all_libraries = []; foreach ($all as $pkg_name => $config) { + if (($config['type'] ?? '') === 'library') { + $all_libraries[$pkg_name] = $config; + continue; + } if (($config['type'] ?? '') !== 'php-extension') { continue; } @@ -154,10 +159,7 @@ class GenExtTestMatrixCommand extends BaseCommand $raw, fn ($d) => isset($pool_set[$d]) && $d !== $pkg_name )); - $os_lib_deps[$this->displayName($pkg_name)] = array_values(array_filter( - $raw, - fn ($d) => !str_starts_with($d, 'ext-') - )); + $os_lib_deps[$this->displayName($pkg_name)] = $this->collectLibraryDeps($raw, $all_libraries, $os); } $all_ext_lib_deps[$os] = $os_lib_deps; @@ -300,6 +302,41 @@ class GenExtTestMatrixCommand extends BaseCommand return str_starts_with($pkg_name, 'ext-') ? substr($pkg_name, 4) : $pkg_name; } + /** + * Collect direct and transitive library dependencies from a package dependency list. + * + * @param string[] $deps + * @param array $library_configs + * @param array $seen + * + * @return string[] + */ + private function collectLibraryDeps(array $deps, array $library_configs, string $platform, array $seen = []): array + { + $collected = []; + foreach ($deps as $dep) { + if (str_starts_with($dep, 'ext-') || isset($seen[$dep])) { + continue; + } + + $seen[$dep] = true; + $collected[$dep] = $dep; + + if (!isset($library_configs[$dep])) { + continue; + } + + $child_deps = array_merge( + $this->resolvePlatformList($library_configs[$dep], 'depends', $platform), + $this->resolvePlatformList($library_configs[$dep], 'suggests', $platform), + ); + foreach ($this->collectLibraryDeps($child_deps, $library_configs, $platform, $seen) as $child_dep) { + $collected[$child_dep] = $child_dep; + } + } + return array_values($collected); + } + /** * Split orphans into batches such that no two conflicting extensions share a batch. * Uses a greedy graph-coloring approach. diff --git a/tests/StaticPHP/Command/Dev/GenExtTestMatrixCommandTest.php b/tests/StaticPHP/Command/Dev/GenExtTestMatrixCommandTest.php index b6beb6fb..4e3fc33a 100644 --- a/tests/StaticPHP/Command/Dev/GenExtTestMatrixCommandTest.php +++ b/tests/StaticPHP/Command/Dev/GenExtTestMatrixCommandTest.php @@ -176,6 +176,20 @@ class GenExtTestMatrixCommandTest extends TestCase } } + /** + * --for-libs must include extensions that depend on the library through other libraries. + */ + public function testForLibsFilterIncludesTransitiveLibraryDeps(): void + { + $matrix = $this->runMatrix(['--os' => 'Linux', '--for-libs' => 'libde265']); + + $this->assertNotEmpty($matrix, '--for-libs=libde265 must yield at least one entry'); + foreach ($matrix as $entry) { + $parts = explode(',', $entry['extension']); + $this->assertContains('imagick', $parts, "Entry {$entry['extension']} should not appear in --for-libs=libde265 results"); + } + } + /** * --tier2 must produce only Tier2 runners and no Windows entries. */ @@ -260,12 +274,14 @@ class GenExtTestMatrixCommandTest extends TestCase * - ext-redis simple orphan * - ext-xml depends on lib 'libxml2' * - ext-dom depends on ext-xml (DFS chain) + * - ext-imagick depends on imagemagick -> libheif -> libde265 * - ext-linux-only restricted to Linux via os: [Linux] */ private static function buildFixture(): array { // php-extension must be a non-empty assoc array ([] fails is_assoc_array() check). $ext = static fn (array $phpExt = ['arg-type' => 'standard'], array $topLevel = []): array => array_merge(['type' => 'php-extension', 'php-extension' => $phpExt], $topLevel); + $lib = static fn (array $topLevel = []): array => array_merge(['type' => 'library', 'artifact' => ['source' => 'custom']], $topLevel); return [ // Isolated standalones @@ -284,6 +300,12 @@ class GenExtTestMatrixCommandTest extends TestCase 'ext-xml' => $ext(['arg-type' => 'standard'], ['depends' => ['libxml2']]), 'ext-dom' => $ext(['arg-type' => 'standard'], ['depends' => ['ext-xml']]), + // Transitive library chain: imagick -> imagemagick -> libheif -> libde265 + 'ext-imagick' => $ext(['arg-type' => 'standard'], ['depends' => ['imagemagick']]), + 'imagemagick' => $lib(['depends' => ['libheif']]), + 'libheif' => $lib(['depends' => ['libde265']]), + 'libde265' => $lib(), + // OS-restricted to Linux only 'ext-linux-only' => $ext(['os' => ['Linux']]), ];