diff --git a/src/SPC/builder/linux/LinuxBuilder.php b/src/SPC/builder/linux/LinuxBuilder.php
index d15816fa..74702b1f 100644
--- a/src/SPC/builder/linux/LinuxBuilder.php
+++ b/src/SPC/builder/linux/LinuxBuilder.php
@@ -23,6 +23,8 @@ class LinuxBuilder extends UnixBuilderBase
/** @var bool Micro patch phar flag */
private bool $phar_patched = false;
+ private ?PgoManager $pgo = null;
+
public function __construct(array $options = [])
{
$this->options = $options;
@@ -49,6 +51,8 @@ class LinuxBuilder extends UnixBuilderBase
*/
public function buildPHP(int $build_target = BUILD_TARGET_NONE): void
{
+ $this->pgo = PgoManager::fromBuilder($this, $build_target);
+
$cflags = $this->arch_c_flags;
f_putenv('CFLAGS=' . $cflags);
@@ -134,7 +138,7 @@ class LinuxBuilder extends UnixBuilderBase
$this->cleanMake();
- $pgo = PgoManager::active();
+ $pgo = $this->pgo;
$needsClean = false;
$sapiBuilds = [
['cli', $enableCli, true, fn () => $this->buildCli()],
diff --git a/src/SPC/command/BuildPHPCommand.php b/src/SPC/command/BuildPHPCommand.php
index 9c10b102..8ae537d0 100644
--- a/src/SPC/command/BuildPHPCommand.php
+++ b/src/SPC/command/BuildPHPCommand.php
@@ -12,7 +12,6 @@ use SPC\store\SourcePatcher;
use SPC\util\DependencyUtil;
use SPC\util\GlobalEnvManager;
use SPC\util\LicenseDumper;
-use SPC\util\PgoManager;
use SPC\util\SPCTarget;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
@@ -215,20 +214,6 @@ class BuildPHPCommand extends BuildCommand
// clean old modules that may conflict with the new php build
FileSystem::removeDir(BUILD_MODULES_PATH);
- $pgi = (bool) $this->getOption('pgi');
- $csPgi = (bool) $this->getOption('cs-pgi');
- $pgo = (bool) $this->getOption('pgo');
- if (((int) $pgi + (int) $csPgi + (int) $pgo) > 1) {
- $this->output->writeln('--pgi, --cs-pgi, and --pgo are mutually exclusive');
- return static::FAILURE;
- }
- if ($pgi) {
- (new PgoManager())->setupInstrument($rule);
- } elseif ($csPgi) {
- (new PgoManager())->setupCsInstrument($rule);
- } elseif ($pgo) {
- (new PgoManager())->setupUse($rule);
- }
$builder->buildPHP($rule);
$builder->testPHP($rule);
diff --git a/src/SPC/util/PgoManager.php b/src/SPC/util/PgoManager.php
index efb75c0d..3401798c 100644
--- a/src/SPC/util/PgoManager.php
+++ b/src/SPC/util/PgoManager.php
@@ -43,64 +43,29 @@ class PgoManager
private string $mode;
- private static ?self $active = null;
-
- public function __construct()
+ private function __construct()
{
$this->profileRoot = BUILD_ROOT_PATH . '/pgo-data';
}
- public static function active(): ?self
+ /** Build a PgoManager for the active --pgi/--cs-pgi/--pgo option, or null if none set. */
+ public static function fromBuilder(BuilderBase $builder, int $rule): ?self
{
- return self::$active;
- }
-
- /** Setup --pgi: build with -fprofile-generate=. */
- public function setupInstrument(int $rule): void
- {
- $this->validateRule($rule);
- FileSystem::removeDir($this->profileRoot);
- f_mkdir($this->profileRoot, recursive: true);
- foreach ($this->trainableIn($rule) as $sapi) {
- f_mkdir($this->rawDir($sapi), recursive: true);
+ $modes = array_filter(['pgi', 'cs-pgi', 'pgo'], fn ($m) => (bool) $builder->getOption($m));
+ if (count($modes) > 1) {
+ throw new WrongUsageException('--pgi, --cs-pgi, and --pgo are mutually exclusive');
}
- $this->mode = self::MODE_INSTRUMENT;
- self::$active = $this;
- $this->applyShutdownPatches();
- $this->applyForSapi($this->trainableIn($rule)[0]);
- logger()->info('pgo --pgi: instrumented build, profraw will land under ' . $this->profileRoot . '//');
- }
-
- /** Setup --cs-pgi: build with -fprofile-use= -fcs-profile-generate=. Requires existing .profdata. */
- public function setupCsInstrument(int $rule): void
- {
- $this->validateRule($rule);
- foreach ($this->trainableIn($rule) as $sapi) {
- if (!is_file($this->profDataFile($sapi))) {
- throw new WrongUsageException("--cs-pgi: missing {$sapi}.profdata; run --pgi + --pgo first");
- }
- f_mkdir($this->csRawDir($sapi), recursive: true);
+ $mode = array_values($modes)[0] ?? null;
+ if ($mode === null) {
+ return null;
}
- $this->mode = self::MODE_CS_INSTRUMENT;
- self::$active = $this;
- $this->applyShutdownPatches();
- $this->applyForSapi($this->trainableIn($rule)[0]);
- logger()->info('pgo --cs-pgi: cs-instrumented build, cs-profraw under ' . $this->profileRoot . '/cs-/');
- }
-
- /** Setup --pgo: merge collected .profraw, then build with -fprofile-use=. */
- public function setupUse(int $rule): void
- {
- $this->validateRule($rule);
- if (trim((string) shell_exec('command -v llvm-profdata 2>/dev/null')) === '') {
- throw new WrongUsageException('--pgo: llvm-profdata not on PATH');
- }
- foreach ($this->trainableIn($rule) as $sapi) {
- $this->mergeSapi($sapi);
- }
- $this->mode = self::MODE_USE;
- self::$active = $this;
- $this->applyForSapi($this->trainableIn($rule)[0]);
+ $instance = new self();
+ match ($mode) {
+ 'pgi' => $instance->setupInstrument($rule),
+ 'cs-pgi' => $instance->setupCsInstrument($rule),
+ 'pgo' => $instance->setupUse($rule),
+ };
+ return $instance;
}
/** Patches php-src/libtool to passthrough -fcs-profile-* flags (otherwise dropped during shared lib link). */
@@ -155,6 +120,51 @@ class PgoManager
logger()->info("pgo {$this->mode} ({$sapi})");
}
+ /** Setup --pgi: build with -fprofile-generate=. */
+ private function setupInstrument(int $rule): void
+ {
+ $this->validateRule($rule);
+ FileSystem::removeDir($this->profileRoot);
+ f_mkdir($this->profileRoot, recursive: true);
+ foreach ($this->trainableIn($rule) as $sapi) {
+ f_mkdir($this->rawDir($sapi), recursive: true);
+ }
+ $this->mode = self::MODE_INSTRUMENT;
+ $this->applyShutdownPatches();
+ $this->applyForSapi($this->trainableIn($rule)[0]);
+ logger()->info('pgo --pgi: instrumented build, profraw will land under ' . $this->profileRoot . '//');
+ }
+
+ /** Setup --cs-pgi: build with -fprofile-use= -fcs-profile-generate=. Requires existing .profdata. */
+ private function setupCsInstrument(int $rule): void
+ {
+ $this->validateRule($rule);
+ foreach ($this->trainableIn($rule) as $sapi) {
+ if (!is_file($this->profDataFile($sapi))) {
+ throw new WrongUsageException("--cs-pgi: missing {$sapi}.profdata; run --pgi + --pgo first");
+ }
+ f_mkdir($this->csRawDir($sapi), recursive: true);
+ }
+ $this->mode = self::MODE_CS_INSTRUMENT;
+ $this->applyShutdownPatches();
+ $this->applyForSapi($this->trainableIn($rule)[0]);
+ logger()->info('pgo --cs-pgi: cs-instrumented build, cs-profraw under ' . $this->profileRoot . '/cs-/');
+ }
+
+ /** Setup --pgo: merge collected .profraw, then build with -fprofile-use=. */
+ private function setupUse(int $rule): void
+ {
+ $this->validateRule($rule);
+ if (trim((string) shell_exec('command -v llvm-profdata 2>/dev/null')) === '') {
+ throw new WrongUsageException('--pgo: llvm-profdata not on PATH');
+ }
+ foreach ($this->trainableIn($rule) as $sapi) {
+ $this->mergeSapi($sapi);
+ }
+ $this->mode = self::MODE_USE;
+ $this->applyForSapi($this->trainableIn($rule)[0]);
+ }
+
/**
* Static-embed mode links libphp.a into frankenphp; both end up in one
* binary so must share one profdata. Shared-embed mode keeps libphp.so