diff --git a/src/Package/Extension/opcache.php b/src/Package/Extension/opcache.php index 07758de2..699e124d 100644 --- a/src/Package/Extension/opcache.php +++ b/src/Package/Extension/opcache.php @@ -72,6 +72,10 @@ class opcache extends PhpExtensionPackage ) { $opcache_jit = ' --disable-opcache-jit'; } - return '--enable-opcache' . ($shared ? '=shared' : '') . $opcache_jit; + // PHP 8.5+ has opcache built-in + if ($phpVersionID < 80500) { + return '--enable-opcache' . ($shared ? '=shared' : '') . $opcache_jit; + } + return trim($opcache_jit); } } diff --git a/src/Package/Extension/spx.php b/src/Package/Extension/spx.php index bb230ec9..96a8c75d 100644 --- a/src/Package/Extension/spx.php +++ b/src/Package/Extension/spx.php @@ -6,14 +6,27 @@ namespace Package\Extension; use Package\Target\php; use StaticPHP\Attribute\Package\BeforeStage; +use StaticPHP\Attribute\Package\CustomPhpConfigureArg; use StaticPHP\Attribute\Package\Extension; use StaticPHP\Attribute\PatchDescription; +use StaticPHP\Package\PackageInstaller; use StaticPHP\Package\PhpExtensionPackage; use StaticPHP\Util\FileSystem; #[Extension('spx')] class spx extends PhpExtensionPackage { + #[CustomPhpConfigureArg('Linux')] + #[CustomPhpConfigureArg('Darwin')] + public function getUnixConfigureArg(bool $shared, PackageInstaller $installer): string + { + $arg = '--enable-SPX' . ($shared ? '=shared' : ''); + if ($installer->getLibraryPackage('zlib') !== null) { + $arg .= ' --with-zlib-dir=' . BUILD_ROOT_PATH; + } + return $arg; + } + #[BeforeStage('php', [php::class, 'buildconfForUnix'], 'ext-spx')] #[PatchDescription('Fix spx extension compile error when building as static')] public function patchBeforeBuildconf(): bool diff --git a/src/Package/Library/bzip2.php b/src/Package/Library/bzip2.php index 90fcce7c..7f99c22b 100644 --- a/src/Package/Library/bzip2.php +++ b/src/Package/Library/bzip2.php @@ -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')] diff --git a/src/Package/Library/fastlz.php b/src/Package/Library/fastlz.php index f9dd010d..7c3792cd 100644 --- a/src/Package/Library/fastlz.php +++ b/src/Package/Library/fastlz.php @@ -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 diff --git a/src/Package/Library/icu.php b/src/Package/Library/icu.php index 7364e94a..bab24508 100644 --- a/src/Package/Library/icu.php +++ b/src/Package/Library/icu.php @@ -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} " . diff --git a/src/Package/Library/jbig.php b/src/Package/Library/jbig.php index 1cfe60b7..94527f45 100644 --- a/src/Package/Library/jbig.php +++ b/src/Package/Library/jbig.php @@ -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')] diff --git a/src/Package/Library/liblz4.php b/src/Package/Library/liblz4.php index 5dfbc995..1be56fe0 100644 --- a/src/Package/Library/liblz4.php +++ b/src/Package/Library/liblz4.php @@ -17,10 +17,17 @@ use StaticPHP\Util\FileSystem; class liblz4 { #[PatchBeforeBuild] - #[PatchDescription('Fix Makefile install target for static liblz4')] + #[PatchDescription('Compile lib sources individually so -flto -c with multiple inputs works under zig-cc/clang')] public function patchBeforeBuild(LibraryPackage $lib): void { - FileSystem::replaceFileStr($lib->getSourceDir() . '/programs/Makefile', 'install: lz4', "install: lz4\n\ninstallewfwef: lz4"); + // `-flto -c` with multiple input files only writes a .o for the + // first source — the others are silently dropped, leaving liblz4.a with a + // single object. Compile each source individually so all .o files exist. + FileSystem::replaceFileStr( + $lib->getSourceDir() . '/lib/Makefile', + "liblz4.a: \$(SRCFILES)\nifeq (\$(BUILD_STATIC),yes) # can be disabled on command line\n\t@echo compiling static library\n\t\$(COMPILE.c) \$^\n\t\$(AR) rcs \$@ *.o\nendif", + "liblz4.a: \$(SRCFILES:.c=.o)\nifeq (\$(BUILD_STATIC),yes) # can be disabled on command line\n\t@echo compiling static library\n\t\$(AR) rcs \$@ \$^\nendif" + ); } #[BuildFor('Windows')] diff --git a/src/Package/Library/ncurses.php b/src/Package/Library/ncurses.php index dd591a6f..e27cefcd 100644 --- a/src/Package/Library/ncurses.php +++ b/src/Package/Library/ncurses.php @@ -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 diff --git a/src/Package/Library/openssl.php b/src/Package/Library/openssl.php index 69297198..e0493ea5 100644 --- a/src/Package/Library/openssl.php +++ b/src/Package/Library/openssl.php @@ -111,6 +111,12 @@ 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 " . @@ -121,7 +127,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}\"") diff --git a/src/Package/Library/qdbm.php b/src/Package/Library/qdbm.php index 358846fb..c7785683 100644 --- a/src/Package/Library/qdbm.php +++ b/src/Package/Library/qdbm.php @@ -20,6 +20,11 @@ class qdbm { $ac = UnixAutoconfExecutor::create($lib)->configure(); FileSystem::replaceFileRegex($lib->getSourceDir() . '/Makefile', '/MYLIBS = libqdbm.a.*/m', 'MYLIBS = libqdbm.a'); + // Makefile pins -O3, replace with SPC_DEFAULT_CFLAGS + $extra = trim((string) getenv('SPC_DEFAULT_CFLAGS')); + if ($extra !== '') { + FileSystem::replaceFileRegex($lib->getSourceDir() . '/Makefile', '/^CFLAGS = .*$/m', "CFLAGS = -Wall {$extra}"); + } $ac->make(SystemTarget::getTargetOS() === 'Darwin' ? 'mac' : ''); $lib->patchPkgconfPrefix(['qdbm.pc']); } diff --git a/src/Package/Library/unixodbc.php b/src/Package/Library/unixodbc.php index e482e68b..62149b1f 100644 --- a/src/Package/Library/unixodbc.php +++ b/src/Package/Library/unixodbc.php @@ -27,7 +27,20 @@ class unixodbc extends LibraryPackage 'Linux' => '/etc', default => throw new WrongUsageException("Unsupported OS: {$os}"), }; - UnixAutoconfExecutor::create($this) + + // unixodbc bundles libltdl; libltdl is incompatible with -flto + // (https://bugs.gentoo.org/532672). + $stripLto = static fn (string $s): string => clean_spaces((string) preg_replace('/(^|\s)-flto(=\S+)?(?=\s|$)/', ' ', $s)); + $cflags = $stripLto((string) getenv('SPC_DEFAULT_CFLAGS')); + $cxxflags = $stripLto((string) getenv('SPC_DEFAULT_CXXFLAGS')); + $ldflags = $stripLto((string) getenv('SPC_DEFAULT_LDFLAGS')); + + $make = UnixAutoconfExecutor::create($this) + ->setEnv([ + 'CFLAGS' => $cflags, + 'CXXFLAGS' => $cxxflags, + 'LDFLAGS' => $ldflags, + ]) ->configure( '--disable-debug', '--disable-dependency-tracking', @@ -35,8 +48,15 @@ class unixodbc extends LibraryPackage '--with-included-ltdl', "--sysconfdir={$sysconf_selector}", '--enable-gui=no', - ) - ->make(); + ); + + // The exe/ subdirectory builds odbcinst/iusql/etc, turn it into a no-op + file_put_contents( + "{$this->getSourceDir()}/exe/Makefile", + ".PHONY: all install clean check distclean install-strip\nall install clean check distclean install-strip:\n\t@true\n", + ); + + $make->make(); $this->patchPkgconfPrefix(['odbc.pc', 'odbccr.pc', 'odbcinst.pc']); $this->patchLaDependencyPrefix(); } diff --git a/src/Package/Target/curl.php b/src/Package/Target/curl.php index 43a2904b..86f81855 100644 --- a/src/Package/Target/curl.php +++ b/src/Package/Target/curl.php @@ -81,6 +81,7 @@ class curl ->addConfigureArgs( '-DBUILD_CURL_EXE=ON', '-DBUILD_LIBCURL_DOCS=OFF', + '-DOPENSSL_ROOT_DIR=' . BUILD_ROOT_PATH, ) ->build(); diff --git a/src/StaticPHP/DI/ApplicationContext.php b/src/StaticPHP/DI/ApplicationContext.php index 73ff9f2d..2eca635c 100644 --- a/src/StaticPHP/DI/ApplicationContext.php +++ b/src/StaticPHP/DI/ApplicationContext.php @@ -98,6 +98,25 @@ class ApplicationContext return self::getContainer()->has($id); } + /** + * Resolve $id, returning null if it can't be constructed. + * PHP-DI's has() returns true for any autowirable class even when get() + * would throw on missing scalar args — for "is this resolvable right now" + * semantics use this. + * + * @template T + * @param class-string $id + * @return null|T + */ + public static function tryGet(string $id): mixed + { + try { + return self::getContainer()->get($id); + } catch (\Throwable) { + return null; + } + } + /** * Set a service in the container. * Use sparingly - prefer configuration-based definitions.