mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-03 23:05:41 +08:00
Merge pull request #835 from crazywhalecc/chore/test-and-validate
Chore: PHPUnit test & docs & PHPDoc for vendor mode
This commit is contained in:
@@ -4,7 +4,18 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder;
|
||||
|
||||
/**
|
||||
* Interface for library implementations
|
||||
*
|
||||
* This interface defines the basic contract that all library classes must implement.
|
||||
* It provides a common way to identify and work with different library types.
|
||||
*/
|
||||
interface LibraryInterface
|
||||
{
|
||||
/**
|
||||
* Get the name of the library
|
||||
*
|
||||
* @return string The library name
|
||||
*/
|
||||
public function getName(): string;
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ class LinuxBuilder extends UnixBuilderBase
|
||||
->exec('sed -i "s|^EXTENSION_DIR = .*|EXTENSION_DIR = /' . basename(BUILD_MODULES_PATH) . '|" Makefile')
|
||||
->exec(getenv('SPC_CMD_PREFIX_PHP_MAKE') . ' INSTALL_ROOT=' . BUILD_ROOT_PATH . " {$vars} install");
|
||||
|
||||
$ldflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS');
|
||||
$ldflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS') ?: '';
|
||||
if (preg_match('/-release\s+(\S+)/', $ldflags, $matches)) {
|
||||
$release = $matches[1];
|
||||
$realLibName = 'libphp-' . $release . '.so';
|
||||
|
||||
@@ -7,9 +7,6 @@ namespace SPC\store;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\WrongUsageException;
|
||||
|
||||
/**
|
||||
* 一个读取 config 配置的操作类
|
||||
*/
|
||||
class Config
|
||||
{
|
||||
public static ?array $pkg = null;
|
||||
@@ -23,6 +20,10 @@ class Config
|
||||
public static ?array $pre_built = null;
|
||||
|
||||
/**
|
||||
* Get pre-built configuration by name
|
||||
*
|
||||
* @param string $name The name of the pre-built configuration
|
||||
* @return mixed The pre-built configuration or null if not found
|
||||
* @throws WrongUsageException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
@@ -50,8 +51,10 @@ class Config
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置文件读取一个资源(source)的元信息
|
||||
* Get source configuration by name
|
||||
*
|
||||
* @param string $name The name of the source
|
||||
* @return null|array The source configuration or null if not found
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function getSource(string $name): ?array
|
||||
@@ -63,8 +66,10 @@ class Config
|
||||
}
|
||||
|
||||
/**
|
||||
* Read pkg from pkg.json
|
||||
* Get package configuration by name
|
||||
*
|
||||
* @param string $name The name of the package
|
||||
* @return null|array The package configuration or null if not found
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function getPkg(string $name): ?array
|
||||
@@ -76,11 +81,13 @@ class Config
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据不同的操作系统分别选择不同的 lib 库依赖项
|
||||
* 如果 key 为 null,那么直接返回整个 meta。
|
||||
* 如果 key 不为 null,则可以使用的 key 有 static-libs、headers、lib-depends、lib-suggests。
|
||||
* 对于 macOS 平台,支持 frameworks。
|
||||
* Get library configuration by name and optional key
|
||||
* Supports platform-specific configurations for different operating systems
|
||||
*
|
||||
* @param string $name The name of the library
|
||||
* @param null|string $key The configuration key (static-libs, headers, lib-depends, lib-suggests, frameworks, bin)
|
||||
* @param mixed $default Default value if key not found
|
||||
* @return mixed The library configuration or default value
|
||||
* @throws FileSystemException
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
@@ -115,6 +122,9 @@ class Config
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all library configurations
|
||||
*
|
||||
* @return array All library configurations
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function getLibs(): array
|
||||
@@ -126,6 +136,10 @@ class Config
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extension target configuration by name
|
||||
*
|
||||
* @param string $name The name of the extension
|
||||
* @return null|array The extension target configuration or default ['static', 'shared']
|
||||
* @throws WrongUsageException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
@@ -141,6 +155,13 @@ class Config
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extension configuration by name and optional key
|
||||
* Supports platform-specific configurations for different operating systems
|
||||
*
|
||||
* @param string $name The name of the extension
|
||||
* @param null|string $key The configuration key (lib-depends, lib-suggests, ext-depends, ext-suggests, arg-type)
|
||||
* @param mixed $default Default value if key not found
|
||||
* @return mixed The extension configuration or default value
|
||||
* @throws FileSystemException
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
@@ -175,6 +196,9 @@ class Config
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all extension configurations
|
||||
*
|
||||
* @return array All extension configurations
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function getExts(): array
|
||||
@@ -186,6 +210,9 @@ class Config
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all source configurations
|
||||
*
|
||||
* @return array All source configurations
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function getSources(): array
|
||||
|
||||
@@ -18,12 +18,13 @@ use SPC\util\SPCTarget;
|
||||
class Downloader
|
||||
{
|
||||
/**
|
||||
* Get latest version from BitBucket tag (type = bitbuckettag)
|
||||
* Get latest version from BitBucket tag
|
||||
*
|
||||
* @param string $name source name
|
||||
* @param array $source source meta info: [repo]
|
||||
* @param string $name Source name
|
||||
* @param array $source Source meta info: [repo]
|
||||
* @return array<int, string> [url, filename]
|
||||
* @throws DownloaderException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function getLatestBitbucketTag(string $name, array $source): array
|
||||
{
|
||||
@@ -53,13 +54,12 @@ class Downloader
|
||||
}
|
||||
|
||||
/**
|
||||
* Get latest version from GitHub tarball (type = ghtar / ghtagtar)
|
||||
*
|
||||
* @param string $name source name
|
||||
* @param array $source source meta info: [repo]
|
||||
* @param string $type type of tarball, default is 'releases'
|
||||
* @return array<int, string> [url, filename]
|
||||
* Get latest version from GitHub tarball
|
||||
*
|
||||
* @param string $name Source name
|
||||
* @param array $source Source meta info: [repo]
|
||||
* @param string $type Type of tarball, default is 'releases'
|
||||
* @return array<int, string> [url, filename]
|
||||
* @throws DownloaderException
|
||||
*/
|
||||
public static function getLatestGithubTarball(string $name, array $source, string $type = 'releases'): array
|
||||
@@ -107,8 +107,8 @@ class Downloader
|
||||
/**
|
||||
* Get latest version from GitHub release (uploaded archive)
|
||||
*
|
||||
* @param string $name source name
|
||||
* @param array $source source meta info: [repo, match]
|
||||
* @param string $name Source name
|
||||
* @param array $source Source meta info: [repo, match]
|
||||
* @param bool $match_result Whether to return matched result by `match` param (default: true)
|
||||
* @return array<int, string> When $match_result = true, and we matched, [url, filename]. Otherwise, [{asset object}. ...]
|
||||
* @throws DownloaderException
|
||||
@@ -150,8 +150,8 @@ class Downloader
|
||||
/**
|
||||
* Get latest version from file list (regex based crawler)
|
||||
*
|
||||
* @param string $name source name
|
||||
* @param array $source source meta info: [url, regex]
|
||||
* @param string $name Source name
|
||||
* @param array $source Source meta info: [filelist]
|
||||
* @return array<int, string> [url, filename]
|
||||
* @throws DownloaderException
|
||||
*/
|
||||
@@ -187,11 +187,17 @@ class Downloader
|
||||
}
|
||||
|
||||
/**
|
||||
* Just download file using system curl command, and lock it
|
||||
* Download file from URL
|
||||
*
|
||||
* @throws FileSystemException
|
||||
* @param string $name Download name
|
||||
* @param string $url Download URL
|
||||
* @param string $filename Target filename
|
||||
* @param null|string $move_path Optional move path after download
|
||||
* @param int $download_as Download type constant
|
||||
* @param array $headers Optional HTTP headers
|
||||
* @param array $hooks Optional curl hooks
|
||||
* @throws DownloaderException
|
||||
* @throws RuntimeException
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
public static function downloadFile(string $name, string $url, string $filename, ?string $move_path = null, int $download_as = SPC_DOWNLOAD_SOURCE, array $headers = [], array $hooks = []): void
|
||||
{
|
||||
@@ -213,11 +219,17 @@ class Downloader
|
||||
}
|
||||
|
||||
/**
|
||||
* Download git source, and lock it.
|
||||
* Download Git repository
|
||||
*
|
||||
* @throws FileSystemException
|
||||
* @param string $name Repository name
|
||||
* @param string $url Git repository URL
|
||||
* @param string $branch Branch to checkout
|
||||
* @param null|array $submodules Optional submodules to initialize
|
||||
* @param null|string $move_path Optional move path after download
|
||||
* @param int $retries Number of retry attempts
|
||||
* @param int $lock_as Lock type constant
|
||||
* @throws DownloaderException
|
||||
* @throws RuntimeException
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
public static function downloadGit(string $name, string $url, string $branch, ?array $submodules = null, ?string $move_path = null, int $retries = 0, int $lock_as = SPC_DOWNLOAD_SOURCE): void
|
||||
{
|
||||
@@ -304,7 +316,6 @@ class Downloader
|
||||
if ($pkg === null) {
|
||||
$pkg = Config::getPkg($name);
|
||||
}
|
||||
|
||||
if ($pkg === null) {
|
||||
logger()->warning('Package {name} unknown. Skipping.', ['name' => $name]);
|
||||
return;
|
||||
@@ -398,7 +409,7 @@ class Downloader
|
||||
}
|
||||
|
||||
/**
|
||||
* Download source by name and meta.
|
||||
* Download source
|
||||
*
|
||||
* @param string $name source name
|
||||
* @param null|array{
|
||||
@@ -428,7 +439,6 @@ class Downloader
|
||||
if ($source === null) {
|
||||
$source = Config::getSource($name);
|
||||
}
|
||||
|
||||
if ($source === null) {
|
||||
logger()->warning('Source {name} unknown. Skipping.', ['name' => $name]);
|
||||
return;
|
||||
@@ -522,7 +532,14 @@ class Downloader
|
||||
/**
|
||||
* Use curl command to get http response
|
||||
*
|
||||
* @param string $url Target URL
|
||||
* @param string $method HTTP method (GET, POST, etc.)
|
||||
* @param array $headers HTTP headers
|
||||
* @param array $hooks Curl hooks
|
||||
* @param int $retries Number of retry attempts
|
||||
* @return string Response body
|
||||
* @throws DownloaderException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function curlExec(string $url, string $method = 'GET', array $headers = [], array $hooks = [], int $retries = 0): string
|
||||
{
|
||||
@@ -574,6 +591,13 @@ class Downloader
|
||||
/**
|
||||
* Use curl to download sources from url
|
||||
*
|
||||
* @param string $url Download URL
|
||||
* @param string $path Target file path
|
||||
* @param string $method HTTP method
|
||||
* @param array $headers HTTP headers
|
||||
* @param array $hooks Curl hooks
|
||||
* @param int $retries Number of retry attempts
|
||||
* @throws DownloaderException
|
||||
* @throws RuntimeException
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
@@ -603,6 +627,12 @@ class Downloader
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pre-built lock name from source
|
||||
*
|
||||
* @param string $source Source name
|
||||
* @return string Lock name
|
||||
*/
|
||||
public static function getPreBuiltLockName(string $source): string
|
||||
{
|
||||
$os_family = PHP_OS_FAMILY;
|
||||
@@ -613,6 +643,12 @@ class Downloader
|
||||
return "{$source}-{$os_family}-{$gnu_arch}-{$libc}-{$libc_version}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default alternative source
|
||||
*
|
||||
* @param string $source_name Source name
|
||||
* @return array Alternative source configuration
|
||||
*/
|
||||
public static function getDefaultAlternativeSource(string $source_name): array
|
||||
{
|
||||
return [
|
||||
|
||||
@@ -12,6 +12,11 @@ class FileSystem
|
||||
private static array $_extract_hook = [];
|
||||
|
||||
/**
|
||||
* Load configuration array from JSON file
|
||||
*
|
||||
* @param string $config The configuration name (ext, lib, source, pkg, pre-built)
|
||||
* @param null|string $config_dir Optional custom config directory
|
||||
* @return array The loaded configuration array
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function loadConfigArray(string $config, ?string $config_dir = null): array
|
||||
@@ -37,9 +42,10 @@ class FileSystem
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件,读不出来直接抛出异常
|
||||
* Read file contents and throw exception on failure
|
||||
*
|
||||
* @param string $filename 文件路径
|
||||
* @param string $filename The file path to read
|
||||
* @return string The file contents
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function readFile(string $filename): string
|
||||
@@ -53,6 +59,12 @@ class FileSystem
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace string content in file
|
||||
*
|
||||
* @param string $filename The file path
|
||||
* @param mixed $search The search string
|
||||
* @param mixed $replace The replacement string
|
||||
* @return false|int Number of replacements or false on failure
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function replaceFileStr(string $filename, mixed $search = null, mixed $replace = null): false|int
|
||||
@@ -61,6 +73,12 @@ class FileSystem
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace content in file using regex
|
||||
*
|
||||
* @param string $filename The file path
|
||||
* @param mixed $search The regex pattern
|
||||
* @param mixed $replace The replacement string
|
||||
* @return false|int Number of replacements or false on failure
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function replaceFileRegex(string $filename, mixed $search = null, mixed $replace = null): false|int
|
||||
@@ -69,6 +87,11 @@ class FileSystem
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace content in file using custom callback
|
||||
*
|
||||
* @param string $filename The file path
|
||||
* @param mixed $callback The callback function
|
||||
* @return false|int Number of replacements or false on failure
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function replaceFileUser(string $filename, mixed $callback = null): false|int
|
||||
@@ -77,9 +100,10 @@ class FileSystem
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件后缀
|
||||
* Get file extension from filename
|
||||
*
|
||||
* @param string $fn 文件名
|
||||
* @param string $fn The filename
|
||||
* @return string The file extension (without dot)
|
||||
*/
|
||||
public static function extname(string $fn): string
|
||||
{
|
||||
@@ -91,10 +115,11 @@ class FileSystem
|
||||
}
|
||||
|
||||
/**
|
||||
* 寻找命令的真实路径,效果类似 which
|
||||
* Find command path in system PATH (similar to which command)
|
||||
*
|
||||
* @param string $name 命令名称
|
||||
* @param array $paths 路径列表,如果为空则默认从 PATH 系统变量搜索
|
||||
* @param string $name The command name
|
||||
* @param array $paths Optional array of paths to search
|
||||
* @return null|string The full path to the command or null if not found
|
||||
*/
|
||||
public static function findCommandPath(string $name, array $paths = []): ?string
|
||||
{
|
||||
@@ -120,6 +145,10 @@ class FileSystem
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy directory recursively
|
||||
*
|
||||
* @param string $from Source directory path
|
||||
* @param string $to Destination directory path
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function copyDir(string $from, string $to): void
|
||||
@@ -139,6 +168,12 @@ class FileSystem
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract package archive to specified directory
|
||||
*
|
||||
* @param string $name Package name
|
||||
* @param string $source_type Archive type (tar.gz, zip, etc.)
|
||||
* @param string $filename Archive filename
|
||||
* @param null|string $extract_path Optional extraction path
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
@@ -171,10 +206,12 @@ class FileSystem
|
||||
}
|
||||
|
||||
/**
|
||||
* 解压缩下载的资源包到 source 目录
|
||||
* Extract source archive to source directory
|
||||
*
|
||||
* @param string $name 资源名
|
||||
* @param string $filename 文件名
|
||||
* @param string $name Source name
|
||||
* @param string $source_type Archive type (tar.gz, zip, etc.)
|
||||
* @param string $filename Archive filename
|
||||
* @param null|string $move_path Optional move path
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
@@ -207,9 +244,10 @@ class FileSystem
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据系统环境的不同,自动转换路径的分隔符
|
||||
* Convert path to system-specific format
|
||||
*
|
||||
* @param string $path 路径
|
||||
* @param string $path The path to convert
|
||||
* @return string The converted path
|
||||
*/
|
||||
public static function convertPath(string $path): string
|
||||
{
|
||||
@@ -219,6 +257,12 @@ class FileSystem
|
||||
return str_replace('/', DIRECTORY_SEPARATOR, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Windows path to MinGW format
|
||||
*
|
||||
* @param string $path The Windows path
|
||||
* @return string The MinGW format path
|
||||
*/
|
||||
public static function convertWinPathToMinGW(string $path): string
|
||||
{
|
||||
if (preg_match('/^[A-Za-z]:/', $path)) {
|
||||
@@ -228,28 +272,21 @@ class FileSystem
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归或非递归扫描目录,可返回相对目录的文件列表或绝对目录的文件列表
|
||||
* Scan directory files recursively
|
||||
*
|
||||
* @param string $dir 目录
|
||||
* @param bool $recursive 是否递归扫描子目录
|
||||
* @param bool|string $relative 是否返回相对目录,如果为true则返回相对目录,如果为false则返回绝对目录
|
||||
* @param bool $include_dir 非递归模式下,是否包含目录
|
||||
* @since 2.5
|
||||
* @param string $dir Directory to scan
|
||||
* @param bool $recursive Whether to scan recursively
|
||||
* @param bool|string $relative Whether to return relative paths
|
||||
* @param bool $include_dir Whether to include directories in result
|
||||
* @return array|false Array of files or false on failure
|
||||
*/
|
||||
public static function scanDirFiles(string $dir, bool $recursive = true, bool|string $relative = false, bool $include_dir = false): array|false
|
||||
{
|
||||
$dir = self::convertPath($dir);
|
||||
// 不是目录不扫,直接 false 处理
|
||||
if (!file_exists($dir)) {
|
||||
logger()->debug('Scan dir failed, no such file or directory.');
|
||||
return false;
|
||||
}
|
||||
if (!is_dir($dir)) {
|
||||
logger()->warning('Scan dir failed, not directory.');
|
||||
return false;
|
||||
}
|
||||
logger()->debug('scanning directory ' . $dir);
|
||||
// 套上 zm_dir
|
||||
$scan_list = scandir($dir);
|
||||
if ($scan_list === false) {
|
||||
logger()->warning('Scan dir failed, cannot scan directory: ' . $dir);
|
||||
@@ -283,18 +320,17 @@ class FileSystem
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该路径下的所有类名,根据 psr-4 方式
|
||||
* Get PSR-4 classes from directory
|
||||
*
|
||||
* @param string $dir 目录
|
||||
* @param string $base_namespace 基类命名空间
|
||||
* @param null|mixed $rule 规则回调
|
||||
* @param bool|string $return_path_value 是否返回路径对应的数组,默认只返回类名列表
|
||||
* @throws FileSystemException
|
||||
* @param string $dir Directory to scan
|
||||
* @param string $base_namespace Base namespace
|
||||
* @param mixed $rule Optional filtering rule
|
||||
* @param bool|string $return_path_value Whether to return path as value
|
||||
* @return array Array of class names or class=>path pairs
|
||||
*/
|
||||
public static function getClassesPsr4(string $dir, string $base_namespace, mixed $rule = null, bool|string $return_path_value = false): array
|
||||
{
|
||||
$classes = [];
|
||||
// 扫描目录,使用递归模式,相对路径模式,因为下面此路径要用作转换成namespace
|
||||
$files = FileSystem::scanDirFiles($dir, true, true);
|
||||
if ($files === false) {
|
||||
throw new FileSystemException('Cannot scan dir files during get classes psr-4 from dir: ' . $dir);
|
||||
@@ -325,15 +361,15 @@ class FileSystem
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除目录及目录下的所有文件(危险操作)
|
||||
* Remove directory recursively
|
||||
*
|
||||
* @throws FileSystemException
|
||||
* @param string $dir Directory to remove
|
||||
* @return bool Success status
|
||||
*/
|
||||
public static function removeDir(string $dir): bool
|
||||
{
|
||||
$dir = FileSystem::convertPath($dir);
|
||||
logger()->debug('Removing path recursively: "' . $dir . '"');
|
||||
// 不是目录不扫,直接 false 处理
|
||||
if (!file_exists($dir)) {
|
||||
logger()->debug('Scan dir failed, no such file or directory.');
|
||||
return false;
|
||||
@@ -374,6 +410,9 @@ class FileSystem
|
||||
}
|
||||
|
||||
/**
|
||||
* Create directory recursively
|
||||
*
|
||||
* @param string $path Directory path to create
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function createDir(string $path): void
|
||||
@@ -384,7 +423,12 @@ class FileSystem
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed ...$args Arguments passed to file_put_contents
|
||||
* Write content to file
|
||||
*
|
||||
* @param string $path File path
|
||||
* @param mixed $content Content to write
|
||||
* @param mixed ...$args Additional arguments passed to file_put_contents
|
||||
* @return bool|int|string Result of file writing operation
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function writeFile(string $path, mixed $content, ...$args): bool|int|string
|
||||
@@ -397,27 +441,36 @@ class FileSystem
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset (remove recursively and create again) dir
|
||||
* Reset directory by removing and recreating it
|
||||
*
|
||||
* @param string $dir_name Directory name
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function resetDir(string $dir_name): void
|
||||
{
|
||||
$dir_name = self::convertPath($dir_name);
|
||||
if (is_dir($dir_name)) {
|
||||
self::removeDir($dir_name);
|
||||
}
|
||||
self::createDir($dir_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add source extraction hook
|
||||
*
|
||||
* @param string $name Source name
|
||||
* @param callable $callback Callback function
|
||||
*/
|
||||
public static function addSourceExtractHook(string $name, callable $callback): void
|
||||
{
|
||||
self::$_extract_hook[$name][] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the path is a relative path (judging according to whether the first character is "/")
|
||||
* Check if path is relative
|
||||
*
|
||||
* @param string $path Path
|
||||
* @param string $path Path to check
|
||||
* @return bool True if path is relative
|
||||
*/
|
||||
public static function isRelativePath(string $path): bool
|
||||
{
|
||||
@@ -427,6 +480,12 @@ class FileSystem
|
||||
return strlen($path) > 0 && $path[0] !== '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace path variables with actual values
|
||||
*
|
||||
* @param string $path Path with variables
|
||||
* @return string Path with replaced variables
|
||||
*/
|
||||
public static function replacePathVariable(string $path): string
|
||||
{
|
||||
$replacement = [
|
||||
@@ -439,12 +498,23 @@ class FileSystem
|
||||
return str_replace(array_keys($replacement), array_values($replacement), $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create backup of file
|
||||
*
|
||||
* @param string $path File path
|
||||
* @return string Backup file path
|
||||
*/
|
||||
public static function backupFile(string $path): string
|
||||
{
|
||||
copy($path, $path . '.bak');
|
||||
return $path . '.bak';
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore file from backup
|
||||
*
|
||||
* @param string $path Original file path
|
||||
*/
|
||||
public static function restoreBackupFile(string $path): void
|
||||
{
|
||||
if (!file_exists($path . '.bak')) {
|
||||
@@ -454,14 +524,26 @@ class FileSystem
|
||||
unlink($path . '.bak');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove file if it exists
|
||||
*
|
||||
* @param string $string File path
|
||||
*/
|
||||
public static function removeFileIfExists(string $string): void
|
||||
{
|
||||
$string = self::convertPath($string);
|
||||
if (file_exists($string)) {
|
||||
unlink($string);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace line in file that contains specific string
|
||||
*
|
||||
* @param string $file File path
|
||||
* @param string $find String to find in line
|
||||
* @param string $line New line content
|
||||
* @return false|int Number of replacements or false on failure
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function replaceFileLineContainsString(string $file, string $find, string $line): false|int
|
||||
|
||||
@@ -4,12 +4,36 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\store\pkg;
|
||||
|
||||
/**
|
||||
* Abstract base class for custom package implementations
|
||||
*
|
||||
* This class provides a framework for implementing custom package download
|
||||
* and extraction logic. Extend this class to create custom package handlers.
|
||||
*/
|
||||
abstract class CustomPackage
|
||||
{
|
||||
/**
|
||||
* Get the list of package names supported by this implementation
|
||||
*
|
||||
* @return array Array of supported package names
|
||||
*/
|
||||
abstract public function getSupportName(): array;
|
||||
|
||||
/**
|
||||
* Fetch the package from its source
|
||||
*
|
||||
* @param string $name Package name
|
||||
* @param bool $force Force download even if already exists
|
||||
* @param null|array $config Optional configuration array
|
||||
*/
|
||||
abstract public function fetch(string $name, bool $force = false, ?array $config = null): void;
|
||||
|
||||
/**
|
||||
* Extract the downloaded package
|
||||
*
|
||||
* @param string $name Package name
|
||||
* @throws \RuntimeException If extraction is not implemented
|
||||
*/
|
||||
public function extract(string $name): void
|
||||
{
|
||||
throw new \RuntimeException("Extract method not implemented for package: {$name}");
|
||||
|
||||
@@ -4,9 +4,25 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\store\source;
|
||||
|
||||
/**
|
||||
* Abstract base class for custom source implementations
|
||||
*
|
||||
* This class provides a framework for implementing custom source download
|
||||
* logic. Extend this class to create custom source handlers.
|
||||
*/
|
||||
abstract class CustomSourceBase
|
||||
{
|
||||
/**
|
||||
* The name of this source implementation
|
||||
*/
|
||||
public const NAME = 'unknown';
|
||||
|
||||
/**
|
||||
* Fetch the source from its repository
|
||||
*
|
||||
* @param bool $force Force download even if already exists
|
||||
* @param null|array $config Optional configuration array
|
||||
* @param int $lock_as Lock type constant
|
||||
*/
|
||||
abstract public function fetch(bool $force = false, ?array $config = null, int $lock_as = SPC_DOWNLOAD_SOURCE): void;
|
||||
}
|
||||
|
||||
@@ -4,15 +4,27 @@ declare(strict_types=1);
|
||||
|
||||
namespace SPC\toolchain;
|
||||
|
||||
/**
|
||||
* Interface for toolchain implementations
|
||||
*
|
||||
* This interface defines the contract for toolchain classes that handle
|
||||
* environment initialization and setup for different build targets.
|
||||
*/
|
||||
interface ToolchainInterface
|
||||
{
|
||||
/**
|
||||
* Initialize the environment for the given target.
|
||||
*
|
||||
* This method should set up any necessary environment variables,
|
||||
* paths, or configurations required for the build process.
|
||||
*/
|
||||
public function initEnv(): void;
|
||||
|
||||
/**
|
||||
* Perform actions after the environment has been initialized for the given target.
|
||||
*
|
||||
* This method is called after initEnv() and can be used for any
|
||||
* post-initialization setup or validation.
|
||||
*/
|
||||
public function afterInit(): void;
|
||||
}
|
||||
|
||||
@@ -20,33 +20,48 @@ class ConfigValidator
|
||||
public static function validateSource(array $data): void
|
||||
{
|
||||
foreach ($data as $name => $src) {
|
||||
isset($src['type']) || throw new ValidationException("source {$name} must have prop: [type]");
|
||||
is_string($src['type']) || throw new ValidationException("source {$name} type prop must be string");
|
||||
in_array($src['type'], ['filelist', 'git', 'ghtagtar', 'ghtar', 'ghrel', 'url', 'custom']) || throw new ValidationException("source {$name} type [{$src['type']}] is invalid");
|
||||
switch ($src['type']) {
|
||||
case 'filelist':
|
||||
isset($src['url'], $src['regex']) || throw new ValidationException("source {$name} needs [url] and [regex] props");
|
||||
is_string($src['url']) && is_string($src['regex']) || throw new ValidationException("source {$name} [url] and [regex] must be string");
|
||||
break;
|
||||
case 'git':
|
||||
isset($src['url'], $src['rev']) || throw new ValidationException("source {$name} needs [url] and [rev] props");
|
||||
is_string($src['url']) && is_string($src['rev']) || throw new ValidationException("source {$name} [url] and [rev] must be string");
|
||||
is_string($src['path'] ?? '') || throw new ValidationException("source {$name} [path] must be string");
|
||||
break;
|
||||
case 'ghtagtar':
|
||||
case 'ghtar':
|
||||
isset($src['repo']) || throw new ValidationException("source {$name} needs [repo] prop");
|
||||
is_string($src['repo']) || throw new ValidationException("source {$name} [repo] must be string");
|
||||
is_string($src['path'] ?? '') || throw new ValidationException("source {$name} [path] must be string");
|
||||
break;
|
||||
case 'ghrel':
|
||||
isset($src['repo'], $src['match']) || throw new ValidationException("source {$name} needs [repo] and [match] props");
|
||||
is_string($src['repo']) && is_string($src['match']) || throw new ValidationException("source {$name} [repo] and [match] must be string");
|
||||
break;
|
||||
case 'url':
|
||||
isset($src['url']) || throw new ValidationException("source {$name} needs [url] prop");
|
||||
is_string($src['url']) || throw new ValidationException("source {$name} [url] must be string");
|
||||
break;
|
||||
// Validate basic source type configuration
|
||||
self::validateSourceTypeConfig($src, $name, 'source');
|
||||
|
||||
// Check source-specific fields
|
||||
// check if alt is valid
|
||||
if (isset($src['alt'])) {
|
||||
if (!is_assoc_array($src['alt']) && !is_bool($src['alt'])) {
|
||||
throw new ValidationException("source {$name} alt must be object or boolean");
|
||||
}
|
||||
if (is_assoc_array($src['alt'])) {
|
||||
// validate alt source recursively
|
||||
self::validateSource([$name . '_alt' => $src['alt']]);
|
||||
}
|
||||
}
|
||||
|
||||
// check if provide-pre-built is boolean
|
||||
if (isset($src['provide-pre-built']) && !is_bool($src['provide-pre-built'])) {
|
||||
throw new ValidationException("source {$name} provide-pre-built must be boolean");
|
||||
}
|
||||
|
||||
// check if prefer-stable is boolean
|
||||
if (isset($src['prefer-stable']) && !is_bool($src['prefer-stable'])) {
|
||||
throw new ValidationException("source {$name} prefer-stable must be boolean");
|
||||
}
|
||||
|
||||
// check if license is valid
|
||||
if (isset($src['license'])) {
|
||||
if (!is_assoc_array($src['license'])) {
|
||||
throw new ValidationException("source {$name} license must be object");
|
||||
}
|
||||
if (!isset($src['license']['type'])) {
|
||||
throw new ValidationException("source {$name} license must have type");
|
||||
}
|
||||
if (!in_array($src['license']['type'], ['file', 'text'])) {
|
||||
throw new ValidationException("source {$name} license type is invalid");
|
||||
}
|
||||
if ($src['license']['type'] === 'file' && !isset($src['license']['path'])) {
|
||||
throw new ValidationException("source {$name} license file must have path");
|
||||
}
|
||||
if ($src['license']['type'] === 'text' && !isset($src['license']['text'])) {
|
||||
throw new ValidationException("source {$name} license text must have text");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,7 +93,11 @@ class ConfigValidator
|
||||
if (isset($lib['source']) && !empty($source_data) && !isset($source_data[$lib['source']])) {
|
||||
throw new ValidationException("lib {$name} assigns an invalid source: {$lib['source']}");
|
||||
}
|
||||
// check if [lib-depends|lib-suggests|static-libs][-windows|-unix|-macos|-linux] are valid list array
|
||||
// check if source is string
|
||||
if (isset($lib['source']) && !is_string($lib['source'])) {
|
||||
throw new ValidationException("lib {$name} source must be string");
|
||||
}
|
||||
// check if [lib-depends|lib-suggests|static-libs|headers|bin][-windows|-unix|-macos|-linux] are valid list array
|
||||
$suffixes = ['', '-windows', '-unix', '-macos', '-linux'];
|
||||
foreach ($suffixes as $suffix) {
|
||||
if (isset($lib['lib-depends' . $suffix]) && !is_list_array($lib['lib-depends' . $suffix])) {
|
||||
@@ -93,6 +112,12 @@ class ConfigValidator
|
||||
if (isset($lib['pkg-configs' . $suffix]) && !is_list_array($lib['pkg-configs' . $suffix])) {
|
||||
throw new ValidationException("lib {$name} pkg-configs must be a list");
|
||||
}
|
||||
if (isset($lib['headers' . $suffix]) && !is_list_array($lib['headers' . $suffix])) {
|
||||
throw new ValidationException("lib {$name} headers must be a list");
|
||||
}
|
||||
if (isset($lib['bin' . $suffix]) && !is_list_array($lib['bin' . $suffix])) {
|
||||
throw new ValidationException("lib {$name} bin must be a list");
|
||||
}
|
||||
}
|
||||
// check if frameworks is a list array
|
||||
if (isset($lib['frameworks']) && !is_list_array($lib['frameworks'])) {
|
||||
@@ -106,7 +131,65 @@ class ConfigValidator
|
||||
*/
|
||||
public static function validateExts(mixed $data): void
|
||||
{
|
||||
is_array($data) || throw new ValidationException('ext.json is broken');
|
||||
if (!is_array($data)) {
|
||||
throw new ValidationException('ext.json is broken');
|
||||
}
|
||||
// check each extension
|
||||
foreach ($data as $name => $ext) {
|
||||
// check if ext is an assoc array
|
||||
if (!is_assoc_array($ext)) {
|
||||
throw new ValidationException("ext {$name} is not an object");
|
||||
}
|
||||
// check if ext has valid type
|
||||
if (!in_array($ext['type'] ?? '', ['builtin', 'external', 'addon', 'wip'])) {
|
||||
throw new ValidationException("ext {$name} type is invalid");
|
||||
}
|
||||
// check if external ext has source
|
||||
if (($ext['type'] ?? '') === 'external' && !isset($ext['source'])) {
|
||||
throw new ValidationException("ext {$name} does not assign any source");
|
||||
}
|
||||
// check if source is string
|
||||
if (isset($ext['source']) && !is_string($ext['source'])) {
|
||||
throw new ValidationException("ext {$name} source must be string");
|
||||
}
|
||||
// check if support is valid
|
||||
if (isset($ext['support']) && !is_assoc_array($ext['support'])) {
|
||||
throw new ValidationException("ext {$name} support must be an object");
|
||||
}
|
||||
// check if notes is boolean
|
||||
if (isset($ext['notes']) && !is_bool($ext['notes'])) {
|
||||
throw new ValidationException("ext {$name} notes must be boolean");
|
||||
}
|
||||
// check if [lib-depends|lib-suggests|ext-depends][-windows|-unix|-macos|-linux] are valid list array
|
||||
$suffixes = ['', '-windows', '-unix', '-macos', '-linux'];
|
||||
foreach ($suffixes as $suffix) {
|
||||
if (isset($ext['lib-depends' . $suffix]) && !is_list_array($ext['lib-depends' . $suffix])) {
|
||||
throw new ValidationException("ext {$name} lib-depends must be a list");
|
||||
}
|
||||
if (isset($ext['lib-suggests' . $suffix]) && !is_list_array($ext['lib-suggests' . $suffix])) {
|
||||
throw new ValidationException("ext {$name} lib-suggests must be a list");
|
||||
}
|
||||
if (isset($ext['ext-depends' . $suffix]) && !is_list_array($ext['ext-depends' . $suffix])) {
|
||||
throw new ValidationException("ext {$name} ext-depends must be a list");
|
||||
}
|
||||
}
|
||||
// check if arg-type is valid
|
||||
if (isset($ext['arg-type'])) {
|
||||
$valid_arg_types = ['enable', 'with', 'with-path', 'custom', 'none', 'enable-path'];
|
||||
if (!in_array($ext['arg-type'], $valid_arg_types)) {
|
||||
throw new ValidationException("ext {$name} arg-type is invalid");
|
||||
}
|
||||
}
|
||||
// check if arg-type with suffix is valid
|
||||
foreach ($suffixes as $suffix) {
|
||||
if (isset($ext['arg-type' . $suffix])) {
|
||||
$valid_arg_types = ['enable', 'with', 'with-path', 'custom', 'none', 'enable-path'];
|
||||
if (!in_array($ext['arg-type' . $suffix], $valid_arg_types)) {
|
||||
throw new ValidationException("ext {$name} arg-type{$suffix} is invalid");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,7 +197,96 @@ class ConfigValidator
|
||||
*/
|
||||
public static function validatePkgs(mixed $data): void
|
||||
{
|
||||
is_array($data) || throw new ValidationException('pkg.json is broken');
|
||||
if (!is_array($data)) {
|
||||
throw new ValidationException('pkg.json is broken');
|
||||
}
|
||||
// check each package
|
||||
foreach ($data as $name => $pkg) {
|
||||
// check if pkg is an assoc array
|
||||
if (!is_assoc_array($pkg)) {
|
||||
throw new ValidationException("pkg {$name} is not an object");
|
||||
}
|
||||
|
||||
// Validate basic source type configuration (reuse from source validation)
|
||||
self::validateSourceTypeConfig($pkg, $name, 'pkg');
|
||||
|
||||
// Check pkg-specific fields
|
||||
// check if extract-files is valid
|
||||
if (isset($pkg['extract-files'])) {
|
||||
if (!is_assoc_array($pkg['extract-files'])) {
|
||||
throw new ValidationException("pkg {$name} extract-files must be an object");
|
||||
}
|
||||
// check each extract file mapping
|
||||
foreach ($pkg['extract-files'] as $source => $target) {
|
||||
if (!is_string($source) || !is_string($target)) {
|
||||
throw new ValidationException("pkg {$name} extract-files mapping must be string to string");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate pre-built.json configuration
|
||||
*
|
||||
* @param mixed $data pre-built.json loaded data
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public static function validatePreBuilt(mixed $data): void
|
||||
{
|
||||
if (!is_array($data)) {
|
||||
throw new ValidationException('pre-built.json is broken');
|
||||
}
|
||||
|
||||
// Check required fields
|
||||
if (!isset($data['repo'])) {
|
||||
throw new ValidationException('pre-built.json must have [repo] field');
|
||||
}
|
||||
if (!is_string($data['repo'])) {
|
||||
throw new ValidationException('pre-built.json [repo] must be string');
|
||||
}
|
||||
|
||||
// Check optional prefer-stable field
|
||||
if (isset($data['prefer-stable']) && !is_bool($data['prefer-stable'])) {
|
||||
throw new ValidationException('pre-built.json [prefer-stable] must be boolean');
|
||||
}
|
||||
|
||||
// Check match pattern fields (at least one must exist)
|
||||
$pattern_fields = ['match-pattern-linux', 'match-pattern-macos', 'match-pattern-windows'];
|
||||
$has_pattern = false;
|
||||
|
||||
foreach ($pattern_fields as $field) {
|
||||
if (isset($data[$field])) {
|
||||
$has_pattern = true;
|
||||
if (!is_string($data[$field])) {
|
||||
throw new ValidationException("pre-built.json [{$field}] must be string");
|
||||
}
|
||||
// Validate pattern contains required placeholders
|
||||
if (!str_contains($data[$field], '{name}')) {
|
||||
throw new ValidationException("pre-built.json [{$field}] must contain {name} placeholder");
|
||||
}
|
||||
if (!str_contains($data[$field], '{arch}')) {
|
||||
throw new ValidationException("pre-built.json [{$field}] must contain {arch} placeholder");
|
||||
}
|
||||
if (!str_contains($data[$field], '{os}')) {
|
||||
throw new ValidationException("pre-built.json [{$field}] must contain {os} placeholder");
|
||||
}
|
||||
|
||||
// Linux pattern should have libc-related placeholders
|
||||
if ($field === 'match-pattern-linux') {
|
||||
if (!str_contains($data[$field], '{libc}')) {
|
||||
throw new ValidationException('pre-built.json [match-pattern-linux] must contain {libc} placeholder');
|
||||
}
|
||||
if (!str_contains($data[$field], '{libcver}')) {
|
||||
throw new ValidationException('pre-built.json [match-pattern-linux] must contain {libcver} placeholder');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$has_pattern) {
|
||||
throw new ValidationException('pre-built.json must have at least one match-pattern field');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,4 +414,85 @@ class ConfigValidator
|
||||
$craft['craft-options']['build'] ??= true;
|
||||
return $craft;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate source type configuration (shared between source.json and pkg.json)
|
||||
*
|
||||
* @param array $item The source/package item to validate
|
||||
* @param string $name The name of the item for error messages
|
||||
* @param string $config_type The type of config file ("source" or "pkg")
|
||||
* @throws ValidationException
|
||||
*/
|
||||
private static function validateSourceTypeConfig(array $item, string $name, string $config_type): void
|
||||
{
|
||||
if (!isset($item['type'])) {
|
||||
throw new ValidationException("{$config_type} {$name} must have prop: [type]");
|
||||
}
|
||||
if (!is_string($item['type'])) {
|
||||
throw new ValidationException("{$config_type} {$name} type prop must be string");
|
||||
}
|
||||
if (!in_array($item['type'], ['filelist', 'git', 'ghtagtar', 'ghtar', 'ghrel', 'url', 'custom'])) {
|
||||
throw new ValidationException("{$config_type} {$name} type [{$item['type']}] is invalid");
|
||||
}
|
||||
|
||||
// Validate type-specific requirements
|
||||
switch ($item['type']) {
|
||||
case 'filelist':
|
||||
if (!isset($item['url'], $item['regex'])) {
|
||||
throw new ValidationException("{$config_type} {$name} needs [url] and [regex] props");
|
||||
}
|
||||
if (!is_string($item['url']) || !is_string($item['regex'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [url] and [regex] must be string");
|
||||
}
|
||||
break;
|
||||
case 'git':
|
||||
if (!isset($item['url'], $item['rev'])) {
|
||||
throw new ValidationException("{$config_type} {$name} needs [url] and [rev] props");
|
||||
}
|
||||
if (!is_string($item['url']) || !is_string($item['rev'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [url] and [rev] must be string");
|
||||
}
|
||||
if (isset($item['path']) && !is_string($item['path'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [path] must be string");
|
||||
}
|
||||
break;
|
||||
case 'ghtagtar':
|
||||
case 'ghtar':
|
||||
if (!isset($item['repo'])) {
|
||||
throw new ValidationException("{$config_type} {$name} needs [repo] prop");
|
||||
}
|
||||
if (!is_string($item['repo'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [repo] must be string");
|
||||
}
|
||||
if (isset($item['path']) && !is_string($item['path'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [path] must be string");
|
||||
}
|
||||
break;
|
||||
case 'ghrel':
|
||||
if (!isset($item['repo'], $item['match'])) {
|
||||
throw new ValidationException("{$config_type} {$name} needs [repo] and [match] props");
|
||||
}
|
||||
if (!is_string($item['repo']) || !is_string($item['match'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [repo] and [match] must be string");
|
||||
}
|
||||
break;
|
||||
case 'url':
|
||||
if (!isset($item['url'])) {
|
||||
throw new ValidationException("{$config_type} {$name} needs [url] prop");
|
||||
}
|
||||
if (!is_string($item['url'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [url] must be string");
|
||||
}
|
||||
if (isset($item['filename']) && !is_string($item['filename'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [filename] must be string");
|
||||
}
|
||||
if (isset($item['path']) && !is_string($item['path'])) {
|
||||
throw new ValidationException("{$config_type} {$name} [path] must be string");
|
||||
}
|
||||
break;
|
||||
case 'custom':
|
||||
// custom type has no specific requirements
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,16 +8,30 @@ use SPC\builder\Extension;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\store\FileSystem;
|
||||
|
||||
/**
|
||||
* Custom extension attribute and manager
|
||||
*
|
||||
* This class provides functionality to register and manage custom PHP extensions
|
||||
* that can be used during the build process.
|
||||
*/
|
||||
#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_CLASS)]
|
||||
class CustomExt
|
||||
{
|
||||
private static array $custom_ext_class = [];
|
||||
|
||||
/**
|
||||
* Constructor for custom extension attribute
|
||||
*
|
||||
* @param string $ext_name The extension name
|
||||
*/
|
||||
public function __construct(protected string $ext_name) {}
|
||||
|
||||
/**
|
||||
* Load all custom extension classes
|
||||
*
|
||||
* This method scans the extension directory and registers all classes
|
||||
* that have the CustomExt attribute.
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
@@ -32,6 +46,12 @@ class CustomExt
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class name for a custom extension
|
||||
*
|
||||
* @param string $ext_name The extension name
|
||||
* @return string The class name for the extension
|
||||
*/
|
||||
public static function getExtClass(string $ext_name): string
|
||||
{
|
||||
return self::$custom_ext_class[$ext_name] ?? Extension::class;
|
||||
|
||||
@@ -9,13 +9,25 @@ use SPC\exception\WrongUsageException;
|
||||
use SPC\store\Config;
|
||||
|
||||
/**
|
||||
* Dependency processing tool class, including processing extensions, library dependency list order, etc.
|
||||
* Dependency processing tool class
|
||||
*
|
||||
* This class handles processing extensions, library dependency list ordering, etc.
|
||||
* It provides utilities for managing dependencies between extensions and libraries.
|
||||
*/
|
||||
class DependencyUtil
|
||||
{
|
||||
/**
|
||||
* Convert platform extensions to library dependencies and suggestions.
|
||||
* Convert platform extensions to library dependencies and suggestions
|
||||
*
|
||||
* This method processes all extensions and libraries to create a comprehensive
|
||||
* dependency map that can be used for build ordering.
|
||||
*
|
||||
* Returns an associative array where the key is the extension or library name (string),
|
||||
* and the value is an array with two keys:
|
||||
* - 'depends': array of dependency names (string)
|
||||
* - 'suggests': array of suggested dependency names (string)
|
||||
*
|
||||
* @return array<string, array{depends: array<int, string>, suggests: array<int, string>}>
|
||||
* @throws WrongUsageException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
@@ -53,6 +65,10 @@ class DependencyUtil
|
||||
}
|
||||
|
||||
/**
|
||||
* Get library dependencies in correct order
|
||||
*
|
||||
* @param array $libs Array of library names
|
||||
* @return array Ordered array of library names
|
||||
* @throws WrongUsageException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
@@ -88,7 +104,13 @@ class DependencyUtil
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException|WrongUsageException
|
||||
* Get extension dependencies in correct order
|
||||
*
|
||||
* @param array $exts Array of extension names
|
||||
* @param array $additional_libs Array of additional libraries
|
||||
* @return array Ordered array of extension names
|
||||
* @throws WrongUsageException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function getExtsAndLibs(array $exts, array $additional_libs = [], bool $include_suggested_exts = false, bool $include_suggested_libs = false): array
|
||||
{
|
||||
@@ -155,9 +177,6 @@ class DependencyUtil
|
||||
return [$exts_final, $libs_final, $not_included_final];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws WrongUsageException
|
||||
*/
|
||||
private static function doVisitPlat(array $deps, array $dep_list): array
|
||||
{
|
||||
// default: get extension exts and libs sorted by dep_list
|
||||
|
||||
@@ -6,15 +6,23 @@ namespace SPC\util;
|
||||
|
||||
use SPC\exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* Utility class for pkg-config operations
|
||||
*
|
||||
* This class provides methods to interact with pkg-config to get
|
||||
* compilation flags and library information for building extensions.
|
||||
*/
|
||||
class PkgConfigUtil
|
||||
{
|
||||
/**
|
||||
* Returns --cflags-only-other output.
|
||||
* Get CFLAGS from pkg-config
|
||||
*
|
||||
* Returns --cflags-only-other output from pkg-config.
|
||||
* The reason we return the string is we cannot use array_unique() on cflags,
|
||||
* some cflags may contains spaces.
|
||||
*
|
||||
* @param string $pkg_config_str .pc file str, accepts multiple files
|
||||
* @return string cflags string, e.g. "-Wno-implicit-int-float-conversion ..."
|
||||
* @param string $pkg_config_str .pc file string, accepts multiple files
|
||||
* @return string CFLAGS string, e.g. "-Wno-implicit-int-float-conversion ..."
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function getCflags(string $pkg_config_str): string
|
||||
@@ -25,10 +33,12 @@ class PkgConfigUtil
|
||||
}
|
||||
|
||||
/**
|
||||
* Get library flags from pkg-config
|
||||
*
|
||||
* Returns --libs-only-l and --libs-only-other output.
|
||||
* The reason we return the array is to avoid duplicate lib defines.
|
||||
*
|
||||
* @param string $pkg_config_str .pc file str, accepts multiple files
|
||||
* @param string $pkg_config_str .pc file string, accepts multiple files
|
||||
* @return array Unique libs array, e.g. [-lz, -lxml, ...]
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
@@ -64,6 +74,13 @@ class PkgConfigUtil
|
||||
return array_reverse(array_unique(array_reverse($libs)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute pkg-config command and return result
|
||||
*
|
||||
* @param string $cmd The pkg-config command to execute
|
||||
* @return string The command output
|
||||
* @throws RuntimeException If command fails
|
||||
*/
|
||||
private static function execWithResult(string $cmd): string
|
||||
{
|
||||
f_exec($cmd, $output, $result_code);
|
||||
|
||||
Reference in New Issue
Block a user