Add package outputs, colorize motd

This commit is contained in:
crazywhalecc 2025-12-09 16:34:43 +08:00
parent ac01867e9c
commit b0f630f95f
No known key found for this signature in database
GPG Key ID: 1F4BDD59391F2680
8 changed files with 98 additions and 26 deletions

View File

@ -45,7 +45,7 @@ use ZM\Logger\ConsoleColor;
#[Target('php-cgi')]
#[Target('php-embed')]
#[Target('frankenphp')]
class php
class php extends TargetPackage
{
public static function getPHPVersionID(): int
{
@ -350,6 +350,9 @@ class php
shell()->cd($package->getSourceDir())
->setEnv($this->makeVars($installer))
->exec("make -j{$concurrency} cli");
$builder->deployBinary("{$package->getSourceDir()}/sapi/cli/php", BUILD_BIN_PATH . '/php');
$package->setOutput('Binary path for cli SAPI', BUILD_BIN_PATH . '/php');
}
#[Stage]
@ -360,6 +363,9 @@ class php
shell()->cd($package->getSourceDir())
->setEnv($this->makeVars($installer))
->exec("make -j{$concurrency} cgi");
$builder->deployBinary("{$package->getSourceDir()}/sapi/cgi/php-cgi", BUILD_BIN_PATH . '/php-cgi');
$package->setOutput('Binary path for cgi SAPI', BUILD_BIN_PATH . '/php-cgi');
}
#[Stage]
@ -370,6 +376,9 @@ class php
shell()->cd($package->getSourceDir())
->setEnv($this->makeVars($installer))
->exec("make -j{$concurrency} fpm");
$builder->deployBinary("{$package->getSourceDir()}/sapi/fpm/php-fpm", BUILD_BIN_PATH . '/php-fpm');
$package->setOutput('Binary path for fpm SAPI', BUILD_BIN_PATH . '/php-fpm');
}
#[Stage]
@ -392,6 +401,7 @@ class php
->exec("make -j{$builder->concurrency} micro");
$builder->deployBinary($package->getSourceDir() . '/sapi/micro/micro.sfx', BUILD_BIN_PATH . '/micro.sfx');
$package->setOutput('Binary path for micro SAPI', BUILD_BIN_PATH . '/micro.sfx');
} finally {
if ($phar_patched) {
SourcePatcher::unpatchMicroPhar();
@ -432,12 +442,17 @@ class php
}
// deploy
$builder->deployBinary($libphp_so, $libphp_so, false);
$package->setOutput('Library path for embed SAPI', $libphp_so);
}
// process shared extensions that built-with-php
$increment_files = $diff->getChangedFiles();
foreach ($increment_files as $increment_file) {
$builder->deployBinary($increment_file, $increment_file, false);
$files[] = basename($increment_file);
}
if (!empty($files)) {
$package->setOutput('Built shared extensions', implode(', ', $files));
}
// ------------- SPC_CMD_VAR_PHP_EMBED_TYPE=static -------------
@ -524,6 +539,7 @@ class php
logger()->debug('Patching phpize prefix');
FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', "prefix=''", "prefix='" . BUILD_ROOT_PATH . "'");
FileSystem::replaceFileStr(BUILD_BIN_PATH . '/phpize', 's##', 's#/usr/local#');
$this->setOutput('phpize script path for embed SAPI', BUILD_BIN_PATH . '/phpize');
}
// patch php-config
if (file_exists(BUILD_BIN_PATH . '/php-config')) {
@ -535,6 +551,7 @@ class php
// move lstdc++ to the end of libs
$php_config_str = preg_replace('/(libs=")(.*?)\s*(-lstdc\+\+)\s*(.*?)"/', '$1$2 $4 $3"', $php_config_str);
FileSystem::writeFile(BUILD_BIN_PATH . '/php-config', $php_config_str);
$this->setOutput('php-config script path for embed SAPI', BUILD_BIN_PATH . '/php-config');
}
}

View File

@ -1,11 +0,0 @@
<?php
declare(strict_types=1);
namespace StaticPHP\Attribute\Package;
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
readonly class PatchBeforeBuild
{
public function __construct() {}
}

View File

@ -10,6 +10,7 @@ use StaticPHP\Exception\SPCException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use ZM\Logger\ConsoleColor;
abstract class BaseCommand extends Command
{
@ -21,7 +22,7 @@ abstract class BaseCommand extends Command
/ ___|| |_ __ _| |_(_) ___| _ \| | | | _ \
\___ \| __/ _` | __| |/ __| |_) | |_| | |_) |
___) | || (_| | |_| | (__| __/| _ | __/
|____/ \__\__,_|\__|_|\___|_| |_| |_|_| v{version}
|____/ \__\__,_|\__|_|\___|_| |_| |_|_| {version}
';
@ -69,7 +70,7 @@ abstract class BaseCommand extends Command
});
$version = $this->getVersionWithCommit();
if (!$this->no_motd) {
echo str_replace('{version}', $version, self::$motd);
echo str_replace('{version}', '' . ConsoleColor::none("v{$version}"), '' . ConsoleColor::magenta(self::$motd));
}
}

View File

@ -51,6 +51,8 @@ class BuildTargetCommand extends BaseCommand
$this->output->writeln("<info>✔ BUILD SUCCESSFUL ({$usedtime} s)</info>");
$this->output->writeln("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
$installer->printBuildPackageOutputs();
return static::SUCCESS;
}
}

View File

@ -26,6 +26,10 @@ class CallbackInvoker
* 4. Default value
* 5. Null (if nullable)
*
* Note: For object values in context, the invoker automatically registers
* the object under all its parent classes and interfaces, allowing type hints
* to match any type in the inheritance hierarchy.
*
* @param callable $callback The callback to invoke
* @param array $context Context parameters (type => value or name => value)
*
@ -35,6 +39,9 @@ class CallbackInvoker
*/
public function invoke(callable $callback, array $context = []): mixed
{
// Expand context to include all parent classes and interfaces for objects
$context = $this->expandContextHierarchy($context);
$reflection = new \ReflectionFunction(\Closure::fromCallable($callback));
$args = [];
@ -95,4 +102,43 @@ class CallbackInvoker
'void', 'null', 'false', 'true', 'never',
], true);
}
/**
* Expand context to include all parent classes and interfaces for object values.
* This allows type hints to match any type in the object's inheritance hierarchy.
*
* @param array $context Original context array
* @return array Expanded context with all class hierarchy mappings
*/
private function expandContextHierarchy(array $context): array
{
$expanded = [];
foreach ($context as $key => $value) {
// Keep the original key-value pair
$expanded[$key] = $value;
// If value is an object, add mappings for all parent classes and interfaces
if (is_object($value)) {
$reflection = new \ReflectionClass($value);
// Add concrete class
$expanded[$reflection->getName()] = $value;
// Add all parent classes
while ($parent = $reflection->getParentClass()) {
$expanded[$parent->getName()] = $value;
$reflection = $parent;
}
// Add all interfaces
$interfaces = (new \ReflectionClass($value))->getInterfaceNames();
foreach ($interfaces as $interface) {
$expanded[$interface] = $value;
}
}
}
return $expanded;
}
}

View File

@ -23,6 +23,9 @@ abstract class Package
/** @var array<string, callable> $build_functions Build functions for different OS binding */
protected array $build_functions = [];
/** @var array<string, string> */
protected array $outputs = [];
/**
* @param string $name Name of the package
* @param string $type Type of the package
@ -69,6 +72,17 @@ abstract class Package
return $ret;
}
public function setOutput(string $key, string $value): static
{
$this->outputs[$key] = $value;
return $this;
}
public function getOutputs(): array
{
return $this->outputs;
}
/**
* Add a build function for a specific platform.
*

View File

@ -104,6 +104,16 @@ class PackageInstaller
return $this;
}
public function printBuildPackageOutputs(): void
{
foreach ($this->build_packages as $package) {
if (($outputs = $package->getOutputs()) !== []) {
InteractiveTerm::notice('Package ' . ConsoleColor::green($package->getName()) . ' outputs');
$this->printArrayInfo(info: $outputs);
}
}
}
/**
* Run the package installation process.
*/

View File

@ -12,7 +12,6 @@ use StaticPHP\Attribute\Package\Extension;
use StaticPHP\Attribute\Package\Info;
use StaticPHP\Attribute\Package\InitPackage;
use StaticPHP\Attribute\Package\Library;
use StaticPHP\Attribute\Package\PatchBeforeBuild;
use StaticPHP\Attribute\Package\ResolveBuild;
use StaticPHP\Attribute\Package\Stage;
use StaticPHP\Attribute\Package\Target;
@ -166,16 +165,14 @@ class PackageLoader
if ($refClass->getParentClass() !== false) {
if (is_a($class_name, Package::class, true)) {
self::$packages[$attribute_instance->name] = new $class_name($attribute_instance->name, $package_type);
$instance_class = self::$packages[$attribute_instance->name];
}
}
if (!isset($instance_class)) {
$instance_class = $refClass->newInstance();
}
$pkg = self::$packages[$attribute_instance->name];
// Use the package instance if it's a Package subclass, otherwise create a new instance
$instance_class = is_a($class_name, Package::class, true) ? $pkg : $refClass->newInstance();
// validate package type matches
$pkg_type_attr = match ($attribute->getName()) {
Target::class => ['target', 'virtual-target'],
@ -204,18 +201,13 @@ class PackageLoader
// #[Stage('stage_name')]
Stage::class => self::addStage($method, $pkg, $instance_class, $method_instance),
// #[InitPackage] (run now with package context)
InitPackage::class => ApplicationContext::invoke([$instance_class, $method->getName()], [
Package::class => $pkg,
$pkg::class => $pkg,
]),
InitPackage::class => ApplicationContext::invoke([$instance_class, $method->getName()], ['package' => $pkg]),
// #[InitBuild]
ResolveBuild::class => $pkg instanceof TargetPackage ? $pkg->setResolveBuildCallback([$instance_class, $method->getName()]) : null,
// #[Info]
Info::class => $pkg->setInfoCallback([$instance_class, $method->getName()]),
// #[Validate]
Validate::class => $pkg->setValidateCallback([$instance_class, $method->getName()]),
// #[PatchBeforeBuild]
PatchBeforeBuild::class => $pkg->setPatchBeforeBuildCallback([$instance_class, $method->getName()]),
default => null,
};
}
@ -224,6 +216,7 @@ class PackageLoader
self::$packages[$pkg->getName()] = $pkg;
}
// For classes without package attributes, create a simple instance for non-package stage callbacks
if (!isset($instance_class)) {
$instance_class = $refClass->newInstance();
}