Refactor all (except command) modules using new exceptions

This commit is contained in:
crazywhalecc 2025-08-06 20:43:23 +08:00 committed by Jerry Ma
parent 722bb31815
commit f68f060be2
47 changed files with 335 additions and 291 deletions

View File

@ -4,8 +4,9 @@ declare(strict_types=1);
namespace SPC\builder; namespace SPC\builder;
use SPC\exception\FileSystemException; use SPC\exception\EnvironmentException;
use SPC\exception\RuntimeException; use SPC\exception\SPCException;
use SPC\exception\ValidationException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
@ -30,10 +31,10 @@ class Extension
$unix_only = Config::getExt($this->name, 'unix-only', false); $unix_only = Config::getExt($this->name, 'unix-only', false);
$windows_only = Config::getExt($this->name, 'windows-only', false); $windows_only = Config::getExt($this->name, 'windows-only', false);
if (PHP_OS_FAMILY !== 'Windows' && $windows_only) { if (PHP_OS_FAMILY !== 'Windows' && $windows_only) {
throw new RuntimeException("{$ext_type} extension {$name} is not supported on Linux and macOS platform"); throw new EnvironmentException("{$ext_type} extension {$name} is not supported on Linux and macOS platform");
} }
if (PHP_OS_FAMILY === 'Windows' && $unix_only) { if (PHP_OS_FAMILY === 'Windows' && $unix_only) {
throw new RuntimeException("{$ext_type} extension {$name} is not supported on Windows platform"); throw new EnvironmentException("{$ext_type} extension {$name} is not supported on Windows platform");
} }
// set source_dir for builtin // set source_dir for builtin
if ($ext_type === 'builtin') { if ($ext_type === 'builtin') {
@ -41,7 +42,7 @@ class Extension
} elseif ($ext_type === 'external') { } elseif ($ext_type === 'external') {
$source = Config::getExt($this->name, 'source'); $source = Config::getExt($this->name, 'source');
if ($source === null) { if ($source === null) {
throw new RuntimeException("{$ext_type} extension {$name} source not found"); throw new ValidationException("{$ext_type} extension {$name} source not found", validation_module: "Extension [{$name}] loader");
} }
$source_path = Config::getSource($source)['path'] ?? null; $source_path = Config::getSource($source)['path'] ?? null;
$source_path = $source_path === null ? SOURCE_PATH . '/' . $source : SOURCE_PATH . '/' . $source_path; $source_path = $source_path === null ? SOURCE_PATH . '/' . $source : SOURCE_PATH . '/' . $source_path;
@ -287,13 +288,12 @@ class Extension
{ {
// Run compile check if build target is cli // Run compile check if build target is cli
// If you need to run some check, overwrite this or add your assert in src/globals/ext-tests/{extension_name}.php // If you need to run some check, overwrite this or add your assert in src/globals/ext-tests/{extension_name}.php
// If check failed, throw RuntimeException
$sharedExtensions = $this->getSharedExtensionLoadString(); $sharedExtensions = $this->getSharedExtensionLoadString();
[$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"'); [$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"');
if ($ret !== 0) { if ($ret !== 0) {
throw new RuntimeException( throw new ValidationException(
'extension ' . $this->getName() . ' failed runtime check: php-cli returned ' . $ret . "\n" . "extension {$this->getName()} failed compile check: php-cli returned {$ret}",
join("\n", $out) validation_module: 'Extension ' . $this->getName() . ' sanity check'
); );
} }
@ -307,8 +307,10 @@ class Extension
[$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' -r "' . trim($test) . '"'); [$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' -r "' . trim($test) . '"');
if ($ret !== 0) { if ($ret !== 0) {
var_dump($out); throw new ValidationException(
throw new RuntimeException('extension ' . $this->getName() . ' failed sanity check'); "extension {$this->getName()} failed sanity check. Code: {$ret}, output: " . implode("\n", $out),
validation_module: 'Extension ' . $this->getName() . ' function check'
);
} }
} }
} }
@ -317,10 +319,9 @@ class Extension
{ {
// Run compile check if build target is cli // Run compile check if build target is cli
// If you need to run some check, overwrite this or add your assert in src/globals/ext-tests/{extension_name}.php // If you need to run some check, overwrite this or add your assert in src/globals/ext-tests/{extension_name}.php
// If check failed, throw RuntimeException
[$ret] = cmd()->execWithResult(BUILD_ROOT_PATH . '/bin/php.exe -n --ri "' . $this->getDistName() . '"', false); [$ret] = cmd()->execWithResult(BUILD_ROOT_PATH . '/bin/php.exe -n --ri "' . $this->getDistName() . '"', false);
if ($ret !== 0) { if ($ret !== 0) {
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: php-cli returned ' . $ret); throw new ValidationException("extension {$this->getName()} failed compile check: php-cli returned {$ret}", validation_module: "Extension {$this->getName()} sanity check");
} }
if (file_exists(FileSystem::convertPath(ROOT_DIR . '/src/globals/ext-tests/' . $this->getName() . '.php'))) { if (file_exists(FileSystem::convertPath(ROOT_DIR . '/src/globals/ext-tests/' . $this->getName() . '.php'))) {
@ -333,7 +334,10 @@ class Extension
[$ret] = cmd()->execWithResult(BUILD_ROOT_PATH . '/bin/php.exe -n -r "' . trim($test) . '"'); [$ret] = cmd()->execWithResult(BUILD_ROOT_PATH . '/bin/php.exe -n -r "' . trim($test) . '"');
if ($ret !== 0) { if ($ret !== 0) {
throw new RuntimeException('extension ' . $this->getName() . ' failed sanity check'); throw new ValidationException(
"extension {$this->getName()} failed function check",
validation_module: "Extension {$this->getName()} function check"
);
} }
} }
} }
@ -348,34 +352,39 @@ class Extension
*/ */
public function buildShared(): void public function buildShared(): void
{ {
if (Config::getExt($this->getName(), 'type') === 'builtin' || Config::getExt($this->getName(), 'build-with-php') === true) { try {
if (Config::getExt($this->getName(), 'type') === 'builtin' || Config::getExt($this->getName(), 'build-with-php') === true) {
if (file_exists(BUILD_MODULES_PATH . '/' . $this->getName() . '.so')) {
logger()->info('Shared extension [' . $this->getName() . '] was already built by php-src/configure (' . $this->getName() . '.so)');
return;
}
if (Config::getExt($this->getName(), 'build-with-php') === true) {
logger()->warning('Shared extension [' . $this->getName() . '] did not build with php-src/configure (' . $this->getName() . '.so)');
logger()->warning('Try deleting your build and source folders and running `spc build`` again.');
return;
}
}
if (file_exists(BUILD_MODULES_PATH . '/' . $this->getName() . '.so')) { if (file_exists(BUILD_MODULES_PATH . '/' . $this->getName() . '.so')) {
logger()->info('Shared extension [' . $this->getName() . '] was already built by php-src/configure (' . $this->getName() . '.so)'); logger()->info('Shared extension [' . $this->getName() . '] was already built, skipping (' . $this->getName() . '.so)');
return;
} }
if (Config::getExt($this->getName(), 'build-with-php') === true) { logger()->info('Building extension [' . $this->getName() . '] as shared extension (' . $this->getName() . '.so)');
logger()->warning('Shared extension [' . $this->getName() . '] did not build with php-src/configure (' . $this->getName() . '.so)'); foreach ($this->dependencies as $dependency) {
logger()->warning('Try deleting your build and source folders and running `spc build`` again.'); if (!$dependency instanceof Extension) {
return; continue;
}
if (!$dependency->isBuildStatic()) {
logger()->info('extension ' . $this->getName() . ' requires extension ' . $dependency->getName());
$dependency->buildShared();
}
} }
match (PHP_OS_FAMILY) {
'Darwin', 'Linux' => $this->buildUnixShared(),
default => throw new WrongUsageException(PHP_OS_FAMILY . ' build shared extensions is not supported yet'),
};
} catch (SPCException $e) {
$e->bindExtensionInfo(['extension_name' => $this->getName()]);
throw $e;
} }
if (file_exists(BUILD_MODULES_PATH . '/' . $this->getName() . '.so')) {
logger()->info('Shared extension [' . $this->getName() . '] was already built, skipping (' . $this->getName() . '.so)');
}
logger()->info('Building extension [' . $this->getName() . '] as shared extension (' . $this->getName() . '.so)');
foreach ($this->dependencies as $dependency) {
if (!$dependency instanceof Extension) {
continue;
}
if (!$dependency->isBuildStatic()) {
logger()->info('extension ' . $this->getName() . ' requires extension ' . $dependency->getName());
$dependency->buildShared();
}
}
match (PHP_OS_FAMILY) {
'Darwin', 'Linux' => $this->buildUnixShared(),
default => throw new WrongUsageException(PHP_OS_FAMILY . ' build shared extensions is not supported yet'),
};
} }
/** /**
@ -479,7 +488,7 @@ class Extension
$depLib = $this->builder->getLib($name); $depLib = $this->builder->getLib($name);
if (!$depLib) { if (!$depLib) {
if (!$optional) { if (!$optional) {
throw new RuntimeException("extension {$this->name} requires library {$name}"); throw new WrongUsageException("extension {$this->name} requires library {$name}");
} }
logger()->info("enabling {$this->name} without library {$name}"); logger()->info("enabling {$this->name} without library {$name}");
} else { } else {
@ -492,7 +501,7 @@ class Extension
$depExt = $this->builder->getExt($name); $depExt = $this->builder->getExt($name);
if (!$depExt) { if (!$depExt) {
if (!$optional) { if (!$optional) {
throw new RuntimeException("{$this->name} requires extension {$name}"); throw new WrongUsageException("{$this->name} requires extension {$name} which is not included");
} }
logger()->info("enabling {$this->name} without extension {$name}"); logger()->info("enabling {$this->name} without extension {$name}");
} else { } else {

View File

@ -4,8 +4,8 @@ declare(strict_types=1);
namespace SPC\builder; namespace SPC\builder;
use SPC\exception\FileSystemException; use SPC\exception\SPCException;
use SPC\exception\RuntimeException; use SPC\exception\SPCInternalException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\Downloader; use SPC\store\Downloader;
@ -30,9 +30,9 @@ abstract class LibraryBase
public function __construct(?string $source_dir = null) public function __construct(?string $source_dir = null)
{ {
if (static::NAME === 'unknown') { if (static::NAME === 'unknown') {
throw new RuntimeException('no unknown!!!!!'); throw new SPCInternalException('Please set the NAME constant in ' . static::class);
} }
$this->source_dir = $source_dir ?? (SOURCE_PATH . '/' . Config::getLib(static::NAME, 'source')); $this->source_dir = $source_dir ?? (SOURCE_PATH . DIRECTORY_SEPARATOR . Config::getLib(static::NAME, 'source'));
} }
/** /**
@ -154,7 +154,7 @@ abstract class LibraryBase
FileSystem::extractPackage($install_file, $lock['source_type'], DOWNLOAD_PATH . '/' . $install_file, BUILD_ROOT_PATH); FileSystem::extractPackage($install_file, $lock['source_type'], DOWNLOAD_PATH . '/' . $install_file, BUILD_ROOT_PATH);
$this->install(); $this->install();
return LIB_STATUS_OK; return LIB_STATUS_OK;
} catch (FileSystemException|RuntimeException $e) { } catch (SPCException $e) {
logger()->error('Failed to extract pre-built library [' . static::NAME . ']: ' . $e->getMessage()); logger()->error('Failed to extract pre-built library [' . static::NAME . ']: ' . $e->getMessage());
return LIB_STATUS_INSTALL_FAILED; return LIB_STATUS_INSTALL_FAILED;
} }
@ -304,7 +304,7 @@ abstract class LibraryBase
} }
$replace_items = json_decode(file_get_contents($replace_item_file), true); $replace_items = json_decode(file_get_contents($replace_item_file), true);
if (!is_array($replace_items)) { if (!is_array($replace_items)) {
throw new RuntimeException('Invalid placeholder file: ' . $replace_item_file); throw new SPCInternalException("Invalid placeholder file: {$replace_item_file}");
} }
$placeholders = get_pack_replace(); $placeholders = get_pack_replace();
// replace placeholders in BUILD_ROOT_PATH // replace placeholders in BUILD_ROOT_PATH
@ -331,7 +331,7 @@ abstract class LibraryBase
return; return;
} }
if (!$optional) { if (!$optional) {
throw new RuntimeException(static::NAME . " requires library {$name}"); throw new WrongUsageException(static::NAME . " requires library {$name} but it is not included");
} }
logger()->debug('enabling ' . static::NAME . " without {$name}"); logger()->debug('enabling ' . static::NAME . " without {$name}");
} }

View File

@ -8,8 +8,7 @@ use SPC\builder\Extension;
use SPC\builder\linux\LinuxBuilder; use SPC\builder\linux\LinuxBuilder;
use SPC\builder\macos\MacOSBuilder; use SPC\builder\macos\MacOSBuilder;
use SPC\builder\windows\WindowsBuilder; use SPC\builder\windows\WindowsBuilder;
use SPC\exception\FileSystemException; use SPC\exception\PatchException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\CustomExt; use SPC\util\CustomExt;
@ -98,7 +97,7 @@ class curl extends Extension
); );
if ($patched === null) { if ($patched === null) {
throw new \RuntimeException('Failed to patch config.m4 due to a regex error'); throw new PatchException('shared extension curl patcher', 'Failed to patch config.m4 due to a regex error');
} }
FileSystem::writeFile($file, $patched); FileSystem::writeFile($file, $patched);

View File

@ -6,6 +6,7 @@ namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\builder\windows\WindowsBuilder; use SPC\builder\windows\WindowsBuilder;
use SPC\exception\ValidationException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\CustomExt; use SPC\util\CustomExt;
use SPC\util\GlobalEnvManager; use SPC\util\GlobalEnvManager;
@ -18,7 +19,7 @@ class grpc extends Extension
public function patchBeforeBuildconf(): bool public function patchBeforeBuildconf(): bool
{ {
if ($this->builder instanceof WindowsBuilder) { if ($this->builder instanceof WindowsBuilder) {
throw new \RuntimeException('grpc extension does not support windows yet'); throw new ValidationException('grpc extension does not support windows yet');
} }
if (file_exists(SOURCE_PATH . '/php-src/ext/grpc')) { if (file_exists(SOURCE_PATH . '/php-src/ext/grpc')) {
return false; return false;
@ -27,7 +28,7 @@ class grpc extends Extension
if (is_dir($this->source_dir . '/src/php/ext/grpc')) { if (is_dir($this->source_dir . '/src/php/ext/grpc')) {
shell()->exec('ln -s ' . $this->source_dir . '/src/php/ext/grpc ' . SOURCE_PATH . '/php-src/ext/grpc'); shell()->exec('ln -s ' . $this->source_dir . '/src/php/ext/grpc ' . SOURCE_PATH . '/php-src/ext/grpc');
} else { } else {
throw new \RuntimeException('Cannot find grpc source code'); throw new ValidationException('Cannot find grpc source code in ' . $this->source_dir . '/src/php/ext/grpc');
} }
if (SPCTarget::getTargetOS() === 'Darwin') { if (SPCTarget::getTargetOS() === 'Darwin') {
FileSystem::replaceFileRegex( FileSystem::replaceFileRegex(

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\exception\RuntimeException; use SPC\exception\ValidationException;
use SPC\util\CustomExt; use SPC\util\CustomExt;
#[CustomExt('mbregex')] #[CustomExt('mbregex')]
@ -16,11 +16,6 @@ class mbregex extends Extension
return 'mbstring'; return 'mbstring';
} }
public function getConfigureArg(bool $shared = false): string
{
return '';
}
/** /**
* mbregex is not an extension, we need to overwrite the default check. * mbregex is not an extension, we need to overwrite the default check.
*/ */
@ -29,7 +24,7 @@ class mbregex extends Extension
$sharedext = $this->builder->getExt('mbstring')->isBuildShared() ? '-d "extension_dir=' . BUILD_MODULES_PATH . '" -d "extension=mbstring"' : ''; $sharedext = $this->builder->getExt('mbstring')->isBuildShared() ? '-d "extension_dir=' . BUILD_MODULES_PATH . '" -d "extension=mbstring"' : '';
[$ret] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n' . $sharedext . ' --ri "mbstring" | grep regex', false); [$ret] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n' . $sharedext . ' --ri "mbstring" | grep regex', false);
if ($ret !== 0) { if ($ret !== 0) {
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: compiled php-cli mbstring extension does not contain regex !'); throw new ValidationException("Extension {$this->getName()} failed compile check: compiled php-cli mbstring extension does not contain regex !");
} }
} }
@ -37,11 +32,11 @@ class mbregex extends Extension
{ {
[$ret, $out] = cmd()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n --ri "mbstring"', false); [$ret, $out] = cmd()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n --ri "mbstring"', false);
if ($ret !== 0) { if ($ret !== 0) {
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: compiled php-cli does not contain mbstring !'); throw new ValidationException("extension {$this->getName()} failed compile check: compiled php-cli does not contain mbstring !");
} }
$out = implode("\n", $out); $out = implode("\n", $out);
if (!str_contains($out, 'regex')) { if (!str_contains($out, 'regex')) {
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: compiled php-cli mbstring extension does not contain regex !'); throw new ValidationException("extension {$this->getName()} failed compile check: compiled php-cli mbstring extension does not contain regex !");
} }
} }
} }

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\exception\ValidationException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\CustomExt; use SPC\util\CustomExt;
use SPC\util\GlobalEnvManager; use SPC\util\GlobalEnvManager;
@ -15,7 +16,7 @@ class opentelemetry extends Extension
public function validate(): void public function validate(): void
{ {
if ($this->builder->getPHPVersionID() < 80000 && getenv('SPC_SKIP_PHP_VERSION_CHECK') !== 'yes') { if ($this->builder->getPHPVersionID() < 80000 && getenv('SPC_SKIP_PHP_VERSION_CHECK') !== 'yes') {
throw new \RuntimeException('The opentelemetry extension requires PHP 8.0 or later'); throw new ValidationException('The opentelemetry extension requires PHP 8.0 or later');
} }
} }

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\exception\RuntimeException; use SPC\exception\ValidationException;
use SPC\util\CustomExt; use SPC\util\CustomExt;
#[CustomExt('password-argon2')] #[CustomExt('password-argon2')]
@ -20,7 +20,7 @@ class password_argon2 extends Extension
{ {
[$ret] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n -r "assert(defined(\'PASSWORD_ARGON2I\'));"'); [$ret] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n -r "assert(defined(\'PASSWORD_ARGON2I\'));"');
if ($ret !== 0) { if ($ret !== 0) {
throw new RuntimeException('extension ' . $this->getName() . ' failed sanity check'); throw new ValidationException('extension ' . $this->getName() . ' failed sanity check', validation_module: 'password_argon2 function check');
} }
} }

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\exception\ValidationException;
use SPC\util\CustomExt; use SPC\util\CustomExt;
#[CustomExt('protobuf')] #[CustomExt('protobuf')]
@ -13,12 +14,12 @@ class protobuf extends Extension
public function validate(): void public function validate(): void
{ {
if ($this->builder->getPHPVersionID() < 80000 && getenv('SPC_SKIP_PHP_VERSION_CHECK') !== 'yes') { if ($this->builder->getPHPVersionID() < 80000 && getenv('SPC_SKIP_PHP_VERSION_CHECK') !== 'yes') {
throw new \RuntimeException('The latest protobuf extension requires PHP 8.0 or later'); throw new ValidationException('The latest protobuf extension requires PHP 8.0 or later');
} }
$grpc = $this->builder->getExt('grpc'); $grpc = $this->builder->getExt('grpc');
// protobuf conflicts with grpc // protobuf conflicts with grpc
if ($grpc?->isBuildStatic()) { if ($grpc?->isBuildStatic()) {
throw new \RuntimeException('protobuf conflicts with grpc, please remove grpc or protobuf extension'); throw new ValidationException('protobuf conflicts with grpc, please remove grpc or protobuf extension');
} }
} }
} }

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\exception\RuntimeException; use SPC\exception\ValidationException;
use SPC\util\CustomExt; use SPC\util\CustomExt;
#[CustomExt('swoole-hook-mysql')] #[CustomExt('swoole-hook-mysql')]
@ -32,10 +32,10 @@ class swoole_hook_mysql extends Extension
[$ret, $out] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n' . $this->getSharedExtensionLoadString() . ' --ri "swoole"', false); [$ret, $out] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -n' . $this->getSharedExtensionLoadString() . ' --ri "swoole"', false);
$out = implode('', $out); $out = implode('', $out);
if ($ret !== 0) { if ($ret !== 0) {
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: php-cli returned ' . $ret); throw new ValidationException("extension {$this->getName()} failed compile check: php-cli returned {$ret}", validation_module: 'extension swoole_hook_mysql sanity check');
} }
if (!str_contains($out, 'mysqlnd')) { if (!str_contains($out, 'mysqlnd')) {
throw new RuntimeException('swoole mysql hook is not enabled correctly.'); throw new ValidationException('swoole mysql hook is not enabled correctly.', validation_module: 'Extension swoole mysql hook avilability check');
} }
} }
} }

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\exception\RuntimeException; use SPC\exception\ValidationException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\util\CustomExt; use SPC\util\CustomExt;
@ -41,10 +41,16 @@ class swoole_hook_pgsql extends Extension
[$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"'); [$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"');
$out = implode('', $out); $out = implode('', $out);
if ($ret !== 0) { if ($ret !== 0) {
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: php-cli returned ' . $ret); throw new ValidationException(
"extension {$this->getName()} failed sanity check: php-cli returned {$ret}",
validation_module: 'Extension swoole-hook-pgsql sanity check'
);
} }
if (!str_contains($out, 'coroutine_pgsql')) { if (!str_contains($out, 'coroutine_pgsql')) {
throw new RuntimeException('swoole pgsql hook is not enabled correctly.'); throw new ValidationException(
'swoole pgsql hook is not enabled correctly.',
validation_module: 'Extension swoole pgsql hook availability check'
);
} }
} }
} }

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\exception\RuntimeException; use SPC\exception\ValidationException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\util\CustomExt; use SPC\util\CustomExt;
@ -41,10 +41,10 @@ class swoole_hook_sqlite extends Extension
[$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"'); [$ret, $out] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n' . $sharedExtensions . ' --ri "' . $this->getDistName() . '"');
$out = implode('', $out); $out = implode('', $out);
if ($ret !== 0) { if ($ret !== 0) {
throw new RuntimeException('extension ' . $this->getName() . ' failed compile check: php-cli returned ' . $ret); throw new ValidationException("extension {$this->getName()} failed compile check: php-cli returned {$ret}", validation_module: "Extension {$this->getName()} sanity check");
} }
if (!str_contains($out, 'coroutine_sqlite')) { if (!str_contains($out, 'coroutine_sqlite')) {
throw new RuntimeException('swoole sqlite hook is not enabled correctly.'); throw new ValidationException('swoole sqlite hook is not enabled correctly.', validation_module: 'Extension swoole sqlite hook availability check');
} }
} }
} }

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\exception\RuntimeException; use SPC\exception\ValidationException;
use SPC\util\CustomExt; use SPC\util\CustomExt;
#[CustomExt('swow')] #[CustomExt('swow')]
@ -14,7 +14,7 @@ class swow extends Extension
public function validate(): void public function validate(): void
{ {
if ($this->builder->getPHPVersionID() < 80000 && getenv('SPC_SKIP_PHP_VERSION_CHECK') !== 'yes') { if ($this->builder->getPHPVersionID() < 80000 && getenv('SPC_SKIP_PHP_VERSION_CHECK') !== 'yes') {
throw new RuntimeException('The latest swow extension requires PHP 8.0 or later'); throw new ValidationException('The latest swow extension requires PHP 8.0 or later');
} }
} }

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\exception\ValidationException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\CustomExt; use SPC\util\CustomExt;
@ -14,7 +15,7 @@ class uv extends Extension
public function validate(): void public function validate(): void
{ {
if ($this->builder->getPHPVersionID() < 80000 && getenv('SPC_SKIP_PHP_VERSION_CHECK') !== 'yes') { if ($this->builder->getPHPVersionID() < 80000 && getenv('SPC_SKIP_PHP_VERSION_CHECK') !== 'yes') {
throw new \RuntimeException('The latest uv extension requires PHP 8.0 or later'); throw new ValidationException('The latest uv extension requires PHP 8.0 or later');
} }
} }

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\extension; namespace SPC\builder\extension;
use SPC\builder\Extension; use SPC\builder\Extension;
use SPC\exception\RuntimeException; use SPC\exception\SPCInternalException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\CustomExt; use SPC\util\CustomExt;
@ -24,7 +24,7 @@ class xml extends Extension
'xmlreader' => '--enable-xmlreader', 'xmlreader' => '--enable-xmlreader',
'xmlwriter' => '--enable-xmlwriter', 'xmlwriter' => '--enable-xmlwriter',
'simplexml' => '--enable-simplexml', 'simplexml' => '--enable-simplexml',
default => throw new RuntimeException('Not accept non-xml extension'), default => throw new SPCInternalException('Not accept non-xml extension'),
}; };
$arg .= ($shared ? '=shared' : '') . ' --with-libxml="' . BUILD_ROOT_PATH . '"'; $arg .= ($shared ? '=shared' : '') . ' --with-libxml="' . BUILD_ROOT_PATH . '"';
return $arg; return $arg;
@ -44,7 +44,7 @@ class xml extends Extension
'xmlreader' => '--enable-xmlreader', 'xmlreader' => '--enable-xmlreader',
'xmlwriter' => '--enable-xmlwriter', 'xmlwriter' => '--enable-xmlwriter',
'simplexml' => '--with-simplexml', 'simplexml' => '--with-simplexml',
default => throw new RuntimeException('Not accept non-xml extension'), default => throw new SPCInternalException('Not accept non-xml extension'),
}; };
$arg .= ' --with-libxml'; $arg .= ' --with-libxml';
return $arg; return $arg;

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\freebsd; namespace SPC\builder\freebsd;
use SPC\builder\traits\UnixSystemUtilTrait; use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\exception\RuntimeException; use SPC\exception\EnvironmentException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
class SystemUtil class SystemUtil
@ -20,7 +20,10 @@ class SystemUtil
{ {
[$ret, $output] = shell()->execWithResult('sysctl -n hw.ncpu'); [$ret, $output] = shell()->execWithResult('sysctl -n hw.ncpu');
if ($ret !== 0) { if ($ret !== 0) {
throw new RuntimeException('Failed to get cpu count'); throw new EnvironmentException(
'Failed to get cpu count from FreeBSD sysctl',
'Please ensure you are running this command on a FreeBSD system and have the sysctl command available.'
);
} }
return (int) $output[0]; return (int) $output[0];

View File

@ -358,7 +358,7 @@ class LinuxBuilder extends UnixBuilderBase
$out[1] = explode(' ', $out[1]); $out[1] = explode(' ', $out[1]);
$offset = $out[1][0]; $offset = $out[1][0];
if ($ret !== 0 || !str_starts_with($offset, '0x')) { if ($ret !== 0 || !str_starts_with($offset, '0x')) {
throw new RuntimeException('Cannot find offset in readelf output'); throw new PatchException('phpmicro UPX patcher', 'Cannot find offset in readelf output');
} }
$offset = hexdec($offset); $offset = hexdec($offset);
// remove upx extra wastes // remove upx extra wastes

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace SPC\builder\macos; namespace SPC\builder\macos;
use SPC\builder\traits\UnixSystemUtilTrait; use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\exception\RuntimeException; use SPC\exception\EnvironmentException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
class SystemUtil class SystemUtil
@ -18,12 +18,15 @@ class SystemUtil
*/ */
public static function getCpuCount(): int public static function getCpuCount(): int
{ {
[$ret, $output] = shell()->execWithResult('sysctl -n hw.ncpu', false); $cpu = exec('sysctl -n hw.ncpu', $output, $ret);
if ($ret !== 0) { if ($ret !== 0) {
throw new RuntimeException('Failed to get cpu count'); throw new EnvironmentException(
'Failed to get cpu count from macOS sysctl',
'Please ensure you are running this command on a macOS system and have the sysctl command available.'
);
} }
return (int) $output[0]; return (int) $cpu;
} }
/** /**

View File

@ -4,9 +4,7 @@ declare(strict_types=1);
namespace SPC\builder\traits; namespace SPC\builder\traits;
use SPC\exception\FileSystemException; use SPC\exception\PatchException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\SPCConfigUtil; use SPC\util\SPCConfigUtil;
@ -38,7 +36,7 @@ trait UnixLibraryTrait
foreach ($files as $name) { foreach ($files as $name) {
$realpath = realpath(BUILD_ROOT_PATH . '/lib/pkgconfig/' . $name); $realpath = realpath(BUILD_ROOT_PATH . '/lib/pkgconfig/' . $name);
if ($realpath === false) { if ($realpath === false) {
throw new RuntimeException('Cannot find library [' . static::NAME . '] pkgconfig file [' . $name . '] !'); throw new PatchException('pkg-config prefix patcher', 'Cannot find library [' . static::NAME . '] pkgconfig file [' . $name . '] in ' . BUILD_LIB_PATH . '/pkgconfig/ !');
} }
logger()->debug('Patching ' . $realpath); logger()->debug('Patching ' . $realpath);
// replace prefix // replace prefix
@ -65,7 +63,7 @@ trait UnixLibraryTrait
$realpath = realpath(BUILD_LIB_PATH . '/' . $name); $realpath = realpath(BUILD_LIB_PATH . '/' . $name);
if ($realpath === false) { if ($realpath === false) {
if ($throwOnMissing) { if ($throwOnMissing) {
throw new RuntimeException('Cannot find library [' . static::NAME . '] la file [' . $name . '] !'); throw new PatchException('la dependency patcher', 'Cannot find library [' . static::NAME . '] la file [' . $name . '] !');
} }
logger()->warning('Cannot find library [' . static::NAME . '] la file [' . $name . '] !'); logger()->warning('Cannot find library [' . static::NAME . '] la file [' . $name . '] !');
continue; continue;

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace SPC\builder\traits; namespace SPC\builder\traits;
use SPC\exception\RuntimeException; use SPC\exception\SPCException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\PkgConfigUtil; use SPC\util\PkgConfigUtil;
@ -24,7 +24,7 @@ trait openssl
if (PHP_OS_FAMILY !== 'Windows') { if (PHP_OS_FAMILY !== 'Windows') {
try { try {
return PkgConfigUtil::getModuleVersion('openssl'); return PkgConfigUtil::getModuleVersion('openssl');
} catch (RuntimeException) { } catch (SPCException) {
} }
} }
// get openssl version from header openssl/opensslv.h // get openssl version from header openssl/opensslv.h

View File

@ -5,8 +5,8 @@ declare(strict_types=1);
namespace SPC\builder\unix; namespace SPC\builder\unix;
use SPC\builder\BuilderBase; use SPC\builder\BuilderBase;
use SPC\exception\FileSystemException; use SPC\exception\SPCInternalException;
use SPC\exception\RuntimeException; use SPC\exception\ValidationException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\CurlHook; use SPC\store\CurlHook;
@ -56,7 +56,14 @@ abstract class UnixBuilderBase extends BuilderBase
} }
// if some libs are not supported (but in config "lib.json", throw exception) // if some libs are not supported (but in config "lib.json", throw exception)
if (!isset($support_lib_list[$library])) { if (!isset($support_lib_list[$library])) {
throw new WrongUsageException('library [' . $library . '] is in the lib.json list but not supported to compile, but in the future I will support it!'); $os = match (PHP_OS_FAMILY) {
'Linux' => 'Linux',
'Darwin' => 'macOS',
'Windows' => 'Windows',
'BSD' => 'FreeBSD',
default => PHP_OS_FAMILY,
};
throw new WrongUsageException("library [{$library}] is in the lib.json list but not supported to build on {$os}.");
} }
$lib = new ($support_lib_list[$library])($this); $lib = new ($support_lib_list[$library])($this);
$this->addLib($lib); $this->addLib($lib);
@ -80,7 +87,7 @@ abstract class UnixBuilderBase extends BuilderBase
[$ret, $output] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n -r "echo \"hello\";"'); [$ret, $output] = shell()->execWithResult(BUILD_BIN_PATH . '/php -n -r "echo \"hello\";"');
$raw_output = implode('', $output); $raw_output = implode('', $output);
if ($ret !== 0 || trim($raw_output) !== 'hello') { if ($ret !== 0 || trim($raw_output) !== 'hello') {
throw new RuntimeException("cli failed sanity check: ret[{$ret}]. out[{$raw_output}]"); throw new ValidationException("cli failed sanity check. code: {$ret}, output: {$raw_output}", validation_module: 'php-cli sanity check');
} }
foreach ($this->getExts() as $ext) { foreach ($this->getExts() as $ext) {
@ -103,7 +110,10 @@ abstract class UnixBuilderBase extends BuilderBase
foreach ($task['conditions'] as $condition => $closure) { foreach ($task['conditions'] as $condition => $closure) {
if (!$closure($ret, $out)) { if (!$closure($ret, $out)) {
$raw_out = trim(implode('', $out)); $raw_out = trim(implode('', $out));
throw new RuntimeException("micro failed sanity check: {$task_name}, condition [{$condition}], ret[{$ret}], out[{$raw_out}]"); throw new ValidationException(
"failure info: {$condition}, code: {$ret}, output: {$raw_out}",
validation_module: "phpmicro sanity check item [{$task_name}]"
);
} }
} }
} }
@ -142,11 +152,17 @@ abstract class UnixBuilderBase extends BuilderBase
} }
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens); [$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens);
if ($ret !== 0) { if ($ret !== 0) {
throw new RuntimeException('embed failed sanity check: build failed. Error message: ' . implode("\n", $out)); throw new ValidationException(
'embed failed sanity check: build failed. Error message: ' . implode("\n", $out),
validation_module: 'static libphp.a sanity check'
);
} }
[$ret, $output] = shell()->cd($sample_file_path)->execWithResult($ext_path . './embed'); [$ret, $output] = shell()->cd($sample_file_path)->execWithResult($ext_path . './embed');
if ($ret !== 0 || trim(implode('', $output)) !== 'hello') { if ($ret !== 0 || trim(implode('', $output)) !== 'hello') {
throw new RuntimeException('embed failed sanity check: run failed. Error message: ' . implode("\n", $output)); throw new ValidationException(
'embed failed sanity check: run failed. Error message: ' . implode("\n", $output),
validation_module: 'static libphp.a sanity check'
);
} }
} }
@ -155,14 +171,20 @@ abstract class UnixBuilderBase extends BuilderBase
logger()->info('running frankenphp sanity check'); logger()->info('running frankenphp sanity check');
$frankenphp = BUILD_BIN_PATH . '/frankenphp'; $frankenphp = BUILD_BIN_PATH . '/frankenphp';
if (!file_exists($frankenphp)) { if (!file_exists($frankenphp)) {
throw new RuntimeException('FrankenPHP binary not found: ' . $frankenphp); throw new ValidationException(
"FrankenPHP binary not found: {$frankenphp}",
validation_module: 'FrankenPHP sanity check'
);
} }
$prefix = PHP_OS_FAMILY === 'Darwin' ? 'DYLD_' : 'LD_'; $prefix = PHP_OS_FAMILY === 'Darwin' ? 'DYLD_' : 'LD_';
[$ret, $output] = shell() [$ret, $output] = shell()
->setEnv(["{$prefix}LIBRARY_PATH" => BUILD_LIB_PATH]) ->setEnv(["{$prefix}LIBRARY_PATH" => BUILD_LIB_PATH])
->execWithResult("{$frankenphp} version"); ->execWithResult("{$frankenphp} version");
if ($ret !== 0 || !str_contains(implode('', $output), 'FrankenPHP')) { if ($ret !== 0 || !str_contains(implode('', $output), 'FrankenPHP')) {
throw new RuntimeException('FrankenPHP failed sanity check: ret[' . $ret . ']. out[' . implode('', $output) . ']'); throw new ValidationException(
'FrankenPHP failed sanity check: ret[' . $ret . ']. out[' . implode('', $output) . ']',
validation_module: 'FrankenPHP sanity check'
);
} }
} }
} }
@ -178,7 +200,7 @@ abstract class UnixBuilderBase extends BuilderBase
BUILD_TARGET_CLI => SOURCE_PATH . '/php-src/sapi/cli/php', BUILD_TARGET_CLI => SOURCE_PATH . '/php-src/sapi/cli/php',
BUILD_TARGET_MICRO => SOURCE_PATH . '/php-src/sapi/micro/micro.sfx', BUILD_TARGET_MICRO => SOURCE_PATH . '/php-src/sapi/micro/micro.sfx',
BUILD_TARGET_FPM => SOURCE_PATH . '/php-src/sapi/fpm/php-fpm', BUILD_TARGET_FPM => SOURCE_PATH . '/php-src/sapi/fpm/php-fpm',
default => throw new RuntimeException('Deployment does not accept type ' . $type), default => throw new SPCInternalException("Deployment does not accept type {$type}"),
}; };
logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file'); logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file');
FileSystem::createDir(BUILD_BIN_PATH); FileSystem::createDir(BUILD_BIN_PATH);
@ -191,7 +213,7 @@ abstract class UnixBuilderBase extends BuilderBase
*/ */
protected function cleanMake(): void protected function cleanMake(): void
{ {
logger()->info('cleaning up'); logger()->info('cleaning up php-src build files');
shell()->cd(SOURCE_PATH . '/php-src')->exec('make clean'); shell()->cd(SOURCE_PATH . '/php-src')->exec('make clean');
} }

View File

@ -4,6 +4,8 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\exception\BuildFailureException;
trait fastlz trait fastlz
{ {
protected function build(): void protected function build(): void
@ -13,10 +15,10 @@ trait fastlz
->exec((getenv('AR') ?: 'ar') . ' rcs libfastlz.a fastlz.o'); ->exec((getenv('AR') ?: 'ar') . ' rcs libfastlz.a fastlz.o');
if (!copy($this->source_dir . '/fastlz.h', BUILD_INCLUDE_PATH . '/fastlz.h')) { if (!copy($this->source_dir . '/fastlz.h', BUILD_INCLUDE_PATH . '/fastlz.h')) {
throw new \RuntimeException('Failed to copy fastlz.h'); throw new BuildFailureException('Failed to copy fastlz.h, file does not exist');
} }
if (!copy($this->source_dir . '/libfastlz.a', BUILD_LIB_PATH . '/libfastlz.a')) { if (!copy($this->source_dir . '/libfastlz.a', BUILD_LIB_PATH . '/libfastlz.a')) {
throw new \RuntimeException('Failed to copy libfastlz.a'); throw new BuildFailureException('Failed to copy libfastlz.a, file does not exist');
} }
} }
} }

View File

@ -5,8 +5,8 @@ declare(strict_types=1);
namespace SPC\builder\unix\library; namespace SPC\builder\unix\library;
use SPC\builder\linux\library\LinuxLibraryBase; use SPC\builder\linux\library\LinuxLibraryBase;
use SPC\exception\BuildFailureException;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\util\SPCTarget; use SPC\util\SPCTarget;
@ -60,7 +60,7 @@ trait postgresql
$envs .= " LIBS=\"{$libs}{$libcpp}\" "; $envs .= " LIBS=\"{$libs}{$libcpp}\" ";
} }
if ($error_exec_cnt > 0) { if ($error_exec_cnt > 0) {
throw new RuntimeException('Failed to get pkg-config information!'); throw new BuildFailureException('Failed to get pkg-config information!');
} }
FileSystem::resetDir($this->source_dir . '/build'); FileSystem::resetDir($this->source_dir . '/build');
@ -74,7 +74,7 @@ trait postgresql
->exec('sed -i.backup "278 s/^/# /" ../src/Makefile.shlib') ->exec('sed -i.backup "278 s/^/# /" ../src/Makefile.shlib')
->exec('sed -i.backup "402 s/^/# /" ../src/Makefile.shlib'); ->exec('sed -i.backup "402 s/^/# /" ../src/Makefile.shlib');
} else { } else {
throw new RuntimeException('Unsupported version for postgresql: ' . $version . ' !'); throw new BuildFailureException('Unsupported version for postgresql: ' . $version . ' !');
} }
// configure // configure

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace SPC\builder\windows; namespace SPC\builder\windows;
use SPC\exception\FileSystemException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
class SystemUtil class SystemUtil

View File

@ -5,8 +5,8 @@ declare(strict_types=1);
namespace SPC\builder\windows; namespace SPC\builder\windows;
use SPC\builder\BuilderBase; use SPC\builder\BuilderBase;
use SPC\exception\FileSystemException; use SPC\exception\SPCInternalException;
use SPC\exception\RuntimeException; use SPC\exception\ValidationException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
@ -267,7 +267,7 @@ class WindowsBuilder extends BuilderBase
logger()->info('running cli sanity check'); logger()->info('running cli sanity check');
[$ret, $output] = cmd()->execWithResult(BUILD_ROOT_PATH . '\bin\php.exe -n -r "echo \"hello\";"'); [$ret, $output] = cmd()->execWithResult(BUILD_ROOT_PATH . '\bin\php.exe -n -r "echo \"hello\";"');
if ($ret !== 0 || trim(implode('', $output)) !== 'hello') { if ($ret !== 0 || trim(implode('', $output)) !== 'hello') {
throw new RuntimeException('cli failed sanity check'); throw new ValidationException('cli failed sanity check', validation_module: 'php-cli function check');
} }
foreach ($this->getExts(false) as $ext) { foreach ($this->getExts(false) as $ext) {
@ -290,7 +290,10 @@ class WindowsBuilder extends BuilderBase
foreach ($task['conditions'] as $condition => $closure) { foreach ($task['conditions'] as $condition => $closure) {
if (!$closure($ret, $out)) { if (!$closure($ret, $out)) {
$raw_out = trim(implode('', $out)); $raw_out = trim(implode('', $out));
throw new RuntimeException("micro failed sanity check: {$task_name}, condition [{$condition}], ret[{$ret}], out[{$raw_out}]"); throw new ValidationException(
"failure info: {$condition}, code: {$ret}, output: {$raw_out}",
validation_module: "phpmicro sanity check item [{$task_name}]"
);
} }
} }
} }
@ -308,7 +311,7 @@ class WindowsBuilder extends BuilderBase
$src = match ($type) { $src = match ($type) {
BUILD_TARGET_CLI => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\php.exe", BUILD_TARGET_CLI => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\php.exe",
BUILD_TARGET_MICRO => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\micro.sfx", BUILD_TARGET_MICRO => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\micro.sfx",
default => throw new RuntimeException('Deployment does not accept type ' . $type), default => throw new SPCInternalException("Deployment does not accept type {$type}"),
}; };
// with-upx-pack for cli and micro // with-upx-pack for cli and micro

View File

@ -5,24 +5,35 @@ declare(strict_types=1);
namespace SPC\builder\windows\library; namespace SPC\builder\windows\library;
use SPC\builder\windows\SystemUtil; use SPC\builder\windows\SystemUtil;
use SPC\exception\RuntimeException; use SPC\exception\BuildFailureException;
use SPC\exception\EnvironmentException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
class libsodium extends WindowsLibraryBase class libsodium extends WindowsLibraryBase
{ {
public const NAME = 'libsodium'; public const NAME = 'libsodium';
protected function build() private string $vs_ver_dir;
public function validate(): void
{ {
FileSystem::replaceFileStr($this->source_dir . '\src\libsodium\include\sodium\export.h', '#ifdef SODIUM_STATIC', '#if 1'); $this->vs_ver_dir = match ($ver = SystemUtil::findVisualStudio()['version']) {
$vs_ver_dir = match (SystemUtil::findVisualStudio()['version']) {
'vs17' => '\vs2022', 'vs17' => '\vs2022',
'vs16' => '\vs2019', 'vs16' => '\vs2019',
default => throw new RuntimeException('Current VS version is not supported yet!'), default => throw new EnvironmentException("Current VS version {$ver} is not supported yet!"),
}; };
}
public function patchBeforeBuild(): bool
{
FileSystem::replaceFileStr($this->source_dir . '\src\libsodium\include\sodium\export.h', '#ifdef SODIUM_STATIC', '#if 1');
return true;
}
protected function build(): void
{
// start build // start build
cmd()->cd($this->source_dir . '\builds\msvc\\' . $vs_ver_dir) cmd()->cd($this->source_dir . '\builds\msvc' . $this->vs_ver_dir)
->execWithWrapper( ->execWithWrapper(
$this->builder->makeSimpleWrapper('msbuild'), $this->builder->makeSimpleWrapper('msbuild'),
'libsodium.sln /t:Rebuild /p:Configuration=StaticRelease /p:Platform=x64 /p:PreprocessorDefinitions="SODIUM_STATIC=1"' 'libsodium.sln /t:Rebuild /p:Configuration=StaticRelease /p:Platform=x64 /p:PreprocessorDefinitions="SODIUM_STATIC=1"'
@ -46,7 +57,7 @@ class libsodium extends WindowsLibraryBase
} }
} }
if (!$find) { if (!$find) {
throw new RuntimeException('libsodium.lib not found'); throw new BuildFailureException("Build libsodium success, but cannot find libsodium.lib in {$this->source_dir}\\bin .");
} }
} }
} }

View File

@ -5,23 +5,33 @@ declare(strict_types=1);
namespace SPC\builder\windows\library; namespace SPC\builder\windows\library;
use SPC\builder\windows\SystemUtil; use SPC\builder\windows\SystemUtil;
use SPC\exception\RuntimeException; use SPC\exception\EnvironmentException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
class openssl extends WindowsLibraryBase class openssl extends WindowsLibraryBase
{ {
public const NAME = 'openssl'; public const NAME = 'openssl';
private ?string $perl;
public function validate(): void
{
global $argv;
$perl_path_native = PKG_ROOT_PATH . '\strawberry-perl-' . arch2gnu(php_uname('m')) . '-win\perl\bin\perl.exe';
$this->perl = file_exists($perl_path_native) ? ($perl_path_native) : SystemUtil::findCommand('perl.exe');
if ($this->perl === null) {
throw new EnvironmentException(
'You need to install perl first!',
"Please run \"{$argv[0]} doctor\" to fix the environment.",
);
}
}
protected function build(): void protected function build(): void
{ {
$perl_path_native = PKG_ROOT_PATH . '\strawberry-perl-' . arch2gnu(php_uname('m')) . '-win\perl\bin\perl.exe';
$perl = file_exists($perl_path_native) ? ($perl_path_native) : SystemUtil::findCommand('perl.exe');
if ($perl === null) {
throw new RuntimeException('You need to install perl first! (easiest way is using static-php-cli command "doctor")');
}
cmd()->cd($this->source_dir) cmd()->cd($this->source_dir)
->execWithWrapper( ->execWithWrapper(
$this->builder->makeSimpleWrapper($perl), $this->builder->makeSimpleWrapper($this->perl),
'Configure zlib VC-WIN64A ' . 'Configure zlib VC-WIN64A ' .
'no-shared ' . 'no-shared ' .
'--prefix=' . quote(BUILD_ROOT_PATH) . ' ' . '--prefix=' . quote(BUILD_ROOT_PATH) . ' ' .

View File

@ -9,10 +9,6 @@ use SPC\doctor\AsCheckItem;
use SPC\doctor\AsFixItem; use SPC\doctor\AsFixItem;
use SPC\doctor\CheckResult; use SPC\doctor\CheckResult;
use SPC\doctor\OptionalCheck; use SPC\doctor\OptionalCheck;
use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\Downloader; use SPC\store\Downloader;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\PackageManager; use SPC\store\PackageManager;
@ -58,55 +54,47 @@ class LinuxMuslCheck
#[AsFixItem('fix-musl-wrapper')] #[AsFixItem('fix-musl-wrapper')]
public function fixMusl(): bool public function fixMusl(): bool
{ {
try { $prefix = '';
$prefix = ''; if (get_current_user() !== 'root') {
if (get_current_user() !== 'root') { $prefix = 'sudo ';
$prefix = 'sudo '; logger()->warning('Current user is not root, using sudo for running command');
logger()->warning('Current user is not root, using sudo for running command');
}
// The hardcoded version here is to be consistent with the version compiled by `musl-cross-toolchain`.
$musl_version_name = 'musl-1.2.5';
$musl_source = [
'type' => 'url',
'url' => "https://musl.libc.org/releases/{$musl_version_name}.tar.gz",
];
logger()->info('Downloading ' . $musl_source['url']);
Downloader::downloadSource($musl_version_name, $musl_source);
FileSystem::extractSource($musl_version_name, SPC_SOURCE_ARCHIVE, DOWNLOAD_PATH . "/{$musl_version_name}.tar.gz");
// Apply CVE-2025-26519 patch
SourcePatcher::patchFile('musl-1.2.5_CVE-2025-26519_0001.patch', SOURCE_PATH . "/{$musl_version_name}");
SourcePatcher::patchFile('musl-1.2.5_CVE-2025-26519_0002.patch', SOURCE_PATH . "/{$musl_version_name}");
logger()->info('Installing musl wrapper');
shell()->cd(SOURCE_PATH . "/{$musl_version_name}")
->exec('CC=gcc CXX=g++ AR=ar LD=ld ./configure --disable-gcc-wrapper')
->exec('CC=gcc CXX=g++ AR=ar LD=ld make -j')
->exec("CC=gcc CXX=g++ AR=ar LD=ld {$prefix}make install");
// TODO: add path using putenv instead of editing /etc/profile
return true;
} catch (RuntimeException) {
return false;
} }
// The hardcoded version here is to be consistent with the version compiled by `musl-cross-toolchain`.
$musl_version_name = 'musl-1.2.5';
$musl_source = [
'type' => 'url',
'url' => "https://musl.libc.org/releases/{$musl_version_name}.tar.gz",
];
logger()->info('Downloading ' . $musl_source['url']);
Downloader::downloadSource($musl_version_name, $musl_source);
FileSystem::extractSource($musl_version_name, SPC_SOURCE_ARCHIVE, DOWNLOAD_PATH . "/{$musl_version_name}.tar.gz");
// Apply CVE-2025-26519 patch
SourcePatcher::patchFile('musl-1.2.5_CVE-2025-26519_0001.patch', SOURCE_PATH . "/{$musl_version_name}");
SourcePatcher::patchFile('musl-1.2.5_CVE-2025-26519_0002.patch', SOURCE_PATH . "/{$musl_version_name}");
logger()->info('Installing musl wrapper');
shell()->cd(SOURCE_PATH . "/{$musl_version_name}")
->exec('CC=gcc CXX=g++ AR=ar LD=ld ./configure --disable-gcc-wrapper')
->exec('CC=gcc CXX=g++ AR=ar LD=ld make -j')
->exec("CC=gcc CXX=g++ AR=ar LD=ld {$prefix}make install");
// TODO: add path using putenv instead of editing /etc/profile
return true;
} }
#[AsFixItem('fix-musl-cross-make')] #[AsFixItem('fix-musl-cross-make')]
public function fixMuslCrossMake(): bool public function fixMuslCrossMake(): bool
{ {
try { $prefix = '';
$prefix = ''; if (get_current_user() !== 'root') {
if (get_current_user() !== 'root') { $prefix = 'sudo ';
$prefix = 'sudo '; logger()->warning('Current user is not root, using sudo for running command');
logger()->warning('Current user is not root, using sudo for running command');
}
$arch = arch2gnu(php_uname('m'));
logger()->info("Downloading package musl-toolchain-{$arch}-linux");
PackageManager::installPackage("musl-toolchain-{$arch}-linux");
$pkg_root = PKG_ROOT_PATH . "/musl-toolchain-{$arch}-linux";
shell()->exec("{$prefix}cp -rf {$pkg_root}/* /usr/local/musl");
FileSystem::removeDir($pkg_root);
return true;
} catch (RuntimeException) {
return false;
} }
$arch = arch2gnu(php_uname('m'));
logger()->info("Downloading package musl-toolchain-{$arch}-linux");
PackageManager::installPackage("musl-toolchain-{$arch}-linux");
$pkg_root = PKG_ROOT_PATH . "/musl-toolchain-{$arch}-linux";
shell()->exec("{$prefix}cp -rf {$pkg_root}/* /usr/local/musl");
FileSystem::removeDir($pkg_root);
return true;
} }
} }

View File

@ -9,7 +9,7 @@ use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\doctor\AsCheckItem; use SPC\doctor\AsCheckItem;
use SPC\doctor\AsFixItem; use SPC\doctor\AsFixItem;
use SPC\doctor\CheckResult; use SPC\doctor\CheckResult;
use SPC\exception\RuntimeException; use SPC\exception\EnvironmentException;
class LinuxToolCheckList class LinuxToolCheckList
{ {
@ -74,16 +74,7 @@ class LinuxToolCheckList
} }
} }
if (!empty($missing)) { if (!empty($missing)) {
return match ($distro['dist']) { return CheckResult::fail(implode(', ', $missing) . ' not installed on your system', 'install-linux-tools', [$distro, $missing]);
'ubuntu',
'alpine',
'redhat',
'centos',
'Deepin',
'arch',
'debian' => CheckResult::fail(implode(', ', $missing) . ' not installed on your system', 'install-linux-tools', [$distro, $missing]),
default => CheckResult::fail(implode(', ', $missing) . ' not installed on your system'),
};
} }
return CheckResult::ok(); return CheckResult::ok();
} }
@ -123,22 +114,23 @@ class LinuxToolCheckList
'redhat' => 'dnf install -y', 'redhat' => 'dnf install -y',
'centos' => 'yum install -y', 'centos' => 'yum install -y',
'arch' => 'pacman -S --noconfirm', 'arch' => 'pacman -S --noconfirm',
default => throw new RuntimeException('Current linux distro does not have an auto-install script for musl packages yet.'), default => throw new EnvironmentException(
"Current linux distro [{$distro['dist']}] does not have an auto-install script for packages yet.",
'You can submit an issue to request support: https://github.com/crazywhalecc/static-php-cli/issues'
),
}; };
$prefix = ''; $prefix = '';
if (($user = exec('whoami')) !== 'root') { if (($user = exec('whoami')) !== 'root') {
$prefix = 'sudo '; $prefix = 'sudo ';
logger()->warning('Current user (' . $user . ') is not root, using sudo for running command'); logger()->warning('Current user (' . $user . ') is not root, using sudo for running command (may require password input)');
}
try {
$is_debian = in_array($distro['dist'], ['debian', 'ubuntu', 'Deepin']);
$to_install = $is_debian ? str_replace('xz', 'xz-utils', $missing) : $missing;
// debian, alpine libtool -> libtoolize
$to_install = str_replace('libtoolize', 'libtool', $to_install);
shell(true)->exec($prefix . $install_cmd . ' ' . implode(' ', $to_install));
} catch (RuntimeException) {
return false;
} }
$is_debian = in_array($distro['dist'], ['debian', 'ubuntu', 'Deepin']);
$to_install = $is_debian ? str_replace('xz', 'xz-utils', $missing) : $missing;
// debian, alpine libtool -> libtoolize
$to_install = str_replace('libtoolize', 'libtool', $to_install);
shell(true)->exec($prefix . $install_cmd . ' ' . implode(' ', $to_install));
return true; return true;
} }
} }

View File

@ -8,7 +8,6 @@ use SPC\builder\traits\UnixSystemUtilTrait;
use SPC\doctor\AsCheckItem; use SPC\doctor\AsCheckItem;
use SPC\doctor\AsFixItem; use SPC\doctor\AsFixItem;
use SPC\doctor\CheckResult; use SPC\doctor\CheckResult;
use SPC\exception\RuntimeException;
class MacOSToolCheckList class MacOSToolCheckList
{ {
@ -89,11 +88,7 @@ class MacOSToolCheckList
#[AsFixItem('brew')] #[AsFixItem('brew')]
public function fixBrew(): bool public function fixBrew(): bool
{ {
try { shell(true)->exec('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"');
shell(true)->exec('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"');
} catch (RuntimeException) {
return false;
}
return true; return true;
} }
@ -104,14 +99,10 @@ class MacOSToolCheckList
'glibtoolize' => 'libtool', 'glibtoolize' => 'libtool',
]; ];
foreach ($missing as $cmd) { foreach ($missing as $cmd) {
try { if (isset($replacement[$cmd])) {
if (isset($replacement[$cmd])) { $cmd = $replacement[$cmd];
$cmd = $replacement[$cmd];
}
shell(true)->exec('brew install --formula ' . escapeshellarg($cmd));
} catch (RuntimeException) {
return false;
} }
shell(true)->exec('brew install --formula ' . escapeshellarg($cmd));
} }
return true; return true;
} }

View File

@ -8,7 +8,6 @@ use SPC\builder\windows\SystemUtil;
use SPC\doctor\AsCheckItem; use SPC\doctor\AsCheckItem;
use SPC\doctor\AsFixItem; use SPC\doctor\AsFixItem;
use SPC\doctor\CheckResult; use SPC\doctor\CheckResult;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem; use SPC\store\FileSystem;
use SPC\store\PackageManager; use SPC\store\PackageManager;
@ -80,12 +79,8 @@ class WindowsToolCheckList
#[AsFixItem('install-php-sdk')] #[AsFixItem('install-php-sdk')]
public function installPhpSdk(): bool public function installPhpSdk(): bool
{ {
try { FileSystem::removeDir(getenv('PHP_SDK_PATH'));
FileSystem::removeDir(getenv('PHP_SDK_PATH')); cmd(true)->exec('git.exe clone --depth 1 https://github.com/php/php-sdk-binary-tools.git ' . getenv('PHP_SDK_PATH'));
cmd(true)->exec('git.exe clone --depth 1 https://github.com/php/php-sdk-binary-tools.git ' . getenv('PHP_SDK_PATH'));
} catch (RuntimeException) {
return false;
}
return true; return true;
} }

View File

@ -5,9 +5,8 @@ declare(strict_types=1);
namespace SPC\store; namespace SPC\store;
use SPC\exception\DownloaderException; use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException; use SPC\exception\InterruptException;
use SPC\exception\RuntimeException; use SPC\exception\SPCException;
use SPC\exception\WrongUsageException;
use SPC\store\pkg\CustomPackage; use SPC\store\pkg\CustomPackage;
use SPC\store\source\CustomSourceBase; use SPC\store\source\CustomSourceBase;
use SPC\util\SPCTarget; use SPC\util\SPCTarget;
@ -201,9 +200,9 @@ class Downloader
unlink(FileSystem::convertPath(DOWNLOAD_PATH . '/' . $filename)); unlink(FileSystem::convertPath(DOWNLOAD_PATH . '/' . $filename));
} }
}; };
self::registerCancelEvent($cancel_func); keyboard_interrupt_register($cancel_func);
self::curlDown(url: $url, path: FileSystem::convertPath(DOWNLOAD_PATH . "/{$filename}"), headers: $headers, hooks: $hooks, retries: self::getRetryAttempts()); self::curlDown(url: $url, path: FileSystem::convertPath(DOWNLOAD_PATH . "/{$filename}"), headers: $headers, hooks: $hooks, retries: self::getRetryAttempts());
self::unregisterCancelEvent(); keyboard_interrupt_unregister();
logger()->debug("Locking {$filename}"); logger()->debug("Locking {$filename}");
if ($download_as === SPC_DOWNLOAD_PRE_BUILT) { if ($download_as === SPC_DOWNLOAD_PRE_BUILT) {
$name = self::getPreBuiltLockName($name); $name = self::getPreBuiltLockName($name);
@ -248,12 +247,12 @@ class Downloader
f_passthru("cd \"{$download_path}\" && {$git} submodule update --init " . escapeshellarg($submodule)); f_passthru("cd \"{$download_path}\" && {$git} submodule update --init " . escapeshellarg($submodule));
} }
} }
} catch (RuntimeException $e) { } catch (SPCException $e) {
if (is_dir($download_path)) { if (is_dir($download_path)) {
FileSystem::removeDir($download_path); FileSystem::removeDir($download_path);
} }
if ($e->getCode() === 2 || $e->getCode() === -1073741510) { if ($e->getCode() === 2 || $e->getCode() === -1073741510) {
throw new WrongUsageException('Keyboard interrupted, download failed !'); throw new InterruptException('Keyboard interrupted, download failed !');
} }
if ($retries > 0) { if ($retries > 0) {
self::downloadGit($name, $url, $branch, $submodules, $move_path, $retries - 1, $lock_as); self::downloadGit($name, $url, $branch, $submodules, $move_path, $retries - 1, $lock_as);
@ -385,7 +384,7 @@ class Downloader
default: default:
throw new DownloaderException('unknown source type: ' . $pkg['type']); throw new DownloaderException('unknown source type: ' . $pkg['type']);
} }
} catch (RuntimeException $e) { } catch (\Throwable $e) {
// Because sometimes files downloaded through the command line are not automatically deleted after a failure. // Because sometimes files downloaded through the command line are not automatically deleted after a failure.
// Here we need to manually delete the file if it is detected to exist. // Here we need to manually delete the file if it is detected to exist.
if (isset($filename) && file_exists(DOWNLOAD_PATH . '/' . $filename)) { if (isset($filename) && file_exists(DOWNLOAD_PATH . '/' . $filename)) {
@ -503,7 +502,7 @@ class Downloader
default: default:
throw new DownloaderException('unknown source type: ' . $source['type']); throw new DownloaderException('unknown source type: ' . $source['type']);
} }
} catch (RuntimeException $e) { } catch (\Throwable $e) {
// Because sometimes files downloaded through the command line are not automatically deleted after a failure. // Because sometimes files downloaded through the command line are not automatically deleted after a failure.
// Here we need to manually delete the file if it is detected to exist. // Here we need to manually delete the file if it is detected to exist.
if (isset($filename) && file_exists(DOWNLOAD_PATH . '/' . $filename)) { if (isset($filename) && file_exists(DOWNLOAD_PATH . '/' . $filename)) {
@ -551,7 +550,7 @@ class Downloader
} }
f_exec($cmd, $output, $ret); f_exec($cmd, $output, $ret);
if ($ret === 2 || $ret === -1073741510) { if ($ret === 2 || $ret === -1073741510) {
throw new RuntimeException(sprintf('Failed to fetch "%s"', $url)); throw new InterruptException(sprintf('Canceled fetching "%s"', $url));
} }
if ($ret !== 0) { if ($ret !== 0) {
throw new DownloaderException(sprintf('Failed to fetch "%s"', $url)); throw new DownloaderException(sprintf('Failed to fetch "%s"', $url));
@ -563,7 +562,7 @@ class Downloader
} }
f_exec($cmd, $output, $ret); f_exec($cmd, $output, $ret);
if ($ret === 2 || $ret === -1073741510) { if ($ret === 2 || $ret === -1073741510) {
throw new RuntimeException(sprintf('Failed to fetch "%s"', $url)); throw new InterruptException(sprintf('Canceled fetching "%s"', $url));
} }
if ($ret !== 0) { if ($ret !== 0) {
throw new DownloaderException(sprintf('Failed to fetch "%s"', $url)); throw new DownloaderException(sprintf('Failed to fetch "%s"', $url));
@ -599,9 +598,9 @@ class Downloader
$cmd = SPC_CURL_EXEC . " -{$check}fSL {$retry} -o \"{$path}\" {$methodArg} {$headerArg} \"{$url}\""; $cmd = SPC_CURL_EXEC . " -{$check}fSL {$retry} -o \"{$path}\" {$methodArg} {$headerArg} \"{$url}\"";
try { try {
f_passthru($cmd); f_passthru($cmd);
} catch (RuntimeException $e) { } catch (\Throwable $e) {
if ($e->getCode() === 2 || $e->getCode() === -1073741510) { if ($e->getCode() === 2 || $e->getCode() === -1073741510) {
throw new WrongUsageException('Keyboard interrupted, download failed !'); throw new InterruptException('Keyboard interrupted, download failed !');
} }
throw $e; throw $e;
} }
@ -639,11 +638,11 @@ class Downloader
$url = "https://dl.static-php.dev/static-php-cli/deps/spc-download-mirror/{$source_name}/?format=json"; $url = "https://dl.static-php.dev/static-php-cli/deps/spc-download-mirror/{$source_name}/?format=json";
$json = json_decode(Downloader::curlExec(url: $url, retries: intval(getenv('SPC_DOWNLOAD_RETRIES') ?: 0)), true); $json = json_decode(Downloader::curlExec(url: $url, retries: intval(getenv('SPC_DOWNLOAD_RETRIES') ?: 0)), true);
if (!is_array($json)) { if (!is_array($json)) {
throw new RuntimeException('failed http fetch'); throw new DownloaderException('failed http fetch');
} }
$item = $json[0] ?? null; $item = $json[0] ?? null;
if ($item === null) { if ($item === null) {
throw new RuntimeException('failed to parse json'); throw new DownloaderException('failed to parse json');
} }
$full_url = 'https://dl.static-php.dev' . $item['full_path']; $full_url = 'https://dl.static-php.dev' . $item['full_path'];
$filename = basename($item['full_path']); $filename = basename($item['full_path']);

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace SPC\store; namespace SPC\store;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\SPCException;
class FileSystem class FileSystem
{ {
@ -147,6 +147,7 @@ class FileSystem
*/ */
public static function copyDir(string $from, string $to): void public static function copyDir(string $from, string $to): void
{ {
logger()->debug("Copying directory from {$from} to {$to}");
$dst_path = FileSystem::convertPath($to); $dst_path = FileSystem::convertPath($to);
$src_path = FileSystem::convertPath($from); $src_path = FileSystem::convertPath($from);
switch (PHP_OS_FAMILY) { switch (PHP_OS_FAMILY) {
@ -161,6 +162,23 @@ class FileSystem
} }
} }
/**
* Copy file from one location to another.
* This method will throw an exception if the copy operation fails.
*
* @param string $from Source file path
* @param string $to Destination file path
*/
public static function copy(string $from, string $to): void
{
logger()->debug("Copying file from {$from} to {$to}");
$dst_path = FileSystem::convertPath($to);
$src_path = FileSystem::convertPath($from);
if (!copy($src_path, $dst_path)) {
throw new FileSystemException('Cannot copy file from ' . $src_path . ' to ' . $dst_path);
}
}
/** /**
* Extract package archive to specified directory * Extract package archive to specified directory
* *
@ -187,13 +205,13 @@ class FileSystem
try { try {
// extract wrapper command // extract wrapper command
self::extractWithType($source_type, $filename, $extract_path); self::extractWithType($source_type, $filename, $extract_path);
} catch (RuntimeException $e) { } catch (SPCException $e) {
if (PHP_OS_FAMILY === 'Windows') { if (PHP_OS_FAMILY === 'Windows') {
f_passthru('rmdir /s /q ' . $target); f_passthru('rmdir /s /q ' . $target);
} else { } else {
f_passthru('rm -rf ' . $target); f_passthru('rm -rf ' . $target);
} }
throw new FileSystemException('Cannot extract package ' . $name, $e->getCode(), $e); throw new FileSystemException("Cannot extract package {$name}", $e->getCode(), $e);
} }
} }
@ -223,7 +241,7 @@ class FileSystem
try { try {
self::extractWithType($source_type, $filename, $move_path); self::extractWithType($source_type, $filename, $move_path);
self::emitSourceExtractHook($name, $target); self::emitSourceExtractHook($name, $target);
} catch (RuntimeException $e) { } catch (SPCException $e) {
if (PHP_OS_FAMILY === 'Windows') { if (PHP_OS_FAMILY === 'Windows') {
f_passthru('rmdir /s /q ' . $target); f_passthru('rmdir /s /q ' . $target);
} else { } else {
@ -505,7 +523,7 @@ class FileSystem
public static function restoreBackupFile(string $path): void public static function restoreBackupFile(string $path): void
{ {
if (!file_exists($path . '.bak')) { if (!file_exists($path . '.bak')) {
throw new RuntimeException('Cannot find bak file for ' . $path); throw new FileSystemException("Backup restore failed: Cannot find bak file for {$path}");
} }
copy($path . '.bak', $path); copy($path . '.bak', $path);
unlink($path . '.bak'); unlink($path . '.bak');

View File

@ -4,8 +4,7 @@ declare(strict_types=1);
namespace SPC\store; namespace SPC\store;
use SPC\exception\FileSystemException; use SPC\exception\SPCInternalException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
class LockFile class LockFile
@ -128,10 +127,10 @@ class LockFile
SPC_SOURCE_ARCHIVE => sha1_file(DOWNLOAD_PATH . '/' . $lock_options['filename']), SPC_SOURCE_ARCHIVE => sha1_file(DOWNLOAD_PATH . '/' . $lock_options['filename']),
SPC_SOURCE_GIT => exec('cd ' . escapeshellarg(DOWNLOAD_PATH . '/' . $lock_options['dirname']) . ' && ' . SPC_GIT_EXEC . ' rev-parse HEAD'), SPC_SOURCE_GIT => exec('cd ' . escapeshellarg(DOWNLOAD_PATH . '/' . $lock_options['dirname']) . ' && ' . SPC_GIT_EXEC . ' rev-parse HEAD'),
SPC_SOURCE_LOCAL => 'LOCAL HASH IS ALWAYS DIFFERENT', SPC_SOURCE_LOCAL => 'LOCAL HASH IS ALWAYS DIFFERENT',
default => filter_var(getenv('SPC_IGNORE_BAD_HASH'), FILTER_VALIDATE_BOOLEAN) ? '' : throw new RuntimeException("Unknown source type: {$lock_options['source_type']}"), default => filter_var(getenv('SPC_IGNORE_BAD_HASH'), FILTER_VALIDATE_BOOLEAN) ? '' : throw new SPCInternalException("Unknown source type: {$lock_options['source_type']}"),
}; };
if ($result === false && !filter_var(getenv('SPC_IGNORE_BAD_HASH'), FILTER_VALIDATE_BOOLEAN)) { if ($result === false && !filter_var(getenv('SPC_IGNORE_BAD_HASH'), FILTER_VALIDATE_BOOLEAN)) {
throw new RuntimeException("Failed to get hash for source: {$lock_options['source_type']}"); throw new SPCInternalException("Failed to get hash for source: {$lock_options['source_type']}");
} }
return $result ?: ''; return $result ?: '';
} }
@ -182,7 +181,7 @@ class LockFile
$file_content = file_get_contents(self::LOCK_FILE); $file_content = file_get_contents(self::LOCK_FILE);
self::$lock_file_content = json_decode($file_content, true); self::$lock_file_content = json_decode($file_content, true);
if (self::$lock_file_content === null) { if (self::$lock_file_content === null) {
throw new \RuntimeException('Failed to decode lock file: ' . self::LOCK_FILE); throw new SPCInternalException('Failed to decode lock file: ' . self::LOCK_FILE);
} }
} }
} }

View File

@ -9,8 +9,7 @@ use SPC\builder\linux\SystemUtil;
use SPC\builder\unix\UnixBuilderBase; use SPC\builder\unix\UnixBuilderBase;
use SPC\builder\windows\WindowsBuilder; use SPC\builder\windows\WindowsBuilder;
use SPC\exception\FileSystemException; use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException; use SPC\exception\PatchException;
use SPC\exception\WrongUsageException;
use SPC\util\SPCTarget; use SPC\util\SPCTarget;
class SourcePatcher class SourcePatcher
@ -171,7 +170,7 @@ class SourcePatcher
$patches[] = "sapi/micro/patches/{$patchName}_{$tryMajMin}.patch"; $patches[] = "sapi/micro/patches/{$patchName}_{$tryMajMin}.patch";
continue 2; continue 2;
} }
throw new RuntimeException("failed finding patch {$patchName}"); throw new PatchException('phpmicro patches', "Failed finding patch file or versioned file {$patchName} !");
} }
foreach ($patches as $patch) { foreach ($patches as $patch) {
@ -202,7 +201,7 @@ class SourcePatcher
$patch_str = FileSystem::convertPath($patch_file); $patch_str = FileSystem::convertPath($patch_file);
if (!file_exists($patch_str)) { if (!file_exists($patch_str)) {
throw new RuntimeException("Patch file [{$patch_str}] does not exist"); throw new PatchException($patch_name, "Patch file [{$patch_str}] does not exist");
} }
// Copy patch from phar // Copy patch from phar
@ -524,7 +523,7 @@ class SourcePatcher
++$line_num; ++$line_num;
} }
if ($found === false) { if ($found === false) {
throw new RuntimeException('Cannot patch windows CLI Makefile!'); throw new PatchException('Windows Makefile patching for php.exe target', 'Cannot patch windows CLI Makefile, Makefile does not contain "$(BUILD_DIR)\php.exe:" line');
} }
$lines[$line_num] = '$(BUILD_DIR)\php.exe: generated_files $(DEPS_CLI) $(PHP_GLOBAL_OBJS) $(CLI_GLOBAL_OBJS) $(STATIC_EXT_OBJS) $(ASM_OBJS) $(BUILD_DIR)\php.exe.res $(BUILD_DIR)\php.exe.manifest'; $lines[$line_num] = '$(BUILD_DIR)\php.exe: generated_files $(DEPS_CLI) $(PHP_GLOBAL_OBJS) $(CLI_GLOBAL_OBJS) $(STATIC_EXT_OBJS) $(ASM_OBJS) $(BUILD_DIR)\php.exe.res $(BUILD_DIR)\php.exe.manifest';
$lines[$line_num + 1] = "\t" . '"$(LINK)" /nologo $(PHP_GLOBAL_OBJS_RESP) $(CLI_GLOBAL_OBJS_RESP) $(STATIC_EXT_OBJS_RESP) $(STATIC_EXT_LIBS) $(ASM_OBJS) $(LIBS) $(LIBS_CLI) $(BUILD_DIR)\php.exe.res /out:$(BUILD_DIR)\php.exe $(LDFLAGS) $(LDFLAGS_CLI) /ltcg /nodefaultlib:msvcrt /nodefaultlib:msvcrtd /ignore:4286'; $lines[$line_num + 1] = "\t" . '"$(LINK)" /nologo $(PHP_GLOBAL_OBJS_RESP) $(CLI_GLOBAL_OBJS_RESP) $(STATIC_EXT_OBJS_RESP) $(STATIC_EXT_LIBS) $(ASM_OBJS) $(LIBS) $(LIBS_CLI) $(BUILD_DIR)\php.exe.res /out:$(BUILD_DIR)\php.exe $(LDFLAGS) $(LDFLAGS_CLI) /ltcg /nodefaultlib:msvcrt /nodefaultlib:msvcrtd /ignore:4286';

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace SPC\store\pkg; namespace SPC\store\pkg;
use SPC\exception\RuntimeException; use SPC\exception\DownloaderException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\store\CurlHook; use SPC\store\CurlHook;
use SPC\store\Downloader; use SPC\store\Downloader;
@ -77,14 +77,14 @@ class Zig extends CustomPackage
} }
if (!$latest_version) { if (!$latest_version) {
throw new RuntimeException('Could not determine latest Zig version'); throw new DownloaderException('Could not determine latest Zig version');
} }
logger()->info("Installing Zig version {$latest_version}"); logger()->info("Installing Zig version {$latest_version}");
$platform_key = "{$zig_arch}-{$zig_os}"; $platform_key = "{$zig_arch}-{$zig_os}";
if (!isset($index_json[$latest_version][$platform_key])) { if (!isset($index_json[$latest_version][$platform_key])) {
throw new RuntimeException("No download available for {$platform_key} in Zig version {$latest_version}"); throw new DownloaderException("No download available for {$platform_key} in Zig version {$latest_version}");
} }
$download_info = $index_json[$latest_version][$platform_key]; $download_info = $index_json[$latest_version][$platform_key];

View File

@ -7,7 +7,7 @@ namespace SPC\toolchain;
use SPC\builder\freebsd\SystemUtil as FreeBSDSystemUtil; use SPC\builder\freebsd\SystemUtil as FreeBSDSystemUtil;
use SPC\builder\linux\SystemUtil as LinuxSystemUtil; use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
use SPC\builder\macos\SystemUtil as MacOSSystemUtil; use SPC\builder\macos\SystemUtil as MacOSSystemUtil;
use SPC\exception\RuntimeException; use SPC\exception\EnvironmentException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\util\GlobalEnvManager; use SPC\util\GlobalEnvManager;
@ -35,7 +35,7 @@ class ClangNativeToolchain implements ToolchainInterface
'Linux' => LinuxSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."), 'Linux' => LinuxSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."),
'Darwin' => MacOSSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."), 'Darwin' => MacOSSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."),
'BSD' => FreeBSDSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."), 'BSD' => FreeBSDSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."),
default => throw new RuntimeException(__CLASS__ . ' is not supported on ' . PHP_OS_FAMILY . '.'), default => throw new EnvironmentException(__CLASS__ . ' is not supported on ' . PHP_OS_FAMILY . '.'),
}; };
} }
} }

View File

@ -7,6 +7,7 @@ namespace SPC\toolchain;
use SPC\builder\freebsd\SystemUtil as FreeBSDSystemUtil; use SPC\builder\freebsd\SystemUtil as FreeBSDSystemUtil;
use SPC\builder\linux\SystemUtil as LinuxSystemUtil; use SPC\builder\linux\SystemUtil as LinuxSystemUtil;
use SPC\builder\macos\SystemUtil as MacOSSystemUtil; use SPC\builder\macos\SystemUtil as MacOSSystemUtil;
use SPC\exception\EnvironmentException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\util\GlobalEnvManager; use SPC\util\GlobalEnvManager;
@ -31,7 +32,7 @@ class GccNativeToolchain implements ToolchainInterface
'Linux' => LinuxSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."), 'Linux' => LinuxSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."),
'Darwin' => MacOSSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."), 'Darwin' => MacOSSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."),
'BSD' => FreeBSDSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."), 'BSD' => FreeBSDSystemUtil::findCommand($command) ?? throw new WrongUsageException("{$command} not found, please install it or set {$env} to a valid path."),
default => throw new \RuntimeException(__CLASS__ . ' is not supported on ' . PHP_OS_FAMILY . '.'), default => throw new EnvironmentException(__CLASS__ . ' is not supported on ' . PHP_OS_FAMILY . '.'),
}; };
} }
} }

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace SPC\toolchain; namespace SPC\toolchain;
use SPC\exception\WrongUsageException; use SPC\exception\EnvironmentException;
use SPC\util\GlobalEnvManager; use SPC\util\GlobalEnvManager;
class MuslToolchain implements ToolchainInterface class MuslToolchain implements ToolchainInterface
@ -33,7 +33,7 @@ class MuslToolchain implements ToolchainInterface
GlobalEnvManager::putenv("SPC_CMD_PREFIX_PHP_CONFIGURE=LD_LIBRARY_PATH=\"{$ld_library_path}\" {$configure}"); GlobalEnvManager::putenv("SPC_CMD_PREFIX_PHP_CONFIGURE=LD_LIBRARY_PATH=\"{$ld_library_path}\" {$configure}");
if (!file_exists("/usr/local/musl/{$arch}-linux-musl/lib/libc.a")) { if (!file_exists("/usr/local/musl/{$arch}-linux-musl/lib/libc.a")) {
throw new WrongUsageException('You are building with musl-libc target in glibc distro, but musl-toolchain is not installed, please install musl-toolchain first. (You can use `doctor` command to install it)'); throw new EnvironmentException('You are building with musl-libc target in glibc distro, but musl-toolchain is not installed, please install musl-toolchain first. (You can use `doctor` command to install it)');
} }
} }

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace SPC\toolchain; namespace SPC\toolchain;
use SPC\exception\WrongUsageException; use SPC\exception\EnvironmentException;
use SPC\store\pkg\Zig; use SPC\store\pkg\Zig;
use SPC\util\GlobalEnvManager; use SPC\util\GlobalEnvManager;
@ -43,7 +43,7 @@ class ZigToolchain implements ToolchainInterface
public function afterInit(): void public function afterInit(): void
{ {
if (!is_dir(Zig::getEnvironment()['PATH'])) { if (!is_dir(Zig::getEnvironment()['PATH'])) {
throw new WrongUsageException('You are building with zig, but zig is not installed, please install zig first. (You can use `doctor` command to install it)'); throw new EnvironmentException('You are building with zig, but zig is not installed, please install zig first. (You can use `doctor` command to install it)');
} }
GlobalEnvManager::addPathIfNotExists(Zig::getEnvironment()['PATH']); GlobalEnvManager::addPathIfNotExists(Zig::getEnvironment()['PATH']);
f_passthru('ulimit -n 2048'); // zig opens extra file descriptors, so when a lot of extensions are built statically, 1024 is not enough f_passthru('ulimit -n 2048'); // zig opens extra file descriptors, so when a lot of extensions are built statically, 1024 is not enough

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace SPC\util; namespace SPC\util;
use SPC\builder\macos\SystemUtil; use SPC\builder\macos\SystemUtil;
use SPC\exception\RuntimeException; use SPC\exception\SPCInternalException;
use SPC\exception\WrongUsageException; use SPC\exception\WrongUsageException;
use SPC\toolchain\ToolchainManager; use SPC\toolchain\ToolchainManager;
@ -33,7 +33,7 @@ class GlobalEnvManager
} }
// Check pre-defined env vars exists // Check pre-defined env vars exists
if (getenv('BUILD_ROOT_PATH') === false) { if (getenv('BUILD_ROOT_PATH') === false) {
throw new RuntimeException('You must include src/globals/internal-env.php before using GlobalEnvManager'); throw new SPCInternalException('You must include src/globals/internal-env.php before using GlobalEnvManager');
} }
// Define env vars for unix // Define env vars for unix

View File

@ -4,9 +4,7 @@ declare(strict_types=1);
namespace SPC\util; namespace SPC\util;
use SPC\exception\FileSystemException; use SPC\exception\SPCInternalException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\Config; use SPC\store\Config;
use SPC\store\FileSystem; use SPC\store\FileSystem;
@ -100,7 +98,7 @@ class LicenseDumper
{ {
$licenses = Config::getSource($source_name)['license'] ?? []; $licenses = Config::getSource($source_name)['license'] ?? [];
if ($licenses === []) { if ($licenses === []) {
throw new RuntimeException('source [' . $source_name . '] license meta not exist'); throw new SPCInternalException("source [{$source_name}] license meta not exist");
} }
if (!array_is_list($licenses)) { if (!array_is_list($licenses)) {
@ -111,7 +109,7 @@ class LicenseDumper
yield $index => match ($license['type']) { yield $index => match ($license['type']) {
'text' => $license['text'], 'text' => $license['text'],
'file' => $this->loadSourceFile($source_name, $index, $license['path'], Config::getSource($source_name)['path'] ?? null), 'file' => $this->loadSourceFile($source_name, $index, $license['path'], Config::getSource($source_name)['path'] ?? null),
default => throw new RuntimeException('source [' . $source_name . '] license type is not allowed'), default => throw new SPCInternalException("source [{$source_name}] license type is not allowed"),
}; };
} }
} }
@ -122,7 +120,7 @@ class LicenseDumper
private function loadSourceFile(string $source_name, int $index, null|array|string $in_path, ?string $custom_base_path = null): string private function loadSourceFile(string $source_name, int $index, null|array|string $in_path, ?string $custom_base_path = null): string
{ {
if (is_null($in_path)) { if (is_null($in_path)) {
throw new RuntimeException('source [' . $source_name . '] license file is not set, please check config/source.json'); throw new SPCInternalException("source [{$source_name}] license file is not set, please check config/source.json");
} }
if (!is_array($in_path)) { if (!is_array($in_path)) {
@ -139,6 +137,6 @@ class LicenseDumper
return file_get_contents(BUILD_ROOT_PATH . '/source-licenses/' . $source_name . '/' . $index . '.txt'); return file_get_contents(BUILD_ROOT_PATH . '/source-licenses/' . $source_name . '/' . $index . '.txt');
} }
throw new RuntimeException('Cannot find any license file in source [' . $source_name . '] directory!'); throw new SPCInternalException("Cannot find any license file in source [{$source_name}] directory!");
} }
} }

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace SPC\util; namespace SPC\util;
use SPC\exception\RuntimeException; use SPC\exception\ExecutionException;
/** /**
* Utility class for pkg-config operations * Utility class for pkg-config operations
@ -95,7 +95,7 @@ class PkgConfigUtil
{ {
f_exec($cmd, $output, $result_code); f_exec($cmd, $output, $result_code);
if ($result_code !== 0) { if ($result_code !== 0) {
throw new RuntimeException("pkg-config command failed with code {$result_code}: {$cmd}"); throw new ExecutionException($cmd, "pkg-config command failed with code: {$result_code}");
} }
return implode("\n", $output); return implode("\n", $output);
} }

View File

@ -6,8 +6,7 @@ namespace SPC\Tests\globals;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
use SPC\exception\RuntimeException; use SPC\exception\ExecutionException;
use SPC\exception\WrongUsageException;
use ZM\Logger\ConsoleLogger; use ZM\Logger\ConsoleLogger;
/** /**
@ -64,7 +63,7 @@ class GlobalFunctionsTest extends TestCase
$this->markTestSkipped('Windows not support f_passthru'); $this->markTestSkipped('Windows not support f_passthru');
} }
$this->assertEquals(null, f_passthru('echo ""')); $this->assertEquals(null, f_passthru('echo ""'));
$this->expectException('SPC\exception\RuntimeException'); $this->expectException(ExecutionException::class);
f_passthru('false'); f_passthru('false');
} }
@ -80,16 +79,16 @@ class GlobalFunctionsTest extends TestCase
$this->markTestSkipped('Windows not support shell'); $this->markTestSkipped('Windows not support shell');
} }
$shell = shell(); $shell = shell();
$this->assertInstanceOf('SPC\util\UnixShell', $shell); $this->assertInstanceOf('SPC\util\shell\UnixShell', $shell);
$this->assertInstanceOf('SPC\util\UnixShell', $shell->cd('/')); $this->assertInstanceOf('SPC\util\shell\UnixShell', $shell->cd('/'));
$this->assertInstanceOf('SPC\util\UnixShell', $shell->exec('echo ""')); $this->assertInstanceOf('SPC\util\shell\UnixShell', $shell->exec('echo ""'));
$this->assertInstanceOf('SPC\util\UnixShell', $shell->setEnv(['SPC_TEST_ENV' => '1'])); $this->assertInstanceOf('SPC\util\shell\UnixShell', $shell->setEnv(['SPC_TEST_ENV' => '1']));
[$code, $out] = $shell->execWithResult('echo "_"'); [$code, $out] = $shell->execWithResult('echo "_"');
$this->assertEquals(0, $code); $this->assertEquals(0, $code);
$this->assertEquals('_', implode('', $out)); $this->assertEquals('_', implode('', $out));
$this->expectException('SPC\exception\RuntimeException'); $this->expectException('SPC\exception\ExecutionException');
$shell->exec('false'); $shell->exec('false');
} }
} }

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace SPC\Tests\store; namespace SPC\Tests\store;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use SPC\exception\WrongUsageException; use SPC\exception\InterruptException;
use SPC\store\Downloader; use SPC\store\Downloader;
use SPC\store\LockFile; use SPC\store\LockFile;
@ -34,7 +34,7 @@ class DownloaderTest extends TestCase
// test keyboard interrupt // test keyboard interrupt
try { try {
Downloader::downloadGit('setup-static-php', 'https://github.com/static-php/setup-static-php.git', 'SIGINT'); Downloader::downloadGit('setup-static-php', 'https://github.com/static-php/setup-static-php.git', 'SIGINT');
} catch (WrongUsageException $e) { } catch (InterruptException $e) {
$this->assertStringContainsString('interrupted', $e->getMessage()); $this->assertStringContainsString('interrupted', $e->getMessage());
return; return;
} }
@ -49,7 +49,7 @@ class DownloaderTest extends TestCase
// test keyboard interrupt // test keyboard interrupt
try { try {
Downloader::downloadFile('fake-file', 'https://fakecmd.com/curlDown', 'SIGINT'); Downloader::downloadFile('fake-file', 'https://fakecmd.com/curlDown', 'SIGINT');
} catch (WrongUsageException $e) { } catch (InterruptException $e) {
$this->assertStringContainsString('interrupted', $e->getMessage()); $this->assertStringContainsString('interrupted', $e->getMessage());
return; return;
} }

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace SPC\Tests\util; namespace SPC\Tests\util;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use SPC\exception\RuntimeException; use SPC\exception\SPCInternalException;
use SPC\util\GlobalEnvManager; use SPC\util\GlobalEnvManager;
/** /**
@ -105,7 +105,7 @@ final class GlobalEnvManagerTest extends TestCase
// Temporarily unset BUILD_ROOT_PATH // Temporarily unset BUILD_ROOT_PATH
putenv('BUILD_ROOT_PATH'); putenv('BUILD_ROOT_PATH');
$this->expectException(RuntimeException::class); $this->expectException(SPCInternalException::class);
GlobalEnvManager::init(); GlobalEnvManager::init();
} }

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace SPC\Tests\util; namespace SPC\Tests\util;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use SPC\exception\RuntimeException; use SPC\exception\ExecutionException;
use SPC\util\PkgConfigUtil; use SPC\util\PkgConfigUtil;
/** /**
@ -75,7 +75,7 @@ final class PkgConfigUtilTest extends TestCase
*/ */
public function testGetCflagsWithInvalidPackage(string $package): void public function testGetCflagsWithInvalidPackage(string $package): void
{ {
$this->expectException(RuntimeException::class); $this->expectException(ExecutionException::class);
PkgConfigUtil::getCflags($package); PkgConfigUtil::getCflags($package);
} }
@ -84,7 +84,7 @@ final class PkgConfigUtilTest extends TestCase
*/ */
public function testGetLibsArrayWithInvalidPackage(string $package): void public function testGetLibsArrayWithInvalidPackage(string $package): void
{ {
$this->expectException(RuntimeException::class); $this->expectException(ExecutionException::class);
PkgConfigUtil::getLibsArray($package); PkgConfigUtil::getLibsArray($package);
} }

View File

@ -6,7 +6,8 @@ declare(strict_types=1);
namespace SPC\store; namespace SPC\store;
use SPC\exception\RuntimeException; use SPC\exception\InterruptException;
use SPC\exception\SPCInternalException;
function f_exec(string $command, mixed &$output, mixed &$result_code): bool function f_exec(string $command, mixed &$output, mixed &$result_code): bool
{ {
@ -52,13 +53,13 @@ function f_passthru(string $cmd): bool
{ {
if (str_starts_with($cmd, 'git')) { if (str_starts_with($cmd, 'git')) {
if (str_contains($cmd, '--branch "SIGINT"')) { if (str_contains($cmd, '--branch "SIGINT"')) {
throw new RuntimeException('Interrupt', 2); throw new InterruptException('interrupted', 2);
} }
return true; return true;
} }
if (str_contains($cmd, 'https://fakecmd.com/curlDown')) { if (str_contains($cmd, 'https://fakecmd.com/curlDown')) {
if (str_contains($cmd, 'SIGINT')) { if (str_contains($cmd, 'SIGINT')) {
throw new RuntimeException('Interrupt', 2); throw new InterruptException('interrupted', 2);
} }
return true; return true;
} }
@ -71,5 +72,5 @@ function f_passthru(string $cmd): bool
return true; return true;
} }
} }
throw new RuntimeException('Invalid tests'); throw new SPCInternalException('Invalid tests');
} }