Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
1865762
Fix config yaml support
crazywhalecc Jan 22, 2026
ae74875
Fix config yaml support
crazywhalecc Jan 22, 2026
7b725bb
Add LicenseDumper component
crazywhalecc Jan 22, 2026
22fc703
Implement attr, brotli, bzip2 build for unix
crazywhalecc Jan 22, 2026
c27ed8b
Implement fastlz, zlib (unix)
crazywhalecc Jan 22, 2026
223dd10
fix spx shared libadd
henderkes Jan 24, 2026
a709221
Add skip helper function for calling events
crazywhalecc Jan 25, 2026
ccd948e
Add openssl lib support
crazywhalecc Jan 25, 2026
3a575f0
Use yml instead of yaml (sync with craft)
crazywhalecc Jan 25, 2026
51415fb
Use shorter summary json file name
crazywhalecc Jan 25, 2026
4531c9f
add option to allow linking musl dynamically on alpine
henderkes Jan 26, 2026
c5882c1
fix gettext v1.0 release
henderkes Jan 30, 2026
0cfa203
fix spx shared libadd (#1022)
henderkes Jan 30, 2026
7a262fe
Merge branch 'refs/heads/main' into v3-refactor/libs
crazywhalecc Feb 2, 2026
a414c65
Forward-port #1022 spc target env var
crazywhalecc Feb 2, 2026
f232588
Merge remote-tracking branch 'origin/v3-refactor/libs' into v3-refact…
crazywhalecc Feb 2, 2026
455d42d
Separate package config
crazywhalecc Feb 2, 2026
f4a29c4
Add dev:lint-config to replace sort-config command
crazywhalecc Feb 2, 2026
f437efe
Add dev:lint-config to replace sort-config command
crazywhalecc Feb 2, 2026
5a83412
Remove sort config command
crazywhalecc Feb 2, 2026
23db10d
Add libiconv,libssh2,libxml2,xz
crazywhalecc Feb 2, 2026
1586825
Add builder options for build:libs command
crazywhalecc Feb 2, 2026
82ab141
Add nghttp2, nghttp3, ngtcp2
crazywhalecc Feb 2, 2026
3d10236
Add PatchBeforeBuild attribute
crazywhalecc Feb 2, 2026
6ee8dc7
Add zstd,libcares
crazywhalecc Feb 2, 2026
19e11ca
Add ldap,libcares,libsodium,libunistring, lint all configs
crazywhalecc Feb 2, 2026
a6c79e3
Add dump license files after installing
crazywhalecc Feb 2, 2026
3492992
Add ncurses
crazywhalecc Feb 3, 2026
fddcdb8
Add filelist downloader debug message
crazywhalecc Feb 3, 2026
e732543
Fix wrong debug message show
crazywhalecc Feb 3, 2026
e4d6723
Add gettext
crazywhalecc Feb 3, 2026
2e8f6bb
Add idn2
crazywhalecc Feb 3, 2026
6688819
Add libedit
crazywhalecc Feb 3, 2026
a2409d9
Add getSourceRoot for artifacts
crazywhalecc Feb 3, 2026
09ddd2f
Add methods to retrieve package sub-dependencies and configuration
crazywhalecc Feb 3, 2026
c536fed
Add krb5 and lint configs
crazywhalecc Feb 3, 2026
103b5b3
Upgrade phpstan to v2
crazywhalecc Feb 3, 2026
7041e06
Add curl
crazywhalecc Feb 3, 2026
6fdbf62
Fix selective artifact installation detect
crazywhalecc Feb 3, 2026
38f7421
Use zig toolchain by default, lint files
crazywhalecc Feb 3, 2026
b89ff3c
Add com_dotnet extension (#1023)
crazywhalecc Feb 3, 2026
274098b
Merge remote-tracking branch 'origin/v3-refactor/libs' into v3-refact…
crazywhalecc Feb 3, 2026
0d4cde7
Add download options for build:libs command
crazywhalecc Feb 3, 2026
a02f287
Fix macOS wrong patch file directory
crazywhalecc Feb 3, 2026
b5c14d6
Fix golang download website hash match pattern
crazywhalecc Feb 4, 2026
c40eaee
Fix custom artifact binary download `is-installed` check
crazywhalecc Feb 4, 2026
08d2020
Allow all artifact configs
crazywhalecc Feb 4, 2026
e2011e1
Verbose message
crazywhalecc Feb 4, 2026
0afa1dd
Use new brand name
crazywhalecc Feb 4, 2026
e9c27de
Add go-xcaddy,musl-toolchain,php,upx, and also glfw linux support
crazywhalecc Feb 4, 2026
8f44b07
Merge remote-tracking branch 'origin/v3-refactor/libs' into v3-refact…
crazywhalecc Feb 4, 2026
0652d4a
Just in case source dir have not been created
crazywhalecc Feb 4, 2026
3fa2d69
Add ext-mbstring,ext-mbregex,onig
crazywhalecc Feb 4, 2026
16f9446
Add artifact name suggestions for download and install commands
crazywhalecc Feb 4, 2026
b9af9ba
Chore
crazywhalecc Feb 4, 2026
6bd3646
Merge branch 'main' into v3-refactor/libs
crazywhalecc Feb 5, 2026
4d4b1a3
Add ext-readline,freetype,gmssl,grpc,icu
crazywhalecc Feb 5, 2026
2a4959d
Chore
crazywhalecc Feb 5, 2026
9f2132c
Add pack lib command
crazywhalecc Feb 5, 2026
81ce777
phpstan fix
crazywhalecc Feb 5, 2026
0d32b7b
Refactor lib packing to v3 postinstall action
crazywhalecc Feb 5, 2026
b3bbe0a
Add libjpeg,libpng
crazywhalecc Feb 5, 2026
8fc2da9
Use OS release definition for openssl
crazywhalecc Feb 5, 2026
97634b0
Forward-port #1006 changes
crazywhalecc Feb 5, 2026
a75060e
Update exit code in ArtifactDownloader to reflect termination signal
crazywhalecc Feb 5, 2026
a072657
Update license file path for bzip2 in configuration
crazywhalecc Feb 5, 2026
807b90b
Fix incorrect variable name for working directory in submodule update…
crazywhalecc Feb 5, 2026
7ae16e5
Add imagemagick,jbig,lerc,libaom,libde265,libheif,libjxl,libtiff,libw…
crazywhalecc Feb 5, 2026
8f798c9
Add imap and BuildRootTracker
crazywhalecc Feb 6, 2026
1eec88f
Add reset command
crazywhalecc Feb 6, 2026
3cfab10
Add libacl
crazywhalecc Feb 6, 2026
39a2070
Add libargon2
crazywhalecc Feb 6, 2026
fba2676
Add lint-config command to check and sort configuration files
crazywhalecc Feb 6, 2026
d999bfc
Add libavif
crazywhalecc Feb 6, 2026
880bb87
Add libevent and postinstall action adder for library package
crazywhalecc Feb 6, 2026
a832cc2
Add libffi
crazywhalecc Feb 6, 2026
2723387
Add liblz4
crazywhalecc Feb 6, 2026
e9a411c
Add libmaxminddb
crazywhalecc Feb 6, 2026
bd11533
Add libmemcached
crazywhalecc Feb 6, 2026
f2d389d
Add librabbitmq
crazywhalecc Feb 6, 2026
4cfd8f4
Add librdkafka
crazywhalecc Feb 6, 2026
127697b
Add liburing
crazywhalecc Feb 6, 2026
fa1b71b
Add libuuid
crazywhalecc Feb 6, 2026
d6af728
Add libuv
crazywhalecc Feb 6, 2026
0c386e9
Allow shell completion for build:libs command
crazywhalecc Feb 6, 2026
017fabd
Add libxslt
crazywhalecc Feb 6, 2026
b42601d
Add libyaml
crazywhalecc Feb 6, 2026
2874336
Add mimalloc
crazywhalecc Feb 6, 2026
9912b21
Add net-snmp
crazywhalecc Feb 6, 2026
aad710e
Add postgresql
crazywhalecc Feb 6, 2026
67bea25
Add qdbm
crazywhalecc Feb 6, 2026
425010f
Add re2c
crazywhalecc Feb 6, 2026
6be4da2
Add readline
crazywhalecc Feb 6, 2026
fd40b92
Add snappy
crazywhalecc Feb 6, 2026
d163c3d
Add sqlite
crazywhalecc Feb 6, 2026
a5f8402
Add tidy
crazywhalecc Feb 6, 2026
b6d8bf5
Add unixodbc
crazywhalecc Feb 6, 2026
ca9dc25
Add watcher
crazywhalecc Feb 6, 2026
368461d
phpstan fix
crazywhalecc Feb 6, 2026
c72a2b6
Refactor nasm,php-sdk-binary-tools,strawberry-perl,vswhere
crazywhalecc Feb 6, 2026
d8d9f38
Refactor patching logic for Alpine Linux and macOS in attr.php
crazywhalecc Feb 6, 2026
95f34fb
Add extension amqp
crazywhalecc Feb 6, 2026
cf5a946
Add extension bcmath,openssl,zlib
crazywhalecc Feb 6, 2026
478b858
Chore
crazywhalecc Feb 6, 2026
5c7ab48
Support define php extension arg-type in config
crazywhalecc Feb 6, 2026
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
33 changes: 33 additions & 0 deletions config/artifact.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
attr:
source:
type: url
url: 'https://download.savannah.nongnu.org/releases/attr/attr-2.5.2.tar.gz'
source-mirror:
type: url
url: 'https://mirror.souseiseki.middlendian.com/nongnu/attr/attr-2.5.2.tar.gz'
metadata:
license-files: ['doc/COPYING.LGPL']
license: LGPL-2.1-or-later

brotli:
source:
type: ghtagtar
repo: google/brotli
match: 'v1\.\d.*'
binary: hosted # 等价于v2的provide-pre-built: true
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think "binary" is the best description here. Binary could also refer to the binaries the artifact produces, not necessarily the prebuilt library.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-built library also belongs to a kind of binary I think. And hosted is just an alias like this:

"binary": {
            "linux-x86_64": {
                "type": "ghrel",
                "repo": "static-php/static-php-cli-hosted",
                "match": "pkg-config-aarch64-linux-musl-1.2.5.txz",
                "extract": {
                    "bin/pkg-config": "{pkg_root_path}/bin/pkg-config"
                }
            },
            "linux-aarch64": {
                "type": "ghrel",
                "repo": "static-php/static-php-cli-hosted",
                "match": "pkg-config-x86_64-linux-musl-1.2.5.txz",
                "extract": {
                    "bin/pkg-config": "{pkg_root_path}/bin/pkg-config"
                }
            },
            "macos-x86_64": {
                "type": "ghrel",
                "repo": "static-php/static-php-cli-hosted",
                "match": "pkg-config-x86_64-darwin.txz",
                "extract": {
                    "bin/pkg-config": "{pkg_root_path}/bin/pkg-config"
                }
            },
            "macos-aarch64": {
                "type": "ghrel",
                "repo": "static-php/static-php-cli-hosted",
                "match": "pkg-config-aarch64-darwin.txz",
                "extract": "{pkg_root_path}"
            }
        }

My expected goal is:

That means that binary is a pre-built product that only needs to be installed, including the pre-built itself. If the hosted alias is expanded, it might look similar to the binary in pkg-config above.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get it, but going by conventions it's confusing. pre-built is better for this.

Copy link
Copy Markdown
Owner Author

@crazywhalecc crazywhalecc Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's true that pre-built is more appropriate than hosted. My main point before was to show that the pre-built version is built by our hosting actions. I will try to change this name later.

Copy link
Copy Markdown
Collaborator

@henderkes henderkes Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think hosted is fine, it's "binary" that's confusing here.

"pre-built": {
    "linux-x86_64": {
        "type": "ghrel",
        "repo": "static-php/static-php-cli-hosted",
        "match": "pkg-config-aarch64-linux-musl-1.2.5.txz",
        "extract": {
            "bin/pkg-config": "{pkg_root_path}/bin/pkg-config"
        }
    },
    "linux-aarch64": {
        "type": "ghrel",
        "repo": "static-php/static-php-cli-hosted",
        "match": "pkg-config-x86_64-linux-musl-1.2.5.txz",
        "extract": {
            "bin/pkg-config": "{pkg_root_path}/bin/pkg-config"
        }
    },
    "macos-x86_64": {
        "type": "ghrel",
        "repo": "static-php/static-php-cli-hosted",
        "match": "pkg-config-x86_64-darwin.txz",
        "extract": {
            "bin/pkg-config": "{pkg_root_path}/bin/pkg-config"
        }
    },
    "macos-aarch64": {
        "type": "ghrel",
        "repo": "static-php/static-php-cli-hosted",
        "match": "pkg-config-aarch64-darwin.txz",
                "extract": "{pkg_root_path}"
    }
}

This would be fine.

Copy link
Copy Markdown
Owner Author

@crazywhalecc crazywhalecc Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My perspective is that since we've merged pre-built and pkg in v2 to artifact.binary in v3, or rather, the concept of v3 has been almost completely redefined, some of the original binary packages here are not part of the pre-built, or were not built by us. For example, nasm, go-xcaddy, etc.

I can't think of any other names besides binary that can represent "pre-built", "closed source software", and "other non-source artifacts" at the same time. Their functions and behaviors are the same.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or were not built by us. For example, nasm, go-xcaddy, etc.

But they are nevertheless pre-built.

Copy link
Copy Markdown
Owner Author

@crazywhalecc crazywhalecc Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But they are nevertheless pre-built.

I agree that closed-source software and third-party binaries are also "pre-built". But I prefer using the "what is it" instead of "how is it built" here.

metadata:
license-files: ['LICENSE']
license: MIT

bzip2:
source:
type: url
url: 'https://dl.static-php.dev/static-php-cli/deps/bzip2/bzip2-1.0.8.tar.gz'
source-mirror:
type: filelist
url: 'https://sourceware.org/pub/bzip2/'
regex: '/href="(?<file>bzip2-(?<version>[^"]+)\.tar\.gz)"/'
binary: hosted
metadata:
license-files: ['{registry_root}/src/globals/licenses/bzip2.txt']
license: bzip2-1.0.6
21 changes: 21 additions & 0 deletions config/pkg.lib.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
attr:
type: library
static-libs@unix:
- libattr.a
artifact: attr
brotli:
type: library
pkg-configs:
- libbrotlicommon
- libbrotlidec
- libbrotlienc
headers:
- brotli
artifact: brotli
bzip2:
type: library
static-libs@unix:
- libbz2.a
headers:
- bzlib.h
artifact: bzip2
4 changes: 2 additions & 2 deletions spc.registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
},
"config": [
"config/pkg.ext.json",
"config/pkg.lib.json",
"config/pkg.lib.yaml",
"config/pkg.target.json"
]
},
"artifact": {
"config": [
"config/artifact.json"
"config/artifact.yaml"
],
"psr-4": {
"Package\\Artifact": "src/Package/Artifact"
Expand Down
23 changes: 23 additions & 0 deletions src/Package/Artifact/attr.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Package\Artifact;

use StaticPHP\Artifact\Artifact;
use StaticPHP\Attribute\Artifact\AfterSourceExtract;
use StaticPHP\Attribute\PatchDescription;
use StaticPHP\Util\SourcePatcher;
use StaticPHP\Util\System\LinuxUtil;

class attr
{
#[AfterSourceExtract('attr')]
#[PatchDescription('Patch attr for Alpine Linux (musl) and macOS - gethostname declaration')]
public function patchAttrForAlpine(Artifact $artifact): void
{
if (PHP_OS_FAMILY === 'Darwin' || PHP_OS_FAMILY === 'Linux' && LinuxUtil::isMuslDist()) {
SourcePatcher::patchFile('attr_alpine_gethostname.patch', $artifact->getSourceDir());
}
}
}
24 changes: 24 additions & 0 deletions src/Package/Artifact/bzip2.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Package\Artifact;

use StaticPHP\Artifact\Artifact;
use StaticPHP\Attribute\Artifact\AfterSourceExtract;
use StaticPHP\Attribute\PatchDescription;
use StaticPHP\Util\FileSystem;

class bzip2
{
#[AfterSourceExtract('bzip2')]
#[PatchDescription('Patch bzip2 Makefile to add -fPIC flag for position-independent code')]
public function patchBzip2Makefile(Artifact $artifact): void
{
FileSystem::replaceFileStr(
$artifact->getSourceDir() . '/Makefile',
'CFLAGS=-Wall',
'CFLAGS=-fPIC -Wall'
);
}
}
29 changes: 29 additions & 0 deletions src/Package/Library/attr.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace Package\Library;

use StaticPHP\Attribute\Package\BuildFor;
use StaticPHP\Attribute\Package\Library;
use StaticPHP\Package\LibraryPackage;
use StaticPHP\Runtime\Executor\UnixAutoconfExecutor;

#[Library('attr')]
class attr
{
#[BuildFor('Linux')]
#[BuildFor('Darwin')]
public function build(LibraryPackage $lib): void
{
UnixAutoconfExecutor::create($lib)
->appendEnv([
'CFLAGS' => '-Wno-int-conversion -Wno-implicit-function-declaration',
])
->exec('libtoolize --force --copy')
->exec('./autogen.sh || autoreconf -if')
->configure('--disable-nls')
->make('install-attributes_h install-data install-libattr_h install-libLTLIBRARIES install-pkgincludeHEADERS install-pkgconfDATA', with_install: false);
$lib->patchPkgconfPrefix(['libattr.pc'], PKGCONF_PATCH_PREFIX);
}
}
55 changes: 55 additions & 0 deletions src/Package/Library/brotli.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace Package\Library;

use StaticPHP\Attribute\Package\BuildFor;
use StaticPHP\Attribute\Package\Library;
use StaticPHP\Package\LibraryPackage;
use StaticPHP\Runtime\Executor\UnixCMakeExecutor;
use StaticPHP\Util\FileSystem;

#[Library('brotli')]
class brotli
{
#[BuildFor('Linux')]
#[BuildFor('Darwin')]
public function build(LibraryPackage $lib): void
{
UnixCMakeExecutor::create($lib)
->setBuildDir($lib->getSourceDir() . '/build-dir')
->addConfigureArgs("-DSHARE_INSTALL_PREFIX={$lib->getBuildRootPath()}")
->build();

// Patch pkg-config files
$lib->patchPkgconfPrefix(['libbrotlicommon.pc', 'libbrotlidec.pc', 'libbrotlienc.pc'], PKGCONF_PATCH_PREFIX);

// Add -lbrotlicommon to libbrotlidec.pc and libbrotlienc.pc
FileSystem::replaceFileLineContainsString(
$lib->getLibDir() . '/pkgconfig/libbrotlidec.pc',
'Libs: -L${libdir} -lbrotlidec',
'Libs: -L${libdir} -lbrotlidec -lbrotlicommon'
);
FileSystem::replaceFileLineContainsString(
$lib->getLibDir() . '/pkgconfig/libbrotlienc.pc',
'Libs: -L${libdir} -lbrotlienc',
'Libs: -L${libdir} -lbrotlienc -lbrotlicommon'
);

// Create symlink: libbrotli.a -> libbrotlicommon.a
shell()->cd($lib->getLibDir())->exec('ln -sf libbrotlicommon.a libbrotli.a');

// Remove dynamic libraries
foreach (FileSystem::scanDirFiles($lib->getLibDir(), false, true) as $filename) {
if (str_starts_with($filename, 'libbrotli') && (str_contains($filename, '.so') || str_ends_with($filename, '.dylib'))) {
unlink($lib->getLibDir() . '/' . $filename);
}
}

// Remove brotli binary if exists
if (file_exists($lib->getBinDir() . '/brotli')) {
unlink($lib->getBinDir() . '/brotli');
}
}
}
25 changes: 25 additions & 0 deletions src/Package/Library/bzip2.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Package\Library;

use StaticPHP\Attribute\Package\BuildFor;
use StaticPHP\Attribute\Package\Library;
use StaticPHP\Package\LibraryPackage;
use StaticPHP\Package\PackageBuilder;

#[Library('bzip2')]
class bzip2
{
#[BuildFor('Linux')]
#[BuildFor('Darwin')]
public function build(LibraryPackage $lib, PackageBuilder $builder): void
{
shell()->cd($lib->getSourceDir())->initializeEnv($lib)
->exec("make PREFIX='{$lib->getBuildRootPath()}' clean")
->exec("make -j{$builder->concurrency} PREFIX='{$lib->getBuildRootPath()}' libbz2.a")
->exec('cp libbz2.a ' . $lib->getLibDir())
->exec('cp bzlib.h ' . $lib->getIncludeDir());
}
}
147 changes: 147 additions & 0 deletions src/StaticPHP/Command/DumpLicenseCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<?php

declare(strict_types=1);

namespace StaticPHP\Command;

use StaticPHP\Registry\PackageLoader;
use StaticPHP\Util\DependencyResolver;
use StaticPHP\Util\InteractiveTerm;
use StaticPHP\Util\LicenseDumper;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;

#[AsCommand('dump-license', 'Dump licenses for artifacts')]
class DumpLicenseCommand extends BaseCommand
{
public function configure(): void
{
$this->addArgument('artifacts', InputArgument::OPTIONAL, 'Specific artifacts to dump licenses, comma separated, e.g "php-src,openssl,curl"');

// v2 compatible options
$this->addOption('for-extensions', 'e', InputOption::VALUE_REQUIRED, 'Dump by extensions (automatically includes php-src), e.g "openssl,mbstring"');
$this->addOption('for-libs', 'l', InputOption::VALUE_REQUIRED, 'Dump by libraries, e.g "openssl,zlib,curl"');

// v3 options
$this->addOption('for-packages', 'p', InputOption::VALUE_REQUIRED, 'Dump by packages, e.g "php,libssl,libcurl"');
$this->addOption('dump-dir', 'd', InputOption::VALUE_REQUIRED, 'Target directory for dumped licenses', BUILD_ROOT_PATH . '/license');
$this->addOption('without-suggests', null, null, 'Do not include suggested packages when using --for-extensions or --for-packages');
}

public function handle(): int
{
$dumper = new LicenseDumper();
$dump_dir = $this->getOption('dump-dir');
$artifacts_to_dump = [];

// Handle direct artifact argument
if ($artifacts = $this->getArgument('artifacts')) {
$artifacts_to_dump = array_merge($artifacts_to_dump, parse_comma_list($artifacts));
}

// Handle --for-extensions option
if ($exts = $this->getOption('for-extensions')) {
$artifacts_to_dump = array_merge(
$artifacts_to_dump,
$this->resolveFromExtensions(parse_extension_list($exts))
);
}

// Handle --for-libs option (v2 compat)
if ($libs = $this->getOption('for-libs')) {
$artifacts_to_dump = array_merge(
$artifacts_to_dump,
$this->resolveFromPackages(parse_comma_list($libs))
);
}

// Handle --for-packages option
if ($packages = $this->getOption('for-packages')) {
$artifacts_to_dump = array_merge(
$artifacts_to_dump,
$this->resolveFromPackages(parse_comma_list($packages))
);
}

// Check if any artifacts to dump
if (empty($artifacts_to_dump)) {
$this->output->writeln('<error>No artifacts specified. Use one of:</error>');
$this->output->writeln(' - Direct argument: <info>dump-license php-src,openssl,curl</info>');
$this->output->writeln(' - --for-extensions: <info>dump-license --for-extensions=openssl,mbstring</info>');
$this->output->writeln(' - --for-libs: <info>dump-license --for-libs=openssl,zlib</info>');
$this->output->writeln(' - --for-packages: <info>dump-license --for-packages=php,libssl</info>');
return self::FAILURE;
}

// Deduplicate artifacts
$artifacts_to_dump = array_values(array_unique($artifacts_to_dump));

logger()->info('Dumping licenses for ' . count($artifacts_to_dump) . ' artifact(s)');
logger()->debug('Artifacts: ' . implode(', ', $artifacts_to_dump));

// Add artifacts to dumper
$dumper->addArtifacts($artifacts_to_dump);

// Dump
$success = $dumper->dump($dump_dir);

if ($success) {
InteractiveTerm::success('Licenses dumped successfully: ' . $dump_dir);
// $this->output->writeln("<info>✓ Successfully dumped licenses to: {$dump_dir}</info>");
// $this->output->writeln("<comment> Total artifacts: " . count($artifacts_to_dump) . '</comment>');
return self::SUCCESS;
}

$this->output->writeln('<error>Failed to dump licenses</error>');
return self::FAILURE;
}

/**
* Resolve artifacts from extension names.
*
* @param array<string> $extensions Extension names
* @return array<string> Artifact names
*/
private function resolveFromExtensions(array $extensions): array
{
// Convert extension names to package names
$packages = array_map(fn ($ext) => "ext-{$ext}", $extensions);

// Automatically include php-related artifacts
array_unshift($packages, 'php');
array_unshift($packages, 'php-micro');
array_unshift($packages, 'php-embed');
array_unshift($packages, 'php-fpm');

return $this->resolveFromPackages($packages);
}

/**
* Resolve artifacts from package names.
*
* @param array<string> $packages Package names
* @return array<string> Artifact names
*/
private function resolveFromPackages(array $packages): array
{
$artifacts = [];
$include_suggests = !$this->getOption('without-suggests');

// Resolve package dependencies
$resolved_packages = DependencyResolver::resolve($packages, [], $include_suggests);

foreach ($resolved_packages as $pkg_name) {
try {
$pkg = PackageLoader::getPackage($pkg_name);
if ($artifact = $pkg->getArtifact()) {
$artifacts[] = $artifact->getName();
}
} catch (\Throwable $e) {
logger()->debug("Package {$pkg_name} has no artifact or failed to load: {$e->getMessage()}");
}
}

return array_unique($artifacts);
}
}
Loading