Compare commits

...

6 Commits

Author SHA1 Message Date
henderkes
4b19f4ec95 forward port #1138 2026-05-15 14:54:04 +07:00
henderkes
1707d21569 don't extract local sources 2026-05-15 14:30:58 +07:00
henderkes
70e717adb6 fix version reevaluating regression from v2 2026-05-15 14:05:33 +07:00
henderkes
a88e426623 clean cgo cache before rebuilding frankenphp (reports wrong version from cgo includes, even if they're updated) 2026-05-15 12:54:05 +07:00
henderkes
6fda358c90 --build-frankenphp didn't actually build frankenphp 2026-05-15 12:36:34 +07:00
henderkes
09de4c9c70 drop ldflags fron watcher-c 2026-05-15 11:05:22 +07:00
10 changed files with 99 additions and 11 deletions

View File

@@ -186,6 +186,7 @@ ext-password-argon2:
type: php-extension
depends:
- libargon2
suggests:
- ext-openssl
php-extension:
os:

View File

@@ -27,7 +27,7 @@ class password_argon2 extends PhpExtensionPackage
#[CustomPhpConfigureArg('Darwin')]
public function getConfigureArg(PackageInstaller $installer, PackageBuilder $builder): string
{
if ($installer->getLibraryPackage('openssl') !== null) {
if ($installer->getPhpExtensionPackage('openssl')?->isBuildStatic() || $this->isBuildShared()) {
if (php::getPHPVersionID() >= 80500 || (php::getPHPVersionID() >= 80400 && !$builder->getOption('enable-zts'))) {
return '--without-password-argon2'; // use --with-openssl-argon2 in openssl extension instead
}

View File

@@ -20,9 +20,8 @@ class watcher extends LibraryPackage
if (stripos($cflags, '-fpic') === false) {
$cflags .= ' -fPIC';
}
$ldflags = $this->getLibExtraLdFlags() ? ' ' . $this->getLibExtraLdFlags() : '';
shell()->cd("{$this->getSourceDir()}/watcher-c")
->exec(getenv('CXX') . " -c -o libwatcher-c.o ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra {$cflags}{$ldflags}")
->exec(getenv('CXX') . " -c -o libwatcher-c.o ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra {$cflags}")
->exec(getenv('AR') . ' rcs libwatcher-c.a libwatcher-c.o');
copy("{$this->getSourceDir()}/watcher-c/libwatcher-c.a", "{$this->getLibDir()}/libwatcher-c.a");

View File

@@ -107,6 +107,7 @@ trait frankenphp
InteractiveTerm::setMessage('Building frankenphp: ' . ConsoleColor::yellow('building with xcaddy'));
shell()->cd(BUILD_LIB_PATH)
->setEnv($env)
->exec('go clean -cache') // fix stale include evaluation
->exec("xcaddy build --output frankenphp {$xcaddy_modules}");
$builder->deployBinary(BUILD_LIB_PATH . '/frankenphp', BUILD_BIN_PATH . '/frankenphp');

View File

@@ -60,7 +60,7 @@ trait unix
}
if (self::getPHPVersionID() >= 80300 && self::getPHPVersionID() < 80400) {
SourcePatcher::patchFile('spc_fix_avx512_cache_before_80400.patch', $this->getSourceDir());
SourcePatcher::patchFile('spc_fix_avx512_cache_before_80400.patch', SOURCE_PATH . '/php-src');
}
}

View File

@@ -285,15 +285,19 @@ class Artifact
* Get source extraction directory.
*
* Rules:
* 1. If extract is not specified: SOURCE_PATH/{artifact_name}
* 2. If extract is relative path: SOURCE_PATH/{value}
* 3. If extract is absolute path: {value}
* 4. If extract is array (dict): handled by extractor (selective extraction)
* 1. If cache_type is 'local': use the absolute dirname recorded at download time (no symlink/copy).
* 2. If extract is not specified: SOURCE_PATH/{artifact_name}
* 3. If extract is relative path: SOURCE_PATH/{value}
* 4. If extract is absolute path: {value}
* 5. If extract is array (dict): handled by extractor (selective extraction)
*/
public function getSourceDir(): string
{
// Prefer cache extract path, fall back to config
$cache_info = ApplicationContext::get(ArtifactCache::class)->getSourceInfo($this->name);
if (($cache_info['cache_type'] ?? null) === 'local' && isset($cache_info['dirname'])) {
return FileSystem::convertPath($cache_info['dirname']);
}
$extract = is_string($cache_info['extract'] ?? null)
? $cache_info['extract']
: ($this->config['source']['extract'] ?? null);

View File

@@ -731,6 +731,16 @@ class ArtifactDownloader
$binary_downloaded = $artifact->isBinaryDownloaded(compare_hash: true);
$source_downloaded = $artifact->isSourceDownloaded(compare_hash: true);
if ($source_downloaded && $artifact->getName() === 'php-src' && ($requested = $this->getOption('with-php'))) {
$info = ApplicationContext::get(ArtifactCache::class)->getSourceInfo('php-src');
$cv = $info['version'] ?? null;
$ct = $info['cache_type'] ?? null;
$matches = $requested === 'git' ? $ct === 'git' : ($cv !== null && $ct !== 'git' && ($cv === $requested || str_starts_with($cv, $requested . '.')));
if (!$matches) {
$source_downloaded = false;
}
}
$item_source = ['display' => 'source', 'lock' => 'source', 'config' => $artifact->getDownloadConfig('source')];
$item_source_mirror = ['display' => 'source (mirror)', 'lock' => 'source', 'config' => $artifact->getDownloadConfig('source-mirror')];

View File

@@ -136,6 +136,12 @@ class ArtifactExtractor
throw new WrongUsageException("Artifact source [{$name}] not downloaded, please download it first!");
}
// Local (--custom-local): source lives in place at $cache_info['dirname'].
if (($cache_info['cache_type'] ?? null) === 'local') {
$artifact->emitAfterSourceExtract($artifact->getSourceDir());
return SPC_STATUS_ALREADY_EXTRACTED;
}
$source_file = $this->cache->getCacheFullPath($cache_info);
$target_path = $artifact->getSourceDir();
@@ -171,8 +177,12 @@ class ArtifactExtractor
return SPC_STATUS_ALREADY_EXTRACTED;
}
// Remove old directory if hash mismatch
if (is_dir($target_path)) {
// Remove old directory if hash mismatch.
// Guard: a symlink at $target_path (left over from older local-source handling) must be
// unlinked directly — never recurse into the link target, that would wipe the user's tree.
if (is_link($target_path)) {
@unlink($target_path);
} elseif (is_dir($target_path)) {
logger()->notice("Source [{$name}] hash mismatch, re-extracting...");
FileSystem::removeDir($target_path);
}

View File

@@ -154,6 +154,8 @@ class PackageInstaller
$this->resolvePackages();
}
$this->reconcilePhpSrcVersion();
if ($this->interactive && !$disable_delay_msg) {
// show install or build options in terminal with beautiful output
$this->printInstallerInfo();
@@ -571,6 +573,67 @@ class PackageInstaller
return null;
}
private function reconcilePhpSrcVersion(): void
{
$src_dir = SOURCE_PATH . '/php-src';
$cache = ApplicationContext::get(ArtifactCache::class);
$requested = $this->options['dl-with-php'] ?? null;
if ($requested !== null && $requested !== '' && $requested !== 'git') {
$info = $cache->getSourceInfo('php-src') ?? [];
$cv = $info['version'] ?? null;
if (($info['cache_type'] ?? null) === 'git' || $cv === null
|| ($cv !== $requested && !str_starts_with($cv, $requested . '.'))) {
$resolved = null;
$candidates = glob(DOWNLOAD_PATH . '/php-' . $requested . '.*.tar.xz') ?: [];
if ($candidates !== []) {
usort($candidates, 'strnatcmp');
if (preg_match('/^php-([0-9.]+)\.tar\.xz$/', basename(end($candidates)), $vm)) {
$resolved = $vm[1];
}
} elseif ($this->download) {
$j = @file_get_contents('https://www.php.net/releases/index.php?json&version=' . urlencode($requested));
$rel = is_string($j) ? json_decode($j, true) : null;
$resolved = is_array($rel) ? ($rel['version'] ?? null) : null;
} else {
throw new WrongUsageException("Requested PHP '{$requested}' but no php-{$requested}.*.tar.xz in downloads/; drop --no-download or run 'bin/spc download php-src --with-php={$requested}' first.");
}
if ($resolved !== null) {
$cf = DOWNLOAD_PATH . '/.cache.json';
$j = json_decode(@file_get_contents($cf) ?: '{}', true) ?: [];
$tarball = DOWNLOAD_PATH . "/php-{$resolved}.tar.xz";
$j['php-src']['source'] = [
'lock_type' => 'source', 'cache_type' => 'archive',
'filename' => "php-{$resolved}.tar.xz",
'extract' => $info['extract'] ?? null,
'hash' => is_file($tarball) ? sha1_file($tarball) : null,
'time' => time(), 'version' => $resolved,
'config' => $info['config'] ?? ['type' => 'php-release', 'domain' => 'https://www.php.net'],
'downloader' => $info['downloader'] ?? \StaticPHP\Artifact\Downloader\Type\PhpRelease::class,
];
$j['php-src']['binary'] ??= [];
file_put_contents($cf, json_encode($j, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
ApplicationContext::set(ArtifactCache::class, $cache = new ArtifactCache());
}
}
}
if (!is_dir($src_dir) || ($info = $cache->getSourceInfo('php-src')) === null) {
return;
}
if (($info['cache_type'] ?? null) === 'git') {
if (!is_dir($src_dir . '/.git')) {
FileSystem::removeDir($src_dir);
}
return;
}
$vh = $src_dir . '/main/php_version.h';
if (is_file($vh)
&& preg_match('/#define\s+PHP_VERSION\s+"([^"]+)"/', file_get_contents($vh), $m)
&& $m[1] !== ($info['version'] ?? null)
) {
FileSystem::removeDir($src_dir);
}
}
private function kindLabel(Package $package): string
{
return match (true) {
@@ -698,7 +761,7 @@ class PackageInstaller
if ($package->getBuildOption('build-all') || $package->getBuildOption('build-frankenphp')) {
$frankenphp = PackageLoader::getPackage('frankenphp');
$this->install_packages[$frankenphp->getName()] = $frankenphp;
$this->build_packages[$package->getName()] = $package;
$this->build_packages[$frankenphp->getName()] = $frankenphp;
$added = true;
}
$this->build_packages[$package->getName()] = $package;