mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-17 05:45:35 +08:00
Compare commits
24 Commits
v3
...
fix/spccon
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d600530813 | ||
|
|
9cad5fb557 | ||
|
|
a3d34df152 | ||
|
|
01a1db85b1 | ||
|
|
8a7cf24b70 | ||
|
|
0f92692f4e | ||
|
|
977ea4a8f8 | ||
|
|
df0bc83aeb | ||
|
|
42c343d44d | ||
|
|
0de02d743d | ||
|
|
873222299b | ||
|
|
b46bc39a3d | ||
|
|
521b949d2c | ||
|
|
7ff2ccb81e | ||
|
|
d6b2b96aa8 | ||
|
|
fbbcc80126 | ||
|
|
b872c425e7 | ||
|
|
3e48d8fea2 | ||
|
|
fb4d151c08 | ||
|
|
21c624eaa5 | ||
|
|
5d76b852cf | ||
|
|
768e1a048c | ||
|
|
6bf0ea18ca | ||
|
|
db83778663 |
@@ -100,9 +100,10 @@ SPC_TARGET=${GNU_ARCH}-linux-musl
|
||||
CC=${SPC_DEFAULT_CC}
|
||||
CXX=${SPC_DEFAULT_CXX}
|
||||
AR=${SPC_DEFAULT_AR}
|
||||
RANLIB=${SPC_DEFAULT_RANLIB}
|
||||
LD=${SPC_DEFAULT_LD}
|
||||
; default compiler flags, used in CMake toolchain file, openssl and pkg-config build
|
||||
SPC_DEFAULT_CFLAGS="-fPIC -O3 -pipe -fno-plt -fno-semantic-interposition -fstack-clash-protection -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffunction-sections -fdata-sections"
|
||||
SPC_DEFAULT_CFLAGS="-fPIC -O3 -pipe -fno-plt -fno-semantic-interposition -fstack-clash-protection -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffunction-sections -fdata-sections -Wno-unused-command-line-argument"
|
||||
SPC_DEFAULT_CXXFLAGS="${SPC_DEFAULT_CFLAGS}"
|
||||
SPC_DEFAULT_LDFLAGS="-Wl,-z,relro -Wl,--as-needed -Wl,-z,now -Wl,-z,noexecstack -Wl,--gc-sections"
|
||||
; upx executable path
|
||||
@@ -125,6 +126,8 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fno-ident -fPIE
|
||||
SPC_CMD_VAR_PHP_MAKE_EXTRA_CXXFLAGS="-g -fstack-protector-strong -fno-ident -fPIE -fvisibility=hidden -fvisibility-inlines-hidden ${SPC_DEFAULT_CXXFLAGS}"
|
||||
; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.so
|
||||
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS=""
|
||||
; EXTRA_LDFLAGS_PROGRAM for `make` php; appended only to SAPI executable links (cli/fpm/cgi/micro/embed). Used by PGO to inject -fprofile-use= without polluting libphp.{a,so}.
|
||||
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM=""
|
||||
|
||||
; optional, path to openssl conf. This affects where openssl will look for the default CA.
|
||||
; default on Debian/Alpine: /etc/ssl, default on RHEL: /etc/pki/tls
|
||||
@@ -140,6 +143,7 @@ SPC_USE_LLVM=system
|
||||
CC=${SPC_DEFAULT_CC}
|
||||
CXX=${SPC_DEFAULT_CXX}
|
||||
AR=${SPC_DEFAULT_AR}
|
||||
RANLIB=${SPC_DEFAULT_RANLIB}
|
||||
LD=${SPC_DEFAULT_LD}
|
||||
; default compiler flags, used in CMake toolchain file, openssl and pkg-config build
|
||||
SPC_DEFAULT_CFLAGS="--target=${MAC_ARCH}-apple-darwin -O3 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffunction-sections -fdata-sections"
|
||||
@@ -163,5 +167,7 @@ SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS="-g -fstack-protector-strong -fpic -fpie -fvis
|
||||
SPC_CMD_VAR_PHP_MAKE_EXTRA_CXXFLAGS="-g -fstack-protector-strong -fno-ident -fpie -fvisibility=hidden -fvisibility-inlines-hidden -Werror=unknown-warning-option ${SPC_DEFAULT_CXXFLAGS}"
|
||||
; EXTRA_LDFLAGS for `make` php, can use -release to set a soname for libphp.dylib
|
||||
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS=""
|
||||
; EXTRA_LDFLAGS_PROGRAM for `make` php; appended only to SAPI executable links (cli/fpm/cgi/micro/embed). Used by PGO to inject -fprofile-use= without polluting libphp.{a,dylib}.
|
||||
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM=""
|
||||
; minimum compatible macOS version (LLVM vars, availability not guaranteed)
|
||||
MACOSX_DEPLOYMENT_TARGET=12.0
|
||||
|
||||
@@ -2,12 +2,13 @@ ext-brotli:
|
||||
type: php-extension
|
||||
artifact:
|
||||
source:
|
||||
type: git
|
||||
type: ghtagtar
|
||||
repo: kjdev/php-ext-brotli
|
||||
extract: php-src/ext/brotli
|
||||
rev: master
|
||||
url: 'https://github.com/kjdev/php-ext-brotli'
|
||||
metadata:
|
||||
license-files: [LICENSE]
|
||||
license: MIT
|
||||
depends:
|
||||
- brotli
|
||||
php-extension:
|
||||
arg-type: '--enable-brotli@shared_suffix@ --with-libbrotli'
|
||||
|
||||
@@ -2,9 +2,8 @@ ext-zstd:
|
||||
type: php-extension
|
||||
artifact:
|
||||
source:
|
||||
type: git
|
||||
url: 'https://github.com/kjdev/php-ext-zstd'
|
||||
rev: master
|
||||
type: ghtar
|
||||
repo: kjdev/php-ext-zstd
|
||||
extract: php-src/ext/zstd
|
||||
metadata:
|
||||
license-files: [LICENSE]
|
||||
|
||||
@@ -2,8 +2,9 @@ gmssl:
|
||||
type: library
|
||||
artifact:
|
||||
source:
|
||||
type: ghtar
|
||||
repo: guanzhi/GmSSL
|
||||
type: git
|
||||
url: 'https://github.com/guanzhi/GmSSL.git'
|
||||
rev: master
|
||||
metadata:
|
||||
license-files: [LICENSE]
|
||||
license: Apache-2.0
|
||||
|
||||
@@ -17,4 +17,11 @@ class gmssl
|
||||
FileSystem::replaceFileStr($target_path . '/src/hex.c', 'unsigned char *OPENSSL_hexstr2buf(const char *str, size_t *len)', 'unsigned char *GMSSL_hexstr2buf(const char *str, size_t *len)');
|
||||
FileSystem::replaceFileStr($target_path . '/src/hex.c', 'OPENSSL_hexchar2int', 'GMSSL_hexchar2int');
|
||||
}
|
||||
|
||||
#[AfterSourceExtract('ext-gmssl')]
|
||||
#[PatchDescription('Rename pbkdf2_hmac_sm3_genkey() to sm3_pbkdf2() (renamed upstream in GmSSL 3.2+; identical signature)')]
|
||||
public function patchExt(string $target_path): void
|
||||
{
|
||||
FileSystem::replaceFileStr($target_path . '/gmssl.c', 'pbkdf2_hmac_sm3_genkey(', 'sm3_pbkdf2(');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ class go_xcaddy
|
||||
])]
|
||||
public function downBinary(ArtifactDownloader $downloader): DownloadResult
|
||||
{
|
||||
$pkgroot = PKG_ROOT_PATH;
|
||||
$name = SystemTarget::getCurrentPlatformString();
|
||||
$arch = match (explode('-', $name)[1]) {
|
||||
'x86_64' => 'amd64',
|
||||
@@ -64,7 +63,7 @@ class go_xcaddy
|
||||
if ($file_hash !== $hash) {
|
||||
throw new DownloaderException("Hash mismatch for downloaded go-xcaddy binary. Expected {$hash}, got {$file_hash}");
|
||||
}
|
||||
return DownloadResult::archive(basename($path), ['url' => $url, 'version' => $version], extract: "{$pkgroot}/go-xcaddy", verified: true, version: $version);
|
||||
return DownloadResult::archive(basename($path), ['url' => $url, 'version' => $version], extract: '{pkg_root_path}/go-xcaddy', verified: true, version: $version);
|
||||
}
|
||||
|
||||
#[CustomBinaryCheckUpdate('go-xcaddy', [
|
||||
@@ -109,7 +108,7 @@ class go_xcaddy
|
||||
'GOROOT' => "{$target_path}",
|
||||
'GOBIN' => "{$target_path}/bin",
|
||||
'GOPATH' => "{$target_path}/go",
|
||||
])->exec('CC=cc go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest');
|
||||
])->exec('CGO_ENABLED=0 go install github.com/caddyserver/xcaddy/cmd/xcaddy@master');
|
||||
GlobalEnvManager::addPathIfNotExists("{$target_path}/bin");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,12 +35,37 @@ class event extends PhpExtensionPackage
|
||||
|
||||
#[BeforeStage('php', [php::class, 'makeForUnix'], 'ext-event')]
|
||||
#[PatchDescription('Prevent event extension compile error on macOS')]
|
||||
#[PatchDescription('Patch libevent http_connection.c to use a const peer address')]
|
||||
public function patchBeforeMake(PackageInstaller $installer): void
|
||||
{
|
||||
$php_src = $installer->getTargetPackage('php')->getSourceDir();
|
||||
// Prevent event extension compile error on macOS
|
||||
if (SystemTarget::getTargetOS() === 'Darwin') {
|
||||
$php_src = $installer->getTargetPackage('php')->getSourceDir();
|
||||
FileSystem::replaceFileRegex("{$php_src}/main/php_config.h", '/^#define HAVE_OPENPTY 1$/m', '');
|
||||
}
|
||||
$this->patchLibeventConstPeer("{$php_src}/ext/event");
|
||||
}
|
||||
|
||||
#[BeforeStage('ext-event', [PhpExtensionPackage::class, 'makeForUnix'])]
|
||||
#[PatchDescription('Patch libevent http_connection.c to use a const peer address')]
|
||||
public function patchBeforeSharedMake(PhpExtensionPackage $pkg): void
|
||||
{
|
||||
$this->patchLibeventConstPeer($pkg->getSourceDir());
|
||||
}
|
||||
|
||||
private function patchLibeventConstPeer(string $event_source_dir): bool
|
||||
{
|
||||
$file = "{$event_source_dir}/php8/classes/http_connection.c";
|
||||
if (!is_file($file)) {
|
||||
return false;
|
||||
}
|
||||
// libevent >= 2.2 changed evhttp_connection_get_peer()'s
|
||||
// address arg to `const char **`
|
||||
$header = $this->getBuilder()->getBuildRootPath() . '/include/event2/http.h';
|
||||
$isConst = is_file($header)
|
||||
&& preg_match('/evhttp_connection_get_peer\s*\([^;]*\bconst\s+char\s*\*\*\s*address/s', file_get_contents($header)) === 1;
|
||||
return (bool) ($isConst
|
||||
? FileSystem::replaceFileRegex($file, '/^\tchar \*address;$/m', "\tconst char *address;")
|
||||
: FileSystem::replaceFileRegex($file, '/^\tconst char \*address;$/m', "\tchar *address;"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,8 @@ class rdkafka extends PhpExtensionPackage
|
||||
#[CustomPhpConfigureArg('Linux')]
|
||||
public function getUnixConfigureArg(bool $shared, PackageBuilder $builder): string
|
||||
{
|
||||
$pkgconf_libs = new SPCConfigUtil(['no_php' => true, 'libs_only_deps' => true])->getExtensionConfig($this);
|
||||
$pkgconf_libs = new SPCConfigUtil(['no_php' => true, 'libs_only_deps' => true])
|
||||
->configForResolvedBuild([$this->getName()], $this->getInstaller());
|
||||
return '--with-rdkafka=' . ($shared ? 'shared,' : '') . $builder->getBuildRootPath() . " RDKAFKA_LIBS=\"{$pkgconf_libs['libs']}\"";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,8 @@ class swoole extends PhpExtensionPackage
|
||||
$arg .= $installer->getPhpExtensionPackage('swoole-hook-mysql') ? ' --enable-mysqlnd' : ' --disable-mysqlnd';
|
||||
$arg .= $installer->getPhpExtensionPackage('swoole-hook-sqlite') ? ' --enable-swoole-sqlite' : ' --disable-swoole-sqlite';
|
||||
if ($installer->getPhpExtensionPackage('swoole-hook-odbc')) {
|
||||
$config = new SPCConfigUtil()->getLibraryConfig($installer->getLibraryPackage('unixodbc'));
|
||||
$config = new SPCConfigUtil(['no_php' => true, 'libs_only_deps' => true])
|
||||
->configForResolvedBuild(['unixodbc'], $installer);
|
||||
$arg .= " --with-swoole-odbc=unixODBC,{$builder->getBuildRootPath()} SWOOLE_ODBC_LIBS=\"{$config['libs']}\"";
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ class bzip2
|
||||
#[PatchBeforeBuild]
|
||||
public function patchBeforeBuild(LibraryPackage $lib): void
|
||||
{
|
||||
FileSystem::replaceFileStr($lib->getSourceDir() . '/Makefile', 'CFLAGS=-Wall', 'CFLAGS=-fPIC -Wall');
|
||||
// Makefile pins -O2 -fPIC; inject SPC_DEFAULT_CFLAGS
|
||||
$extra = deduplicate_flags(trim((string) getenv('SPC_DEFAULT_CFLAGS')) . ' -fPIC -Wall');
|
||||
FileSystem::replaceFileStr($lib->getSourceDir() . '/Makefile', 'CFLAGS=-Wall', "CFLAGS={$extra}");
|
||||
}
|
||||
|
||||
#[BuildFor('Windows')]
|
||||
|
||||
@@ -18,9 +18,11 @@ class fastlz
|
||||
{
|
||||
$cc = getenv('CC') ?: 'cc';
|
||||
$ar = getenv('AR') ?: 'ar';
|
||||
$extra = trim((string) getenv('SPC_DEFAULT_CFLAGS'));
|
||||
$extra = $extra !== '' ? $extra . ' -fPIC' : '-O3 -fPIC';
|
||||
|
||||
shell()->cd($lib->getSourceDir())->initializeEnv($lib)
|
||||
->exec("{$cc} -c -O3 -fPIC fastlz.c -o fastlz.o")
|
||||
->exec("{$cc} -c {$extra} fastlz.c -o fastlz.o")
|
||||
->exec("{$ar} rcs libfastlz.a fastlz.o");
|
||||
|
||||
// Copy header file
|
||||
|
||||
@@ -24,9 +24,12 @@ class icu
|
||||
#[BuildFor('Linux')]
|
||||
public function buildLinux(LibraryPackage $lib, ToolchainInterface $toolchain, PackageBuilder $builder): void
|
||||
{
|
||||
// runConfigureICU bakes CXXFLAGS/LDFLAGS, apply user flags too
|
||||
$userCxxFlags = trim((string) getenv('SPC_DEFAULT_CXXFLAGS'));
|
||||
$userLdFlags = trim((string) getenv('SPC_DEFAULT_LDFLAGS'));
|
||||
$cppflags = 'CPPFLAGS="-DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=1 -DU_STATIC_IMPLEMENTATION=1 -DPIC -fPIC"';
|
||||
$cxxflags = 'CXXFLAGS="-std=c++17 -DPIC -fPIC -fno-ident"';
|
||||
$ldflags = $toolchain->isStatic() ? 'LDFLAGS="-static"' : '';
|
||||
$cxxflags = "CXXFLAGS=\"-std=c++17 -DPIC -fPIC -fno-ident {$userCxxFlags}\"";
|
||||
$ldflags = $toolchain->isStatic() ? "LDFLAGS=\"-static {$userLdFlags}\"" : "LDFLAGS=\"{$userLdFlags}\"";
|
||||
shell()->cd($lib->getSourceDir() . '/source')->initializeEnv($lib)
|
||||
->exec(
|
||||
"{$cppflags} {$cxxflags} {$ldflags} " .
|
||||
|
||||
@@ -17,7 +17,9 @@ class jbig
|
||||
#[PatchBeforeBuild]
|
||||
public function patchBeforeBuild(LibraryPackage $lib): void
|
||||
{
|
||||
FileSystem::replaceFileStr($lib->getSourceDir() . '/Makefile', 'CFLAGS = -O2 -W -Wno-unused-result', 'CFLAGS = -O2 -W -Wno-unused-result -fPIC');
|
||||
$extra = trim((string) getenv('SPC_DEFAULT_CFLAGS'));
|
||||
$cflags = ($extra !== '' ? $extra : '-O2') . ' -W -Wno-unused-result -fPIC';
|
||||
FileSystem::replaceFileStr($lib->getSourceDir() . '/Makefile', 'CFLAGS = -O2 -W -Wno-unused-result', "CFLAGS = {$cflags}");
|
||||
}
|
||||
|
||||
#[BuildFor('Darwin')]
|
||||
|
||||
@@ -27,7 +27,7 @@ class krb5
|
||||
|
||||
$resolved = array_keys($installer->getResolvedPackages());
|
||||
$spc = new SPCConfigUtil(['no_php' => true, 'libs_only_deps' => true]);
|
||||
$config = $spc->getPackageDepsConfig($lib->getName(), $resolved, include_suggests: true);
|
||||
$config = $spc->getPackageDepsConfig($lib->getName(), $resolved);
|
||||
$extraEnv = [
|
||||
'CFLAGS' => '-fcommon',
|
||||
'LIBS' => $config['libs'],
|
||||
|
||||
@@ -9,8 +9,10 @@ use StaticPHP\Attribute\Package\Library;
|
||||
use StaticPHP\Package\LibraryPackage;
|
||||
use StaticPHP\Runtime\Executor\UnixCMakeExecutor;
|
||||
use StaticPHP\Runtime\Executor\WindowsCMakeExecutor;
|
||||
use StaticPHP\Runtime\SystemTarget;
|
||||
use StaticPHP\Toolchain\Interface\ToolchainInterface;
|
||||
use StaticPHP\Toolchain\ZigToolchain;
|
||||
use StaticPHP\Util\System\UnixUtil;
|
||||
|
||||
#[Library('libaom')]
|
||||
class libaom extends LibraryPackage
|
||||
@@ -39,9 +41,27 @@ class libaom extends LibraryPackage
|
||||
$new = trim($extra . ' -D_GNU_SOURCE');
|
||||
f_putenv("SPC_COMPILER_EXTRA={$new}");
|
||||
}
|
||||
$targetCpu = SystemTarget::getTargetArch();
|
||||
if (str_starts_with($targetCpu, 'aarch')) {
|
||||
$targetCpu = str_replace('aarch', 'arm', $targetCpu);
|
||||
}
|
||||
if (
|
||||
in_array($targetCpu, ['x86', 'x86_64'], true)
|
||||
&& !UnixUtil::findCommand('nasm')
|
||||
&& !UnixUtil::findCommand('yasm')
|
||||
) {
|
||||
$targetCpu = 'generic';
|
||||
}
|
||||
UnixCMakeExecutor::create($this)
|
||||
->setBuildDir("{$this->getSourceDir()}/builddir")
|
||||
->addConfigureArgs('-DAOM_TARGET_CPU=generic')
|
||||
->addConfigureArgs(
|
||||
"-DAOM_TARGET_CPU={$targetCpu}",
|
||||
'-DCONFIG_RUNTIME_CPU_DETECT=1',
|
||||
'-DENABLE_EXAMPLES=OFF',
|
||||
'-DENABLE_TESTS=OFF',
|
||||
'-DENABLE_TOOLS=OFF',
|
||||
'-DENABLE_DOCS=OFF',
|
||||
)
|
||||
->build();
|
||||
f_putenv("SPC_COMPILER_EXTRA={$extra}");
|
||||
$this->patchPkgconfPrefix(['aom.pc']);
|
||||
|
||||
@@ -8,6 +8,7 @@ use StaticPHP\Attribute\Package\BuildFor;
|
||||
use StaticPHP\Attribute\Package\Library;
|
||||
use StaticPHP\Package\LibraryPackage;
|
||||
use StaticPHP\Runtime\Executor\UnixAutoconfExecutor;
|
||||
use StaticPHP\Runtime\SystemTarget;
|
||||
|
||||
#[Library('libffi')]
|
||||
class libffi extends LibraryPackage
|
||||
@@ -28,7 +29,7 @@ class libffi extends LibraryPackage
|
||||
#[BuildFor('Darwin')]
|
||||
public function buildDarwin(): void
|
||||
{
|
||||
$arch = getenv('SPC_ARCH');
|
||||
$arch = SystemTarget::getTargetArch();
|
||||
UnixAutoconfExecutor::create($this)
|
||||
->configure(
|
||||
"--host={$arch}-apple-darwin",
|
||||
|
||||
@@ -24,6 +24,17 @@ class libheif
|
||||
'list(APPEND REQUIRES_PRIVATE "libbrotlidec")' . "\n" . ' list(APPEND REQUIRES_PRIVATE "libbrotlienc")'
|
||||
);
|
||||
}
|
||||
// libheif 1.22+ ships a C-incompatible header: `struct heif_bad_pixel`
|
||||
$heif_properties = $lib->getSourceDir() . '/libheif/api/libheif/heif_properties.h';
|
||||
if (file_exists($heif_properties)
|
||||
&& str_contains(file_get_contents($heif_properties), 'struct heif_bad_pixel { uint32_t row; uint32_t column; };')
|
||||
) {
|
||||
FileSystem::replaceFileStr(
|
||||
$heif_properties,
|
||||
'struct heif_bad_pixel { uint32_t row; uint32_t column; };',
|
||||
'typedef struct heif_bad_pixel { uint32_t row; uint32_t column; } heif_bad_pixel;'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[BuildFor('Darwin')]
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Package\Library;
|
||||
|
||||
use StaticPHP\Attribute\Package\BuildFor;
|
||||
use StaticPHP\Attribute\Package\Library;
|
||||
use StaticPHP\Attribute\Package\PatchBeforeBuild;
|
||||
use StaticPHP\Attribute\PatchDescription;
|
||||
use StaticPHP\Package\LibraryPackage;
|
||||
use StaticPHP\Runtime\Executor\UnixAutoconfExecutor;
|
||||
use StaticPHP\Toolchain\Interface\ToolchainInterface;
|
||||
@@ -16,6 +18,24 @@ use StaticPHP\Util\FileSystem;
|
||||
#[Library('ncursesw')]
|
||||
class ncurses
|
||||
{
|
||||
#[PatchBeforeBuild]
|
||||
#[PatchDescription('Filter clang/zig "N warning(s) generated." line out of MKlib_gen.sh preprocessor pipe')]
|
||||
public function patchBeforeBuild(LibraryPackage $lib): void
|
||||
{
|
||||
// MKlib_gen.sh feeds the C preprocessor's stdout through a sed/awk
|
||||
// pipeline into lib_gen.c. zig-cc/clang emits "N warning(s) generated."
|
||||
// on stdout (not stderr), and that line ends up as invalid C in the
|
||||
// generated source. Filter it out of the pipe before sed sees it.
|
||||
$mklibGen = $lib->getSourceDir() . '/ncurses/base/MKlib_gen.sh';
|
||||
if (is_file($mklibGen) && !str_contains((string) file_get_contents($mklibGen), "| grep -v ' generated")) {
|
||||
FileSystem::replaceFileStr(
|
||||
$mklibGen,
|
||||
'$preprocessor $TMP 2>/dev/null \\',
|
||||
"\$preprocessor \$TMP 2>/dev/null \\\n| grep -v ' generated\\.\$' \\",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[BuildFor('Darwin')]
|
||||
#[BuildFor('Linux')]
|
||||
public function build(LibraryPackage $package, ToolchainInterface $toolchain): void
|
||||
@@ -45,6 +65,7 @@ class ncurses
|
||||
'--without-tests',
|
||||
'--without-dlsym',
|
||||
'--without-debug',
|
||||
'--disable-stripping',
|
||||
'--enable-symlinks',
|
||||
"--with-terminfo-dirs={$terminfo_dirs}",
|
||||
"--bindir={$package->getBinDir()}",
|
||||
|
||||
@@ -11,6 +11,7 @@ use StaticPHP\Package\LibraryPackage;
|
||||
use StaticPHP\Package\PackageBuilder;
|
||||
use StaticPHP\Package\ToolPackage;
|
||||
use StaticPHP\Registry\PackageLoader;
|
||||
use StaticPHP\Runtime\SystemTarget;
|
||||
use StaticPHP\Util\FileSystem;
|
||||
use StaticPHP\Util\System\LinuxUtil;
|
||||
|
||||
@@ -57,7 +58,11 @@ class openssl
|
||||
public function buildForDarwin(LibraryPackage $pkg): void
|
||||
{
|
||||
$zlib_libs = $pkg->getInstaller()->getLibraryPackage('zlib')->getStaticLibFiles();
|
||||
$arch = getenv('SPC_ARCH');
|
||||
$targetArch = SystemTarget::getTargetArch();
|
||||
$arch = match ($targetArch) {
|
||||
'aarch64' => 'arm64',
|
||||
default => $targetArch,
|
||||
};
|
||||
|
||||
shell()->cd($pkg->getSourceDir())->initializeEnv($pkg)
|
||||
->exec(
|
||||
@@ -76,12 +81,7 @@ class openssl
|
||||
#[BuildFor('Linux')]
|
||||
public function build(LibraryPackage $lib): void
|
||||
{
|
||||
$arch = getenv('SPC_ARCH');
|
||||
|
||||
$env = "CC='" . getenv('CC') . ' -idirafter ' . BUILD_INCLUDE_PATH .
|
||||
' -idirafter /usr/include/ ' .
|
||||
' -idirafter /usr/include/' . getenv('SPC_ARCH') . '-linux-gnu/ ' .
|
||||
"' ";
|
||||
$arch = SystemTarget::getTargetArch();
|
||||
|
||||
$ex_lib = trim($lib->getInstaller()->getLibraryPackage('zlib')->getStaticLibFiles()) . ' -ldl -pthread';
|
||||
$zlib_extra =
|
||||
@@ -92,9 +92,15 @@ class openssl
|
||||
$openssl_dir ??= LinuxUtil::getOSRelease()['dist'] === 'redhat' ? '/etc/pki/tls' : '/etc/ssl';
|
||||
$ex_lib = trim($ex_lib);
|
||||
|
||||
// anything we want included (PGO -fprofile-*, LTO, custom hardening)
|
||||
// has to be appended on the command line *after* the target name.
|
||||
$userCFlags = trim((string) getenv('SPC_DEFAULT_CFLAGS'));
|
||||
$userLdFlags = trim((string) getenv('SPC_DEFAULT_LDFLAGS'));
|
||||
$userExtra = trim($userCFlags . ' ' . $userLdFlags);
|
||||
|
||||
shell()->cd($lib->getSourceDir())->initializeEnv($lib)
|
||||
->exec(
|
||||
"{$env} ./Configure no-shared zlib " .
|
||||
'./Configure no-shared zlib ' .
|
||||
"--prefix={$lib->getBuildRootPath()} " .
|
||||
"--libdir={$lib->getLibDir()} " .
|
||||
"--openssldir={$openssl_dir} " .
|
||||
@@ -102,7 +108,8 @@ class openssl
|
||||
'enable-pie ' .
|
||||
'no-legacy ' .
|
||||
'no-tests ' .
|
||||
"linux-{$arch}"
|
||||
"linux-{$arch} " .
|
||||
$userExtra
|
||||
)
|
||||
->exec('make clean')
|
||||
->exec("make -j{$lib->getBuilder()->concurrency} CNF_EX_LIBS=\"{$ex_lib}\"")
|
||||
|
||||
@@ -129,7 +129,7 @@ class postgresql extends LibraryPackage
|
||||
public function buildUnix(PackageInstaller $installer, PackageBuilder $builder): void
|
||||
{
|
||||
$spc_config = new SPCConfigUtil(['no_php' => true, 'libs_only_deps' => true]);
|
||||
$config = $spc_config->getPackageDepsConfig('postgresql', array_keys($installer->getResolvedPackages()), include_suggests: $builder->getOption('with-suggests', false));
|
||||
$config = $spc_config->getPackageDepsConfig('postgresql', array_keys($installer->getResolvedPackages()));
|
||||
|
||||
$env_vars = [
|
||||
'CFLAGS' => $config['cflags'] . ' -std=c17',
|
||||
|
||||
@@ -122,10 +122,7 @@ trait frankenphp
|
||||
$staticFlags = '';
|
||||
}
|
||||
|
||||
$resolved = array_keys($installer->getResolvedPackages());
|
||||
// remove self from deps
|
||||
$resolved = array_filter($resolved, fn ($pkg_name) => $pkg_name !== $package->getName());
|
||||
$config = new SPCConfigUtil()->config($resolved);
|
||||
$config = new SPCConfigUtil()->configForResolvedBuild([$package->getName()], $installer);
|
||||
$cflags = "{$package->getLibExtraCFlags()} {$config['cflags']} " . getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS') . " -DFRANKENPHP_VERSION={$frankenphp_version}";
|
||||
$libs = $config['libs'];
|
||||
|
||||
|
||||
@@ -590,7 +590,7 @@ trait unix
|
||||
copy(ROOT_DIR . '/src/globals/common-tests/embed.c', $sample_file_path . '/embed.c');
|
||||
copy(ROOT_DIR . '/src/globals/common-tests/embed.php', $sample_file_path . '/embed.php');
|
||||
|
||||
$config = new SPCConfigUtil()->config($installer->getAvailableResolvedPackageNames());
|
||||
$config = new SPCConfigUtil()->configForResolvedBuild(['php'], $installer);
|
||||
$lens = "{$config['cflags']} {$config['ldflags']} {$config['libs']}";
|
||||
if ($toolchain->isStatic()) {
|
||||
$lens .= ' -static';
|
||||
@@ -785,7 +785,7 @@ trait unix
|
||||
*/
|
||||
private function makeVars(PackageInstaller $installer): array
|
||||
{
|
||||
$config = new SPCConfigUtil(['libs_only_deps' => true])->config($installer->getAvailableResolvedPackageNames());
|
||||
$config = new SPCConfigUtil(['libs_only_deps' => true])->configForResolvedBuild(['php'], $installer);
|
||||
$static = ApplicationContext::get(ToolchainInterface::class)->isStatic() ? '-all-static' : '';
|
||||
$pie = SystemTarget::getTargetOS() === 'Linux' ? '-pie' : '';
|
||||
|
||||
|
||||
@@ -708,7 +708,7 @@ C_CODE;
|
||||
|
||||
// Get build configuration using spc-config
|
||||
$util = new \StaticPHP\Util\SPCConfigUtil();
|
||||
$config = $util->config(array_map(fn ($x) => $x->getName(), $installer->getResolvedPackages()));
|
||||
$config = $util->configForResolvedBuild(['php'], $installer);
|
||||
|
||||
// Build the embed test executable using cl.exe
|
||||
// Note: MSVCToolchain already initialized the VC environment, no need for vcvarsall
|
||||
|
||||
@@ -14,17 +14,18 @@ trait GitHubTokenSetupTrait
|
||||
public static function getGitHubTokenHeadersStatic(): array
|
||||
{
|
||||
// GITHUB_TOKEN support
|
||||
if (($token = getenv('GITHUB_TOKEN')) !== false && ($user = getenv('GITHUB_USER')) !== false) {
|
||||
$token = getenv('GITHUB_TOKEN');
|
||||
if ($token === false || $token === '') {
|
||||
return [];
|
||||
}
|
||||
if (($user = getenv('GITHUB_USER')) !== false && $user !== '') {
|
||||
logger()->debug("Using 'GITHUB_TOKEN' with user {$user} for authentication");
|
||||
$encoded = base64_encode("{$user}:{$token}");
|
||||
spc_add_log_filter([$user, $token, $encoded]);
|
||||
return ["Authorization: Basic {$encoded}"];
|
||||
}
|
||||
if (($token = getenv('GITHUB_TOKEN')) !== false) {
|
||||
logger()->debug("Using 'GITHUB_TOKEN' for authentication");
|
||||
spc_add_log_filter($token);
|
||||
return ["Authorization: Bearer {$token}"];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use StaticPHP\Attribute\Doctor\OptionalCheck;
|
||||
use StaticPHP\DI\ApplicationContext;
|
||||
use StaticPHP\Doctor\CheckResult;
|
||||
use StaticPHP\Runtime\Shell\Shell;
|
||||
use StaticPHP\Runtime\SystemTarget;
|
||||
use StaticPHP\Toolchain\Interface\ToolchainInterface;
|
||||
use StaticPHP\Toolchain\MuslToolchain;
|
||||
use StaticPHP\Toolchain\ZigToolchain;
|
||||
@@ -27,7 +28,14 @@ class LinuxMuslCheck
|
||||
public static function optionalCheck(): bool
|
||||
{
|
||||
$toolchain = ApplicationContext::get(ToolchainInterface::class);
|
||||
return $toolchain instanceof MuslToolchain || $toolchain instanceof ZigToolchain && !LinuxUtil::isMuslDist() && !str_contains(getenv('SPC_TARGET') ?: '', 'gnu');
|
||||
return PHP_OS_FAMILY === 'Linux' && (
|
||||
$toolchain instanceof MuslToolchain || (
|
||||
$toolchain instanceof ZigToolchain
|
||||
&& !LinuxUtil::isMuslDist()
|
||||
&& SystemTarget::getLibc() === 'musl'
|
||||
&& !$toolchain->isStatic()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
|
||||
@@ -305,8 +305,8 @@ class LibraryPackage extends Package
|
||||
*/
|
||||
public function getStaticLibFiles(): string
|
||||
{
|
||||
$config = new SPCConfigUtil(['libs_only_deps' => true, 'absolute_libs' => true]);
|
||||
$res = $config->config([$this->getName()]);
|
||||
$config = new SPCConfigUtil(['no_php' => true, 'libs_only_deps' => true, 'absolute_libs' => true]);
|
||||
$res = $config->configForResolvedBuild([$this->getName()], $this->getInstaller());
|
||||
return $res['libs'];
|
||||
}
|
||||
|
||||
|
||||
@@ -274,13 +274,13 @@ class PhpExtensionPackage extends Package
|
||||
$compiler_extra = trim($compiler_extra . ' -lcompiler_rt');
|
||||
GlobalEnvManager::putenv("SPC_COMPILER_EXTRA={$compiler_extra}");
|
||||
}
|
||||
$config = (new SPCConfigUtil())->getExtensionConfig($this);
|
||||
$config = (new SPCConfigUtil())->configForResolvedBuild([$this->getName()], $this->getInstaller());
|
||||
[$staticLibs, $sharedLibs] = $this->splitLibsIntoStaticAndShared($config['libs']);
|
||||
$preStatic = PHP_OS_FAMILY === 'Darwin' ? '' : '-Wl,--start-group ';
|
||||
$postStatic = PHP_OS_FAMILY === 'Darwin' ? '' : ' -Wl,--end-group ';
|
||||
return [
|
||||
'CFLAGS' => $config['cflags'],
|
||||
'CXXFLAGS' => $config['cflags'],
|
||||
'CXXFLAGS' => $config['cxxflags'],
|
||||
'LDFLAGS' => $config['ldflags'],
|
||||
'LIBS' => clean_spaces("{$preStatic} {$staticLibs} {$postStatic} {$sharedLibs}"),
|
||||
'LD_LIBRARY_PATH' => BUILD_LIB_PATH,
|
||||
|
||||
@@ -136,6 +136,52 @@ class DependencyResolver
|
||||
return $sorted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the link closure for one or more roots within an already-resolved package set.
|
||||
*
|
||||
* Both depends and suggests may be traversed, but only packages present in the
|
||||
* resolved set are returned. Dependency overrides add build-specific graph edges,
|
||||
* such as PHP's resolved static extensions and virtual SAPI targets.
|
||||
*
|
||||
* @param array<Package|string> $packages
|
||||
* @param string[] $resolved_packages
|
||||
* @param array<string, string[]> $dependency_overrides
|
||||
* @return string[] Packages in dependency order, including the roots
|
||||
*/
|
||||
public static function getResolvedPackageClosure(
|
||||
array $packages,
|
||||
array $resolved_packages,
|
||||
array $dependency_overrides = [],
|
||||
bool $include_suggests = true,
|
||||
): array {
|
||||
$resolved_set = array_flip($resolved_packages);
|
||||
$dep_map = [];
|
||||
foreach ($resolved_packages as $package) {
|
||||
$dep_map[$package] = [
|
||||
'depends' => array_merge(
|
||||
PackageConfig::get($package, 'depends', []),
|
||||
$dependency_overrides[$package] ?? [],
|
||||
),
|
||||
'suggests' => PackageConfig::get($package, 'suggests', []),
|
||||
];
|
||||
}
|
||||
|
||||
$visited = [];
|
||||
$sorted = [];
|
||||
foreach ($packages as $package) {
|
||||
$name = is_string($package) ? $package : $package->getName();
|
||||
if (!PackageConfig::isPackageExists($name)) {
|
||||
throw new WrongUsageException("Package '{$name}' does not exist in config, please check your package name !");
|
||||
}
|
||||
if (!isset($resolved_set[$name])) {
|
||||
throw new WrongUsageException("Package '{$name}' is not part of the resolved package set.");
|
||||
}
|
||||
self::visitSubDeps($name, $dep_map, $resolved_set, $include_suggests, $visited, $sorted);
|
||||
}
|
||||
|
||||
return $sorted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a reverse dependency map for the resolved packages.
|
||||
* For each package that is depended upon, list which packages depend on it.
|
||||
|
||||
@@ -7,7 +7,10 @@ namespace StaticPHP\Util;
|
||||
use StaticPHP\Config\PackageConfig;
|
||||
use StaticPHP\Exception\WrongUsageException;
|
||||
use StaticPHP\Package\LibraryPackage;
|
||||
use StaticPHP\Package\Package;
|
||||
use StaticPHP\Package\PackageInstaller;
|
||||
use StaticPHP\Package\PhpExtensionPackage;
|
||||
use StaticPHP\Package\TargetPackage;
|
||||
use StaticPHP\Runtime\SystemTarget;
|
||||
|
||||
class SPCConfigUtil
|
||||
@@ -32,95 +35,72 @@ class SPCConfigUtil
|
||||
$this->absolute_libs = $options['absolute_libs'] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a standalone package request and generate its configuration.
|
||||
*
|
||||
* This is the entry point for spc-config and callers that do not already have
|
||||
* a PackageInstaller resolution result.
|
||||
*
|
||||
* @param array<Package|string> $packages
|
||||
* @return array{cflags: string, cxxflags: string, ldflags: string, libs: string}
|
||||
*/
|
||||
public function config(array $packages = [], bool $include_suggests = false): array
|
||||
{
|
||||
// if have php, make php as all extension's dependency
|
||||
$package_names = array_map(fn ($package) => is_string($package) ? $package : $package->getName(), $packages);
|
||||
$dep_override = $this->no_php
|
||||
? []
|
||||
: ['php' => array_values(array_filter($package_names, fn ($name) => str_starts_with($name, 'ext-')))];
|
||||
|
||||
return $this->configWithResolvedPackages(
|
||||
DependencyResolver::resolve($packages, $dep_override, $include_suggests)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build configuration for roots within the installer's resolved package graph.
|
||||
*
|
||||
* The installer decides whether suggested packages are enabled. This method always
|
||||
* walks depends and suggests, then filters every edge through that resolved set.
|
||||
*
|
||||
* @param string[] $package_names Root package names whose link closure is required
|
||||
* @return array{cflags: string, cxxflags: string, ldflags: string, libs: string}
|
||||
*/
|
||||
public function configForResolvedBuild(array $package_names, PackageInstaller $installer): array
|
||||
{
|
||||
$dependency_overrides = [];
|
||||
if (!$this->no_php) {
|
||||
$dep_override = ['php' => array_filter($packages, fn ($y) => str_starts_with($y, 'ext-'))];
|
||||
} else {
|
||||
$dep_override = [];
|
||||
$php_link_packages = [];
|
||||
foreach ($installer->getResolvedPackages(PhpExtensionPackage::class) as $extension) {
|
||||
if ($extension->isBuildStatic()) {
|
||||
$php_link_packages[] = $extension->getName();
|
||||
}
|
||||
$resolved = DependencyResolver::resolve($packages, $dep_override, $include_suggests);
|
||||
|
||||
$ldflags = $this->getLdflagsString();
|
||||
$cflags = $this->getIncludesString($resolved);
|
||||
$libs = $this->getLibsString($resolved, !$this->absolute_libs);
|
||||
|
||||
// additional OS-specific libraries (e.g. macOS -lresolv)
|
||||
// embed
|
||||
if ($extra_libs = SystemTarget::getRuntimeLibs()) {
|
||||
$libs .= " {$extra_libs}";
|
||||
}
|
||||
foreach ($installer->getResolvedPackages(TargetPackage::class) as $target) {
|
||||
if ($target->isVirtualTarget()) {
|
||||
$php_link_packages[] = $target->getName();
|
||||
}
|
||||
}
|
||||
$dependency_overrides['php'] = $php_link_packages;
|
||||
}
|
||||
|
||||
$extra_env = getenv('SPC_EXTRA_LIBS');
|
||||
if (is_string($extra_env) && !empty($extra_env)) {
|
||||
$libs .= " {$extra_env}";
|
||||
}
|
||||
// package frameworks
|
||||
if (SystemTarget::getTargetOS() === 'Darwin') {
|
||||
$libs .= " {$this->getFrameworksString($resolved)}";
|
||||
}
|
||||
// C++
|
||||
if ($this->hasCpp($resolved)) {
|
||||
$target_os = SystemTarget::getTargetOS();
|
||||
if ($target_os === 'Darwin') {
|
||||
$libcpp = '-lc++';
|
||||
$libs = str_replace($libcpp, '', $libs) . " {$libcpp}";
|
||||
} elseif ($target_os !== 'Windows') {
|
||||
// Linux and other Unix-like systems use libstdc++
|
||||
$libcpp = '-lstdc++';
|
||||
$libs = str_replace($libcpp, '', $libs) . " {$libcpp}";
|
||||
}
|
||||
// Windows (MSVC): C++ runtime is linked automatically, no explicit lib needed
|
||||
}
|
||||
|
||||
if ($this->libs_only_deps) {
|
||||
// mimalloc must come first
|
||||
if (in_array('mimalloc', $resolved) && file_exists(BUILD_LIB_PATH . '/libmimalloc.a')) {
|
||||
$libs = BUILD_LIB_PATH . '/libmimalloc.a ' . str_replace([BUILD_LIB_PATH . '/libmimalloc.a', '-lmimalloc'], ['', ''], $libs);
|
||||
}
|
||||
return [
|
||||
'cflags' => clean_spaces(getenv('CFLAGS') . ' ' . $cflags),
|
||||
'ldflags' => clean_spaces(getenv('LDFLAGS') . ' ' . $ldflags),
|
||||
'libs' => clean_spaces(getenv('LIBS') . ' ' . $libs),
|
||||
];
|
||||
}
|
||||
|
||||
// embed
|
||||
if (!$this->no_php) {
|
||||
if (SystemTarget::getTargetOS() === 'Windows') {
|
||||
// Windows: use php8embed.lib directly (either full path or short name)
|
||||
$major = intdiv(PHP_VERSION_ID, 10000);
|
||||
$php_lib = $this->absolute_libs ? BUILD_LIB_PATH . "\\php{$major}embed.lib" : "php{$major}embed.lib";
|
||||
// Windows system libs required by PHP
|
||||
// Use same system libs as PHP Makefile: LIBS=kernel32.lib ole32.lib user32.lib advapi32.lib shell32.lib ws2_32.lib Dnsapi.lib psapi.lib bcrypt.lib
|
||||
$libs = "{$php_lib} {$libs} kernel32.lib ole32.lib user32.lib advapi32.lib shell32.lib ws2_32.lib dnsapi.lib psapi.lib bcrypt.lib";
|
||||
} else {
|
||||
$libs = "-lphp {$libs} -lc";
|
||||
}
|
||||
}
|
||||
|
||||
$allLibs = getenv('LIBS') . ' ' . $libs;
|
||||
|
||||
// mimalloc must come first
|
||||
if (in_array('mimalloc', $resolved) && file_exists(BUILD_LIB_PATH . '/libmimalloc.a')) {
|
||||
$allLibs = BUILD_LIB_PATH . '/libmimalloc.a ' . str_replace([BUILD_LIB_PATH . '/libmimalloc.a', '-lmimalloc'], ['', ''], $allLibs);
|
||||
}
|
||||
|
||||
return [
|
||||
'cflags' => clean_spaces(getenv('CFLAGS') . ' ' . $cflags),
|
||||
'ldflags' => clean_spaces(getenv('LDFLAGS') . ' ' . $ldflags),
|
||||
'libs' => clean_spaces($allLibs),
|
||||
];
|
||||
return $this->configWithResolvedPackages(
|
||||
DependencyResolver::getResolvedPackageClosure(
|
||||
$package_names,
|
||||
array_keys($installer->getResolvedPackages()),
|
||||
$dependency_overrides,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* [Helper function]
|
||||
* Get configuration for a specific extension(s) dependencies.
|
||||
*
|
||||
* @param array|PhpExtensionPackage $extension_packages Extension instance or list
|
||||
* @param PhpExtensionPackage|PhpExtensionPackage[] $extension_packages Extension instance or list
|
||||
* @return array{
|
||||
* cflags: string,
|
||||
* cxxflags: string,
|
||||
* ldflags: string,
|
||||
* libs: string
|
||||
* }
|
||||
@@ -131,7 +111,7 @@ class SPCConfigUtil
|
||||
$extension_packages = [$extension_packages];
|
||||
}
|
||||
return $this->config(
|
||||
packages: array_map(fn ($y) => $y->getName(), $extension_packages),
|
||||
packages: array_map(fn ($extension_package) => $extension_package->getName(), $extension_packages),
|
||||
include_suggests: $include_suggests,
|
||||
);
|
||||
}
|
||||
@@ -140,30 +120,33 @@ class SPCConfigUtil
|
||||
* [Helper function]
|
||||
* Get configuration for a specific library(s) dependencies.
|
||||
*
|
||||
* @param array|LibraryPackage $lib Library instance or list
|
||||
* @param LibraryPackage|LibraryPackage[] $library_packages Library instance or list
|
||||
* @param bool $include_suggests Whether to include suggested libraries
|
||||
* @return array{
|
||||
* cflags: string,
|
||||
* cxxflags: string,
|
||||
* ldflags: string,
|
||||
* libs: string
|
||||
* }
|
||||
*/
|
||||
public function getLibraryConfig(array|LibraryPackage $lib, bool $include_suggests = false): array
|
||||
public function getLibraryConfig(array|LibraryPackage $library_packages, bool $include_suggests = false): array
|
||||
{
|
||||
if (!is_array($lib)) {
|
||||
$lib = [$lib];
|
||||
if (!is_array($library_packages)) {
|
||||
$library_packages = [$library_packages];
|
||||
}
|
||||
$save_no_php = $this->no_php;
|
||||
$this->no_php = true;
|
||||
$save_libs_only_deps = $this->libs_only_deps;
|
||||
$this->libs_only_deps = true;
|
||||
$ret = $this->config(
|
||||
packages: array_map(fn ($y) => $y->getName(), $lib),
|
||||
try {
|
||||
return $this->config(
|
||||
packages: array_map(fn ($library_package) => $library_package->getName(), $library_packages),
|
||||
include_suggests: $include_suggests,
|
||||
);
|
||||
} finally {
|
||||
$this->no_php = $save_no_php;
|
||||
$this->libs_only_deps = $save_libs_only_deps;
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,22 +157,24 @@ class SPCConfigUtil
|
||||
* dependencies by only including those that were actually resolved.
|
||||
*
|
||||
* @param string $package_name The package to get config for
|
||||
* @param string[] $resolved_packages The full resolved package list
|
||||
* @param string[] $resolved_package_names The full resolved package name list
|
||||
* @param bool $include_suggests Whether to include resolved suggests
|
||||
* @return array{
|
||||
* cflags: string,
|
||||
* cxxflags: string,
|
||||
* ldflags: string,
|
||||
* libs: string
|
||||
* }
|
||||
*/
|
||||
public function getPackageDepsConfig(string $package_name, array $resolved_packages, bool $include_suggests = false): array
|
||||
public function getPackageDepsConfig(string $package_name, array $resolved_package_names, bool $include_suggests = true): array
|
||||
{
|
||||
// Get sub-dependencies within the resolved set
|
||||
$sub_deps = DependencyResolver::getSubDependencies($package_name, $resolved_packages, $include_suggests);
|
||||
$sub_deps = DependencyResolver::getSubDependencies($package_name, $resolved_package_names, $include_suggests);
|
||||
|
||||
if (empty($sub_deps)) {
|
||||
return [
|
||||
'cflags' => '',
|
||||
'cxxflags' => '',
|
||||
'ldflags' => '',
|
||||
'libs' => '',
|
||||
];
|
||||
@@ -201,29 +186,36 @@ class SPCConfigUtil
|
||||
$this->no_php = true;
|
||||
$this->libs_only_deps = true;
|
||||
|
||||
$ret = $this->configWithResolvedPackages($sub_deps);
|
||||
|
||||
try {
|
||||
return $this->configWithResolvedPackages($sub_deps);
|
||||
} finally {
|
||||
$this->no_php = $save_no_php;
|
||||
$this->libs_only_deps = $save_libs_only_deps;
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration using already-resolved packages (skip dependency resolution).
|
||||
* Generate configuration from an exact, dependency-ordered package list.
|
||||
*
|
||||
* @param string[] $resolved_packages Already resolved package names in build order
|
||||
* This low-level method does not resolve dependencies or filter packages.
|
||||
*
|
||||
* @param string[] $resolved_package_names Already resolved package names in build order
|
||||
* @return array{
|
||||
* cflags: string,
|
||||
* cxxflags: string,
|
||||
* ldflags: string,
|
||||
* libs: string
|
||||
* }
|
||||
*/
|
||||
public function configWithResolvedPackages(array $resolved_packages): array
|
||||
public function configWithResolvedPackages(array $resolved_package_names): array
|
||||
{
|
||||
$ldflags = $this->getLdflagsString();
|
||||
$cflags = $this->getIncludesString($resolved_packages);
|
||||
$libs = $this->getLibsString($resolved_packages, !$this->absolute_libs);
|
||||
$includes = $this->getIncludesString($resolved_package_names);
|
||||
$libs = $this->getLibsString($resolved_package_names, !$this->absolute_libs);
|
||||
|
||||
$cflags = deduplicate_flags(clean_spaces((getenv('SPC_DEFAULT_CFLAGS') ?: '') . ' ' . getenv('CFLAGS') . ' ' . $includes));
|
||||
$cxxflags = deduplicate_flags(clean_spaces((getenv('SPC_DEFAULT_CXXFLAGS') ?: '') . ' ' . getenv('CXXFLAGS') . ' ' . $includes));
|
||||
$ldflags = deduplicate_flags(clean_spaces((getenv('SPC_DEFAULT_LDFLAGS') ?: '') . ' ' . getenv('LDFLAGS') . ' ' . $ldflags));
|
||||
|
||||
// additional OS-specific libraries (e.g. macOS -lresolv)
|
||||
if ($extra_libs = SystemTarget::getRuntimeLibs()) {
|
||||
@@ -237,11 +229,11 @@ class SPCConfigUtil
|
||||
|
||||
// package frameworks
|
||||
if (SystemTarget::getTargetOS() === 'Darwin') {
|
||||
$libs .= " {$this->getFrameworksString($resolved_packages)}";
|
||||
$libs .= " {$this->getFrameworksString($resolved_package_names)}";
|
||||
}
|
||||
|
||||
// C++
|
||||
if ($this->hasCpp($resolved_packages)) {
|
||||
if ($this->hasCpp($resolved_package_names)) {
|
||||
$target_os = SystemTarget::getTargetOS();
|
||||
if ($target_os === 'Darwin') {
|
||||
$libcpp = '-lc++';
|
||||
@@ -256,40 +248,49 @@ class SPCConfigUtil
|
||||
|
||||
if ($this->libs_only_deps) {
|
||||
// mimalloc must come first
|
||||
if (in_array('mimalloc', $resolved_packages) && file_exists(BUILD_LIB_PATH . '/libmimalloc.a')) {
|
||||
if (in_array('mimalloc', $resolved_package_names) && file_exists(BUILD_LIB_PATH . '/libmimalloc.a')) {
|
||||
$libs = BUILD_LIB_PATH . '/libmimalloc.a ' . str_replace([BUILD_LIB_PATH . '/libmimalloc.a', '-lmimalloc'], ['', ''], $libs);
|
||||
}
|
||||
return [
|
||||
'cflags' => clean_spaces(getenv('CFLAGS') . ' ' . $cflags),
|
||||
'ldflags' => clean_spaces(getenv('LDFLAGS') . ' ' . $ldflags),
|
||||
'cflags' => $cflags,
|
||||
'cxxflags' => $cxxflags,
|
||||
'ldflags' => $ldflags,
|
||||
'libs' => clean_spaces(getenv('LIBS') . ' ' . $libs),
|
||||
];
|
||||
}
|
||||
|
||||
// embed
|
||||
if (!$this->no_php) {
|
||||
if (SystemTarget::getTargetOS() === 'Windows') {
|
||||
$major = intdiv(PHP_VERSION_ID, 10000);
|
||||
$php_lib = $this->absolute_libs ? BUILD_LIB_PATH . "\\php{$major}embed.lib" : "php{$major}embed.lib";
|
||||
$libs = "{$php_lib} {$libs} kernel32.lib ole32.lib user32.lib advapi32.lib shell32.lib ws2_32.lib dnsapi.lib psapi.lib bcrypt.lib";
|
||||
} else {
|
||||
$libs = "-lphp {$libs} -lc";
|
||||
}
|
||||
}
|
||||
|
||||
$allLibs = getenv('LIBS') . ' ' . $libs;
|
||||
|
||||
// mimalloc must come first
|
||||
if (in_array('mimalloc', $resolved_packages) && file_exists(BUILD_LIB_PATH . '/libmimalloc.a')) {
|
||||
if (in_array('mimalloc', $resolved_package_names) && file_exists(BUILD_LIB_PATH . '/libmimalloc.a')) {
|
||||
$allLibs = BUILD_LIB_PATH . '/libmimalloc.a ' . str_replace([BUILD_LIB_PATH . '/libmimalloc.a', '-lmimalloc'], ['', ''], $allLibs);
|
||||
}
|
||||
|
||||
return [
|
||||
'cflags' => clean_spaces(getenv('CFLAGS') . ' ' . $cflags),
|
||||
'ldflags' => clean_spaces(getenv('LDFLAGS') . ' ' . $ldflags),
|
||||
'cflags' => $cflags,
|
||||
'cxxflags' => $cxxflags,
|
||||
'ldflags' => $ldflags,
|
||||
'libs' => clean_spaces($allLibs),
|
||||
];
|
||||
}
|
||||
|
||||
public function getFrameworksString(array $extensions): string
|
||||
/** @param string[] $package_names */
|
||||
public function getFrameworksString(array $package_names): string
|
||||
{
|
||||
$list = [];
|
||||
foreach ($extensions as $extension) {
|
||||
foreach (PackageConfig::get($extension, 'frameworks', []) as $fw) {
|
||||
foreach ($package_names as $package_name) {
|
||||
foreach (PackageConfig::get($package_name, 'frameworks', []) as $fw) {
|
||||
$ks = '-framework ' . $fw;
|
||||
if (!in_array($ks, $list)) {
|
||||
$list[] = $ks;
|
||||
@@ -299,10 +300,11 @@ class SPCConfigUtil
|
||||
return implode(' ', $list);
|
||||
}
|
||||
|
||||
private function hasCpp(array $packages): bool
|
||||
/** @param string[] $package_names */
|
||||
private function hasCpp(array $package_names): bool
|
||||
{
|
||||
foreach ($packages as $package) {
|
||||
$lang = PackageConfig::get($package, 'lang', 'c');
|
||||
foreach ($package_names as $package_name) {
|
||||
$lang = PackageConfig::get($package_name, 'lang', 'c');
|
||||
if ($lang === 'cpp') {
|
||||
return true;
|
||||
}
|
||||
@@ -310,7 +312,8 @@ class SPCConfigUtil
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getIncludesString(array $packages): string
|
||||
/** @param string[] $package_names */
|
||||
private function getIncludesString(array $package_names): string
|
||||
{
|
||||
$base = BUILD_INCLUDE_PATH;
|
||||
|
||||
@@ -347,8 +350,8 @@ class SPCConfigUtil
|
||||
|
||||
// parse pkg-configs (only for Unix)
|
||||
if (SystemTarget::isUnix()) {
|
||||
foreach ($packages as $package) {
|
||||
$pc = PackageConfig::get($package, 'pkg-configs', []);
|
||||
foreach ($package_names as $package_name) {
|
||||
$pc = PackageConfig::get($package_name, 'pkg-configs', []);
|
||||
$pkg_config_path = getenv('PKG_CONFIG_PATH') ?: '';
|
||||
$search_paths = array_filter(explode(':', $pkg_config_path));
|
||||
foreach ($pc as $file) {
|
||||
@@ -360,7 +363,7 @@ class SPCConfigUtil
|
||||
}
|
||||
}
|
||||
if (!$found) {
|
||||
throw new WrongUsageException("pkg-config file '{$file}.pc' for lib [{$package}] does not exist. Please build it first.");
|
||||
throw new WrongUsageException("pkg-config file '{$file}.pc' for lib [{$package_name}] does not exist. Please build it first.");
|
||||
}
|
||||
}
|
||||
$pc_cflags = implode(' ', $pc);
|
||||
@@ -386,16 +389,17 @@ class SPCConfigUtil
|
||||
return '-L' . BUILD_LIB_PATH;
|
||||
}
|
||||
|
||||
private function getLibsString(array $packages, bool $use_short_libs = true): string
|
||||
/** @param string[] $package_names */
|
||||
private function getLibsString(array $package_names, bool $use_short_libs = true): string
|
||||
{
|
||||
$lib_names = [];
|
||||
$frameworks = [];
|
||||
|
||||
foreach ($packages as $package) {
|
||||
foreach ($package_names as $package_name) {
|
||||
// parse pkg-configs only for unix systems
|
||||
if (SystemTarget::isUnix()) {
|
||||
// add pkg-configs libs
|
||||
$pkg_configs = PackageConfig::get($package, 'pkg-configs', []);
|
||||
$pkg_configs = PackageConfig::get($package_name, 'pkg-configs', []);
|
||||
$pkg_config_path = getenv('PKG_CONFIG_PATH') ?: '';
|
||||
$search_paths = array_filter(explode(':', $pkg_config_path));
|
||||
foreach ($pkg_configs as $pkg_config) {
|
||||
@@ -407,7 +411,7 @@ class SPCConfigUtil
|
||||
}
|
||||
}
|
||||
if (!$found) {
|
||||
throw new WrongUsageException("pkg-config file '{$pkg_config}.pc' for lib [{$package}] does not exist. Please build it first.");
|
||||
throw new WrongUsageException("pkg-config file '{$pkg_config}.pc' for lib [{$package_name}] does not exist. Please build it first.");
|
||||
}
|
||||
}
|
||||
$pkg_configs = implode(' ', $pkg_configs);
|
||||
@@ -418,12 +422,12 @@ class SPCConfigUtil
|
||||
}
|
||||
}
|
||||
// convert all static-libs to short names
|
||||
$libs = array_reverse(PackageConfig::get($package, 'static-libs', []));
|
||||
$libs = array_reverse(PackageConfig::get($package_name, 'static-libs', []));
|
||||
foreach ($libs as $lib) {
|
||||
if (FileSystem::isRelativePath($lib)) {
|
||||
// check file existence
|
||||
if (!file_exists(BUILD_LIB_PATH . "/{$lib}")) {
|
||||
throw new WrongUsageException("Library file '{$lib}' for lib [{$package}] does not exist in '" . BUILD_LIB_PATH . "'. Please build it first.");
|
||||
throw new WrongUsageException("Library file '{$lib}' for lib [{$package_name}] does not exist in '" . BUILD_LIB_PATH . "'. Please build it first.");
|
||||
}
|
||||
$lib_names[] = $this->getShortLibName($lib);
|
||||
} else {
|
||||
@@ -432,7 +436,7 @@ class SPCConfigUtil
|
||||
}
|
||||
// add frameworks for macOS
|
||||
if (SystemTarget::getTargetOS() === 'Darwin') {
|
||||
$frameworks = array_merge($frameworks, PackageConfig::get($package, 'frameworks', []));
|
||||
$frameworks = array_merge($frameworks, PackageConfig::get($package_name, 'frameworks', []));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,7 +455,7 @@ class SPCConfigUtil
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array('imap', $packages) && SystemTarget::getTargetOS() === 'Linux' && SystemTarget::getLibc() === 'glibc') {
|
||||
if (in_array('imap', $package_names) && SystemTarget::getTargetOS() === 'Linux' && SystemTarget::getLibc() === 'glibc') {
|
||||
if (file_exists(BUILD_LIB_PATH . '/libcrypt.a')) {
|
||||
$lib_names[] = '-lcrypt';
|
||||
}
|
||||
|
||||
@@ -173,6 +173,9 @@ class SourcePatcher
|
||||
*/
|
||||
public static function patchMicroPhar(int $version_id): void
|
||||
{
|
||||
if (file_exists(SOURCE_PATH . '/php-src/ext/phar/phar.c.bak')) {
|
||||
return;
|
||||
}
|
||||
FileSystem::backupFile(SOURCE_PATH . '/php-src/ext/phar/phar.c');
|
||||
FileSystem::replaceFileStr(
|
||||
SOURCE_PATH . '/php-src/ext/phar/phar.c',
|
||||
@@ -198,6 +201,12 @@ class SourcePatcher
|
||||
|
||||
public static function unpatchMicroPhar(): void
|
||||
{
|
||||
// Tolerate a missing .bak: both drivers call this, and the first restore
|
||||
// consumes the backup. Without this guard the second call throws
|
||||
// "Cannot find bak file". No .bak means the source is already pristine.
|
||||
if (!file_exists(SOURCE_PATH . '/php-src/ext/phar/phar.c.bak')) {
|
||||
return;
|
||||
}
|
||||
FileSystem::restoreBackupFile(SOURCE_PATH . '/php-src/ext/phar/phar.c');
|
||||
}
|
||||
|
||||
|
||||
79
tests/StaticPHP/Command/SPCConfigCommandTest.php
Normal file
79
tests/StaticPHP/Command/SPCConfigCommandTest.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\StaticPHP\Command;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LogLevel;
|
||||
use StaticPHP\Command\SPCConfigCommand;
|
||||
use StaticPHP\Config\PackageConfig;
|
||||
use StaticPHP\DI\ApplicationContext;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class SPCConfigCommandTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
ApplicationContext::reset();
|
||||
$this->setPackageConfig([
|
||||
'root' => [
|
||||
'type' => 'library',
|
||||
'suggests' => ['optional'],
|
||||
'static-libs' => ['/fixtures/libroot.a'],
|
||||
],
|
||||
'optional' => [
|
||||
'type' => 'library',
|
||||
'static-libs' => ['/fixtures/liboptional.a'],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->setPackageConfig([]);
|
||||
ApplicationContext::reset();
|
||||
logger()->setLevel(LogLevel::ERROR);
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testWithSuggestsOptionStillAffectsStandaloneCommand(): void
|
||||
{
|
||||
$withoutSuggests = $this->runCommand(false);
|
||||
$withSuggests = $this->runCommand(true);
|
||||
|
||||
$this->assertStringNotContainsString('/fixtures/liboptional.a', $withoutSuggests);
|
||||
$this->assertStringContainsString('/fixtures/liboptional.a', $withSuggests);
|
||||
}
|
||||
|
||||
private function runCommand(bool $withSuggests): string
|
||||
{
|
||||
$application = new Application();
|
||||
$application->setAutoExit(false);
|
||||
$application->add(new SPCConfigCommand());
|
||||
$tester = new CommandTester($application->find('spc-config'));
|
||||
$input = [
|
||||
'--with-libs' => 'root',
|
||||
'--libs-only-deps' => true,
|
||||
'--no-php' => true,
|
||||
'--no-ansi' => true,
|
||||
];
|
||||
if ($withSuggests) {
|
||||
$input['--with-suggests'] = true;
|
||||
}
|
||||
$tester->execute($input);
|
||||
|
||||
return $tester->getDisplay();
|
||||
}
|
||||
|
||||
private function setPackageConfig(array $config): void
|
||||
{
|
||||
$reflection = new \ReflectionClass(PackageConfig::class);
|
||||
$reflection->getProperty('package_configs')->setValue(null, $config);
|
||||
}
|
||||
}
|
||||
@@ -412,6 +412,39 @@ class DependencyResolverTest extends TestCase
|
||||
$this->assertNotContains('c', $subDeps, 'c is not in the resolved set, should be excluded');
|
||||
}
|
||||
|
||||
public function testGetResolvedPackageClosureFiltersSuggestsThroughResolvedSet(): void
|
||||
{
|
||||
$this->loadConfig([
|
||||
'a' => ['type' => 'target', 'depends' => ['b'], 'suggests' => ['c']],
|
||||
'b' => ['type' => 'library'],
|
||||
'c' => ['type' => 'library'],
|
||||
]);
|
||||
|
||||
$withoutSuggested = DependencyResolver::getResolvedPackageClosure(['a'], ['b', 'a']);
|
||||
$withSuggested = DependencyResolver::getResolvedPackageClosure(['a'], ['b', 'c', 'a']);
|
||||
|
||||
$this->assertSame(['b', 'a'], $withoutSuggested);
|
||||
$this->assertSame(['b', 'c', 'a'], $withSuggested);
|
||||
}
|
||||
|
||||
public function testGetResolvedPackageClosureAppliesDependencyOverrides(): void
|
||||
{
|
||||
$this->loadConfig([
|
||||
'php' => ['type' => 'target', 'depends' => ['libxml2']],
|
||||
'libxml2' => ['type' => 'library'],
|
||||
'ext-demo' => ['type' => 'php-extension', 'depends' => ['demo-lib']],
|
||||
'demo-lib' => ['type' => 'library'],
|
||||
]);
|
||||
|
||||
$closure = DependencyResolver::getResolvedPackageClosure(
|
||||
['php'],
|
||||
['libxml2', 'demo-lib', 'ext-demo', 'php'],
|
||||
['php' => ['ext-demo']],
|
||||
);
|
||||
|
||||
$this->assertSame(['libxml2', 'demo-lib', 'ext-demo', 'php'], $closure);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// Edge cases & defensive
|
||||
// ──────────────────────────────────────────────
|
||||
|
||||
207
tests/StaticPHP/Util/SPCConfigUtilTest.php
Normal file
207
tests/StaticPHP/Util/SPCConfigUtilTest.php
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\StaticPHP\Util;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use StaticPHP\Config\PackageConfig;
|
||||
use StaticPHP\DI\ApplicationContext;
|
||||
use StaticPHP\Exception\WrongUsageException;
|
||||
use StaticPHP\Package\LibraryPackage;
|
||||
use StaticPHP\Package\Package;
|
||||
use StaticPHP\Package\PackageInstaller;
|
||||
use StaticPHP\Package\PhpExtensionPackage;
|
||||
use StaticPHP\Package\TargetPackage;
|
||||
use StaticPHP\Util\SPCConfigUtil;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class SPCConfigUtilTest extends TestCase
|
||||
{
|
||||
/** @var array<string, false|string> */
|
||||
private array $savedEnv = [];
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
ApplicationContext::reset();
|
||||
$this->resetPackageConfig();
|
||||
foreach (['CFLAGS', 'CXXFLAGS', 'LDFLAGS', 'LIBS', 'SPC_DEFAULT_CFLAGS', 'SPC_DEFAULT_CXXFLAGS', 'SPC_DEFAULT_LDFLAGS', 'SPC_EXTRA_LIBS'] as $name) {
|
||||
$this->savedEnv[$name] = getenv($name);
|
||||
}
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
foreach ($this->savedEnv as $name => $value) {
|
||||
$value === false ? putenv($name) : putenv("{$name}={$value}");
|
||||
}
|
||||
$this->resetPackageConfig();
|
||||
ApplicationContext::reset();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testStandaloneConfigStillControlsSuggestsDuringResolution(): void
|
||||
{
|
||||
$this->loadLinkFixture();
|
||||
$util = new SPCConfigUtil(['no_php' => true, 'libs_only_deps' => true]);
|
||||
|
||||
$withoutSuggests = $util->config(['root']);
|
||||
$withSuggests = $util->config(['root'], include_suggests: true);
|
||||
|
||||
$this->assertStringContainsString('/fixtures/librequired.a', $withoutSuggests['libs']);
|
||||
$this->assertStringNotContainsString('/fixtures/liboptional.a', $withoutSuggests['libs']);
|
||||
$this->assertStringContainsString('/fixtures/liboptional.a', $withSuggests['libs']);
|
||||
}
|
||||
|
||||
public function testResolvedConfigIncludesOnlySuggestedPackagesThatWereResolved(): void
|
||||
{
|
||||
$this->loadLinkFixture();
|
||||
$util = new SPCConfigUtil(['no_php' => true, 'libs_only_deps' => true]);
|
||||
|
||||
$withoutOptional = $util->configForResolvedBuild(['root'], $this->createInstaller([
|
||||
'required' => 'library',
|
||||
'root' => 'library',
|
||||
]));
|
||||
$withOptional = $util->configForResolvedBuild(['root'], $this->createInstaller([
|
||||
'required' => 'library',
|
||||
'optional' => 'library',
|
||||
'root' => 'library',
|
||||
]));
|
||||
|
||||
$this->assertStringNotContainsString('/fixtures/liboptional.a', $withoutOptional['libs']);
|
||||
$this->assertStringContainsString('/fixtures/liboptional.a', $withOptional['libs']);
|
||||
}
|
||||
|
||||
public function testResolvedConfigSupportsExplicitPhpLinkClosureOverrides(): void
|
||||
{
|
||||
$this->loadConfig([
|
||||
'php' => ['type' => 'target', 'depends' => ['core']],
|
||||
'core' => ['type' => 'library', 'static-libs' => ['/fixtures/libcore.a']],
|
||||
'ext-demo' => ['type' => 'php-extension', 'depends' => ['ext-lib']],
|
||||
'ext-lib' => ['type' => 'library', 'static-libs' => ['/fixtures/libext.a']],
|
||||
'php-fpm' => ['type' => 'virtual-target', 'depends' => ['php'], 'suggests' => ['fpm-lib']],
|
||||
'fpm-lib' => ['type' => 'library', 'static-libs' => ['/fixtures/libfpm.a']],
|
||||
]);
|
||||
$util = new SPCConfigUtil(['libs_only_deps' => true]);
|
||||
|
||||
$config = $util->configForResolvedBuild(['php'], $this->createInstaller([
|
||||
'core' => 'library',
|
||||
'ext-lib' => 'library',
|
||||
'ext-demo' => 'static-extension',
|
||||
'fpm-lib' => 'library',
|
||||
'php-fpm' => 'virtual-target',
|
||||
'php' => 'target',
|
||||
]));
|
||||
|
||||
$this->assertStringContainsString('/fixtures/libcore.a', $config['libs']);
|
||||
$this->assertStringContainsString('/fixtures/libext.a', $config['libs']);
|
||||
$this->assertStringContainsString('/fixtures/libfpm.a', $config['libs']);
|
||||
}
|
||||
|
||||
public function testResolvedConfigRejectsRootOutsideResolvedSet(): void
|
||||
{
|
||||
$this->loadLinkFixture();
|
||||
|
||||
$this->expectException(WrongUsageException::class);
|
||||
$this->expectExceptionMessage("Package 'root' is not part of the resolved package set");
|
||||
|
||||
new SPCConfigUtil(['no_php' => true])->configForResolvedBuild(['root'], $this->createInstaller([
|
||||
'required' => 'library',
|
||||
]));
|
||||
}
|
||||
|
||||
public function testCAndCxxFlagsRemainIndependent(): void
|
||||
{
|
||||
putenv('SPC_DEFAULT_CFLAGS=-DDEFAULT_C');
|
||||
putenv('SPC_DEFAULT_CXXFLAGS=-DDEFAULT_CXX');
|
||||
putenv('SPC_DEFAULT_LDFLAGS=-Wl,default');
|
||||
putenv('CFLAGS=-DUSER_C');
|
||||
putenv('CXXFLAGS=-DUSER_CXX');
|
||||
putenv('LDFLAGS=-Wl,user');
|
||||
|
||||
$config = new SPCConfigUtil(['no_php' => true, 'libs_only_deps' => true])->configWithResolvedPackages([]);
|
||||
|
||||
$this->assertStringContainsString('-DDEFAULT_C', $config['cflags']);
|
||||
$this->assertStringContainsString('-DUSER_C', $config['cflags']);
|
||||
$this->assertStringNotContainsString('-DDEFAULT_CXX', $config['cflags']);
|
||||
$this->assertStringContainsString('-DDEFAULT_CXX', $config['cxxflags']);
|
||||
$this->assertStringContainsString('-DUSER_CXX', $config['cxxflags']);
|
||||
$this->assertStringContainsString('-Wl,default', $config['ldflags']);
|
||||
$this->assertStringContainsString('-Wl,user', $config['ldflags']);
|
||||
}
|
||||
|
||||
private function loadLinkFixture(): void
|
||||
{
|
||||
$this->loadConfig([
|
||||
'root' => [
|
||||
'type' => 'library',
|
||||
'depends' => ['required'],
|
||||
'suggests' => ['optional'],
|
||||
'static-libs' => ['/fixtures/libroot.a'],
|
||||
],
|
||||
'required' => ['type' => 'library', 'static-libs' => ['/fixtures/librequired.a']],
|
||||
'optional' => ['type' => 'library', 'static-libs' => ['/fixtures/liboptional.a']],
|
||||
]);
|
||||
}
|
||||
|
||||
private function loadConfig(array $configs): void
|
||||
{
|
||||
$reflection = new \ReflectionClass(PackageConfig::class);
|
||||
$property = $reflection->getProperty('package_configs');
|
||||
$property->setValue(null, $configs);
|
||||
}
|
||||
|
||||
private function resetPackageConfig(): void
|
||||
{
|
||||
$this->loadConfig([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, 'library'|'static-extension'|'target'|'virtual-target'> $packages
|
||||
*/
|
||||
private function createInstaller(array $packages): PackageInstaller
|
||||
{
|
||||
$installer = new PackageInstaller(['no-tracker' => true], false);
|
||||
$resolved = [];
|
||||
foreach ($packages as $name => $type) {
|
||||
$package = match ($type) {
|
||||
'static-extension' => $this->createStaticExtension($name),
|
||||
'target' => $this->createTarget($name, false),
|
||||
'virtual-target' => $this->createTarget($name, true),
|
||||
default => $this->createNamedPackage(LibraryPackage::class, $name),
|
||||
};
|
||||
$resolved[$name] = $package;
|
||||
}
|
||||
|
||||
$reflection = new \ReflectionClass(PackageInstaller::class);
|
||||
$reflection->getProperty('packages')->setValue($installer, $resolved);
|
||||
return $installer;
|
||||
}
|
||||
|
||||
private function createStaticExtension(string $name): PhpExtensionPackage
|
||||
{
|
||||
$extension = $this->createMock(PhpExtensionPackage::class);
|
||||
$extension->method('getName')->willReturn($name);
|
||||
$extension->method('isBuildStatic')->willReturn(true);
|
||||
return $extension;
|
||||
}
|
||||
|
||||
private function createTarget(string $name, bool $virtual): TargetPackage
|
||||
{
|
||||
$target = $this->createMock(TargetPackage::class);
|
||||
$target->method('getName')->willReturn($name);
|
||||
$target->method('isVirtualTarget')->willReturn($virtual);
|
||||
return $target;
|
||||
}
|
||||
|
||||
/** @param class-string<Package> $class */
|
||||
private function createNamedPackage(string $class, string $name): Package
|
||||
{
|
||||
$package = $this->createMock($class);
|
||||
$package->method('getName')->willReturn($name);
|
||||
return $package;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user