diff --git a/bin/tpc.php b/bin/tpc.php index c3f3b72c..168ed785 100755 --- a/bin/tpc.php +++ b/bin/tpc.php @@ -5,5 +5,8 @@ require TYPEPHP_ROOT_PATH . '/src/polyfills.php'; require TYPEPHP_ROOT_PATH . '/src/gen_stub.php'; require TYPEPHP_ROOT_PATH . '/src/compiler.php'; -const TYPEPHP_PHP_SCRIPT_ENTRY = true; -main($argc, $argv); +runCompiler( + $argc, + $argv, + \TypePhp\Build\CompilerRuntime::source(TYPEPHP_ROOT_PATH, $argv[0]), +); diff --git a/phpunit/BinaryEntrypointTest.php b/phpunit/BinaryEntrypointTest.php index 33f3bf81..cf0044cf 100644 --- a/phpunit/BinaryEntrypointTest.php +++ b/phpunit/BinaryEntrypointTest.php @@ -18,6 +18,8 @@ final class BinaryEntrypointTest extends TestCase { self::assertFalse(defined('ROOT_PATH')); self::assertFalse(defined('DEBUG')); + self::assertFalse(defined('TYPEPHP_PHP_SCRIPT_ENTRY')); + self::assertFalse(defined('TYPEPHP_COMPILER_EXECUTABLE')); self::assertSame(realpath(__DIR__ . '/..'), TYPEPHP_ROOT_PATH); } diff --git a/phpunit/src/Build/CompilerGeneratorFingerprintTest.php b/phpunit/src/Build/CompilerGeneratorFingerprintTest.php index 9bec87c5..55596687 100644 --- a/phpunit/src/Build/CompilerGeneratorFingerprintTest.php +++ b/phpunit/src/Build/CompilerGeneratorFingerprintTest.php @@ -7,6 +7,7 @@ use PHPUnit\Framework\Attributes\RunInSeparateProcess; use ReflectionClass; use TypePhp\CompilerBase; use TypePhp\CompilerTest; +use TypePhp\Build\CompilerRuntime; use TypePhp\Entity\ClassDef; use TypePhp\Entity\FunctionDef; use TypePhp\Type; @@ -17,7 +18,10 @@ final class CompilerGeneratorFingerprintTest extends TestCase public function testEmbeddedSnapshotIsUsedOnlyByNativeEntry(): void { define('TYPEPHP_COMPILER_BUILD_FINGERPRINT', str_repeat('a', 64)); - $compiler = CompilerTest::create(TYPEPHP_ROOT_PATH); + $compiler = CompilerTest::create( + TYPEPHP_ROOT_PATH, + CompilerRuntime::nativeAt(TYPEPHP_ROOT_PATH, PHP_BINARY), + ); $method = (new ReflectionClass(\TypePhp\Translator::class))->getMethod('getIncrementalGeneratorFingerprint'); $native = $method->invoke($compiler); self::assertMatchesRegularExpression('/^[a-f0-9]{64}$/D', $native); @@ -27,8 +31,11 @@ final class CompilerGeneratorFingerprintTest extends TestCase } self::assertFalse($reflection->getProperty('opcodeBuildChecked')->getValue($compiler)); self::assertSame($native, $method->invoke($compiler)); - define('TYPEPHP_PHP_SCRIPT_ENTRY', true); - self::assertNotSame($native, $method->invoke($compiler)); + $sourceCompiler = CompilerTest::create( + TYPEPHP_ROOT_PATH, + CompilerRuntime::source(TYPEPHP_ROOT_PATH, PHP_BINARY), + ); + self::assertNotSame($native, $method->invoke($sourceCompiler)); } public function testCompilerSnapshotIsStableAndChangesWithSourceOrOptions(): void diff --git a/phpunit/src/Build/CompilerRuntimeTest.php b/phpunit/src/Build/CompilerRuntimeTest.php new file mode 100644 index 00000000..2f031158 --- /dev/null +++ b/phpunit/src/Build/CompilerRuntimeTest.php @@ -0,0 +1,37 @@ +sourceEntry); + self::assertSame(realpath(dirname(__DIR__, 3)), $runtime->installationRoot); + self::assertSame(realpath(PHP_BINARY), $runtime->executable); + } + + public function testNativeRuntimeUsesExecutableDirectory(): void + { + $runtime = CompilerRuntime::native(PHP_BINARY); + + self::assertFalse($runtime->sourceEntry); + self::assertSame(realpath(PHP_BINARY), $runtime->executable); + self::assertSame(realpath(dirname(PHP_BINARY)), $runtime->installationRoot); + } +} diff --git a/phpunit/src/Build/PhpxLocatorTest.php b/phpunit/src/Build/PhpxLocatorTest.php index cf904a55..2efaf115 100644 --- a/phpunit/src/Build/PhpxLocatorTest.php +++ b/phpunit/src/Build/PhpxLocatorTest.php @@ -24,7 +24,7 @@ final class PhpxLocatorTest extends TestCase } else { putenv('PHPX_HOME=' . $this->originalPhpxHome); } - rmdir($this->phpxHome); + $this->removeDirectory($this->phpxHome); } public function testPhpxHomeHasPriorityAndReturnsAnAbsolutePath(): void @@ -44,4 +44,26 @@ final class PhpxLocatorTest extends TestCase PhpxLocator::resolve($projectRoot), ); } + + public function testCompilerLocalInstallationPrecedesComposerMetadata(): void + { + putenv('PHPX_HOME=' . $this->phpxHome . '/missing'); + $projectRoot = $this->phpxHome . '/compiler'; + $localPhpx = $projectRoot . '/vendor/swoole/phpx'; + mkdir($localPhpx, 0777, true); + + self::assertSame(realpath($localPhpx), PhpxLocator::resolve($projectRoot)); + } + + private function removeDirectory(string $directory): void + { + $items = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST, + ); + foreach ($items as $item) { + $item->isDir() ? rmdir($item->getPathname()) : unlink($item->getPathname()); + } + rmdir($directory); + } } diff --git a/phpunit/src/CompilationStatisticsTest.php b/phpunit/src/CompilationStatisticsTest.php index d6f2cf8a..ba6b4982 100644 --- a/phpunit/src/CompilationStatisticsTest.php +++ b/phpunit/src/CompilationStatisticsTest.php @@ -78,7 +78,7 @@ final class CompilationStatisticsTest extends BaseTest self::assertTrue($selection->completeFallback); self::assertSame(['json', 'standard'], $selection->extensions); - $composition = (new NanoSourceComposer())->compose( + $composition = (new NanoSourceComposer(TYPEPHP_ROOT_PATH))->compose( sys_get_temp_dir() . '/typephp-nano-dynamic-fallback-test', 'dynamic_fallback_test', false, @@ -122,7 +122,7 @@ final class CompilationStatisticsTest extends BaseTest self::assertSame(['date'], $selection->extensions); self::assertSame([], $selection->features); - $composition = (new NanoSourceComposer())->compose( + $composition = (new NanoSourceComposer(TYPEPHP_ROOT_PATH))->compose( sys_get_temp_dir() . '/typephp-nano-date-component-test', 'date_component_test', false, @@ -156,7 +156,7 @@ final class CompilationStatisticsTest extends BaseTest $statistics->record(CompilationStatistics::DIRECT_FUNCTIONS, 'array_keys'); $statistics->finish(); - $composition = (new NanoSourceComposer())->compose( + $composition = (new NanoSourceComposer(TYPEPHP_ROOT_PATH))->compose( sys_get_temp_dir() . '/typephp-nano-component-test', 'component_test', false, @@ -186,7 +186,7 @@ final class CompilationStatisticsTest extends BaseTest $statistics->record(CompilationStatistics::DIRECT_FUNCTIONS, 'json_encode'); $statistics->finish(); - $composition = (new NanoSourceComposer())->compose( + $composition = (new NanoSourceComposer(TYPEPHP_ROOT_PATH))->compose( sys_get_temp_dir() . '/typephp-nano-shared-standard-test', 'shared_standard_test', false, diff --git a/src/Build/CompilerRuntime.php b/src/Build/CompilerRuntime.php new file mode 100644 index 00000000..5b71dc44 --- /dev/null +++ b/src/Build/CompilerRuntime.php @@ -0,0 +1,44 @@ + */ - public static function discover(): array + public static function discover(?string $compilerRoot = null): array { $packages = []; foreach (InstalledVersions::getInstalledPackages() as $package) { @@ -43,7 +43,7 @@ final readonly class ComposerNativePackage || !is_array($manifest['extra']['typephp-native'] ?? null)) { continue; } - $packages[] = self::load($package); + $packages[] = self::load($package, $compilerRoot); } usort( @@ -57,19 +57,20 @@ final readonly class ComposerNativePackage return $packages; } - public static function load(string $package): self + public static function load(string $package, ?string $compilerRoot = null): self { if ($package === 'swoole/php-ext-standard') { throw new RuntimeException( 'The standard extension is built into swoole/php-nano; remove obsolete package `swoole/php-ext-standard`' ); } - $installPath = class_exists(InstalledVersions::class) && InstalledVersions::isInstalled($package) - ? InstalledVersions::getInstallPath($package) - : null; - $root = is_string($installPath) ? realpath($installPath) : false; - if ($root === false) { - $root = self::resolveSiblingPackage($package); + $root = self::resolveLocalPackage($package, $compilerRoot); + if ($root === null) { + $installPath = class_exists(InstalledVersions::class) && InstalledVersions::isInstalled($package) + ? InstalledVersions::getInstallPath($package) + : null; + $resolvedInstallPath = is_string($installPath) ? realpath($installPath) : false; + $root = $resolvedInstallPath !== false ? $resolvedInstallPath : null; } if ($root === null) { throw new RuntimeException( @@ -249,21 +250,29 @@ final readonly class ComposerNativePackage * The same layout is used by the monorepo checkout, where a package may not * yet be present in the checkout's generated InstalledVersions metadata. */ - private static function resolveSiblingPackage(string $package): ?string + private static function resolveLocalPackage(string $package, ?string $compilerRoot): ?string { - if (!defined('TYPEPHP_ROOT_PATH') || !str_starts_with($package, 'swoole/')) { + if ($compilerRoot === null || !str_starts_with($package, 'swoole/')) { return null; } - $candidate = realpath( - dirname(TYPEPHP_ROOT_PATH) . DIRECTORY_SEPARATOR . substr($package, strlen('swoole/')), - ); - if ($candidate === false || !is_dir($candidate)) { - return null; + + $packageDirectory = substr($package, strlen('swoole/')); + $candidates = [ + $compilerRoot . '/vendor/swoole/' . $packageDirectory, + $compilerRoot . '/' . $packageDirectory, + dirname($compilerRoot) . '/' . $packageDirectory, + ]; + foreach ($candidates as $path) { + $candidate = realpath($path); + if ($candidate === false || !is_dir($candidate)) { + continue; + } + $manifest = json_decode((string) @file_get_contents($candidate . '/composer.json'), true); + if (is_array($manifest) && ($manifest['name'] ?? null) === $package) { + return $candidate; + } } - $manifest = json_decode((string) @file_get_contents($candidate . '/composer.json'), true); - return is_array($manifest) && ($manifest['name'] ?? null) === $package - ? $candidate - : null; + return null; } /** @return list */ diff --git a/src/Build/IncrementalCompilationTrait.php b/src/Build/IncrementalCompilationTrait.php index 038220eb..d99c07b9 100644 --- a/src/Build/IncrementalCompilationTrait.php +++ b/src/Build/IncrementalCompilationTrait.php @@ -371,7 +371,7 @@ trait IncrementalCompilationTrait // Reading and hashing a 20+ MB executable on every tiny build costs more // than processing the consumer source. Keep the executable fallback for // binaries built before this metadata was introduced. - if (!defined('TYPEPHP_PHP_SCRIPT_ENTRY') + if (!$this->compilerRuntime->sourceEntry && defined('TYPEPHP_COMPILER_BUILD_FINGERPRINT')) { $snapshot = constant('TYPEPHP_COMPILER_BUILD_FINGERPRINT'); if (is_string($snapshot) && preg_match('/^[a-f0-9]{64}$/D', $snapshot) === 1) { @@ -386,10 +386,9 @@ trait IncrementalCompilationTrait // Zend bridge. Fingerprint the executable snapshot with one native hash // operation instead. The interpreted development entry keeps the source // walk below so edits invalidate generated-code caches immediately. - if (!defined('TYPEPHP_PHP_SCRIPT_ENTRY') - && defined('TYPEPHP_COMPILER_EXECUTABLE')) { - $executable = constant('TYPEPHP_COMPILER_EXECUTABLE'); - if (is_string($executable) && is_file($executable)) { + if (!$this->compilerRuntime->sourceEntry) { + $executable = $this->compilerRuntime->executable; + if (is_file($executable)) { hash_update($context, str_replace('\\', '/', $executable) . "\0"); if (!hash_update_file($context, $executable)) { throw new \RuntimeException( diff --git a/src/Build/NanoSourceComposer.php b/src/Build/NanoSourceComposer.php index b107a277..c6e8cf9c 100644 --- a/src/Build/NanoSourceComposer.php +++ b/src/Build/NanoSourceComposer.php @@ -8,6 +8,10 @@ use TypePhp\Analysis\CompilationStatistics; /** Resolves the Composer-provided runtime sources used by a Nano build. */ final class NanoSourceComposer { + public function __construct(private readonly string $compilerRoot) + { + } + /** * @return array{ * packages: list, @@ -25,8 +29,8 @@ final class NanoSourceComposer ?CompilationStatistics $statistics = null, ): array { - $runtime = ComposerNativePackage::load('swoole/php-nano'); - $phpx = ComposerNativePackage::load('swoole/phpx'); + $runtime = ComposerNativePackage::load('swoole/php-nano', $this->compilerRoot); + $phpx = ComposerNativePackage::load('swoole/phpx', $this->compilerRoot); if ($runtime->abi !== $phpx->abi) { throw new RuntimeException( "Native ABI mismatch: swoole/php-nano={$runtime->abi}, swoole/phpx={$phpx->abi}" @@ -37,7 +41,7 @@ final class NanoSourceComposer $runtime->name => $runtime, $phpx->name => $phpx, ]; - foreach (ComposerNativePackage::discover() as $package) { + foreach (ComposerNativePackage::discover($this->compilerRoot) as $package) { $packagesByName[$package->name] = $package; } $packages = array_values($packagesByName); @@ -147,7 +151,7 @@ final class NanoSourceComposer "Nano component `{$component->name}` requires unknown component `{$requirement}`" ); } - foreach ($componentsByName[$requirement] ?? [] as [$package, $required]) { + foreach ($componentsByName[$requirement] as [$package, $required]) { if (!isset($active[$package->name][$required->name])) { $active[$package->name][$required->name] = true; $pending[] = $required; diff --git a/src/Build/NativeSourceProjectBuilder.php b/src/Build/NativeSourceProjectBuilder.php index 682367af..ff97bd55 100644 --- a/src/Build/NativeSourceProjectBuilder.php +++ b/src/Build/NativeSourceProjectBuilder.php @@ -16,8 +16,10 @@ final class NativeSourceProjectBuilder private bool $progressLineActive = false; private int $progressLineWidth = 0; - public function __construct(private readonly bool $lineProgress = false) - { + public function __construct( + private readonly CompilerRuntime $compilerRuntime, + private readonly bool $lineProgress = false, + ) { } /** @return array{output: string, sourceCount: int, compiledCount: int} */ @@ -45,7 +47,7 @@ final class NativeSourceProjectBuilder 'Generating C++ from ' . count($project->phpSources) . ' TypePHP source file(s)' ); $generatedDir = $project->buildDir . DIRECTORY_SEPARATOR . 'generated'; - $translator = Translator::getInstance(); + $translator = Translator::getInstance($this->compilerRuntime); $phpFiles = $translator->prepareNanoSources( $project->phpSources, $this->projectSymbolName($project), @@ -56,7 +58,7 @@ final class NativeSourceProjectBuilder $generatedIncludeDir = $generatedDir . DIRECTORY_SEPARATOR . 'include'; } - $composition = (new NanoSourceComposer())->compose( + $composition = (new NanoSourceComposer($this->compilerRuntime->installationRoot))->compose( $project->buildDir, $this->projectSymbolName($project), $project->phpSources !== [], diff --git a/src/Build/PhpxLocator.php b/src/Build/PhpxLocator.php index 2c9c3d86..307a7646 100644 --- a/src/Build/PhpxLocator.php +++ b/src/Build/PhpxLocator.php @@ -17,6 +17,11 @@ final class PhpxLocator } } + $resolved = self::existingDirectory(rtrim($rootPath, '/\\') . '/vendor/swoole/phpx'); + if ($resolved !== null) { + return $resolved; + } + if (class_exists(InstalledVersions::class) && InstalledVersions::isInstalled('swoole/phpx')) { $installPath = InstalledVersions::getInstallPath('swoole/phpx'); if (is_string($installPath)) { @@ -27,11 +32,6 @@ final class PhpxLocator } } - $resolved = self::existingDirectory(rtrim($rootPath, '/\\') . '/vendor/swoole/phpx'); - if ($resolved !== null) { - return $resolved; - } - throw new RuntimeException( "phpx directory not found. Set PHPX_HOME or install swoole/phpx with Composer.", ); diff --git a/src/Build/SourcePipelineTrait.php b/src/Build/SourcePipelineTrait.php index aea74774..69db8d1f 100644 --- a/src/Build/SourcePipelineTrait.php +++ b/src/Build/SourcePipelineTrait.php @@ -589,7 +589,7 @@ trait SourcePipelineTrait // file processing/compilation. The compiled tpc executable has libphpx // loaded by the dynamic linker before entering main(), so checking here // is neither needed nor possible. - if (defined('TYPEPHP_PHP_SCRIPT_ENTRY') && !($platform instanceof Wasi)) { + if ($this->compilerRuntime->sourceEntry && !($platform instanceof Wasi)) { $this->validatePhpxLibrary(); } } @@ -607,7 +607,7 @@ trait SourcePipelineTrait $this->getPhpDir(), $this->getPhpxDir(), $this->buildMode, - defined('TYPEPHP_PHP_SCRIPT_ENTRY'), + $this->compilerRuntime->sourceEntry, ) as $message) { if (!empty($message['error'])) { $detail = $message['error']; diff --git a/src/CompilerBase.php b/src/CompilerBase.php index 2e4e77e9..7b397abf 100644 --- a/src/CompilerBase.php +++ b/src/CompilerBase.php @@ -10,6 +10,7 @@ namespace TypePhp; use TypePhp\Analysis\CompilationStatistics; use TypePhp\Build\AstCache; +use TypePhp\Build\CompilerRuntime; use TypePhp\Build\PhpxLocator; use TypePhp\Build\StableIdRegistry; @@ -630,6 +631,7 @@ class CompilerBase implements PropertyAccessContext protected bool $decimalTypes = false; protected bool $bigintTypes = false; protected string $rootPath; + protected CompilerRuntime $compilerRuntime; protected string $buildDir; protected string $targetName = 'app'; protected string $outputDir = ''; // Output directory specified by the -o option @@ -684,7 +686,7 @@ class CompilerBase implements PropertyAccessContext /** @var array Fully resolved class names referenced by source code. */ protected array $referencedClasses = []; - public function __construct(string $rootPath) + public function __construct(string $rootPath, ?CompilerRuntime $compilerRuntime = null) { $this->osType = PHP_OS_FAMILY; if (version_compare(PHP_VERSION, '8.4.0', '<')) { @@ -694,6 +696,8 @@ class CompilerBase implements PropertyAccessContext $this->error('PHP 8.6.0 or later is not supported'); } $this->rootPath = $rootPath; + $this->compilerRuntime = $compilerRuntime + ?? CompilerRuntime::nativeAt($rootPath, PHP_BINARY); $this->compilationStatistics = new CompilationStatistics(); $this->symbols = new SymbolRepository(); $this->setPhpVersion(self::DEFAULT_PHP_VERSION); diff --git a/src/CompilerTest.php b/src/CompilerTest.php index 7754c6fe..ecba3d4e 100644 --- a/src/CompilerTest.php +++ b/src/CompilerTest.php @@ -8,15 +8,17 @@ namespace TypePhp; +use TypePhp\Build\CompilerRuntime; + /** * @internal * @coversNothing */ class CompilerTest extends Translator { - public static function create(string $rootPath = ''): CompilerTest + public static function create(string $rootPath = '', ?CompilerRuntime $runtime = null): CompilerTest { - $instance = new self($rootPath); + $instance = new self($rootPath, $runtime); $instance->forTest = true; return $instance; } diff --git a/src/Translator.php b/src/Translator.php index f83e62f9..4c97d1ad 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -17,6 +17,7 @@ use TypePhp\Analysis\NativeObjectStackPromotionAnalyzer; use TypePhp\Analysis\SsaBuilder; use TypePhp\Backend\CompilerFactory; use TypePhp\Build\CompileOptions; +use TypePhp\Build\CompilerRuntime; use TypePhp\Build\FileScanner; use TypePhp\Build\IncrementalCompilationTrait; use TypePhp\Build\NativeCommandOptionsTrait; @@ -153,9 +154,9 @@ class Translator extends Preprocessor return $func->method && str_ends_with($func->name, self::NAMESPACE_SEPARATOR . '__construct'); } - public function __construct(string $rootPath) + public function __construct(string $rootPath, ?CompilerRuntime $compilerRuntime = null) { - parent::__construct($rootPath); + parent::__construct($rootPath, $compilerRuntime); $this->climate->arguments->add(Constants::COMPILER_OPTIONS); $this->preprocessArgvAdvanced(); $this->climate->arguments->parse(); @@ -199,10 +200,13 @@ class Translator extends Preprocessor self::$instance = $this; } - public static function getInstance(): Translator + public static function getInstance(?CompilerRuntime $runtime = null): Translator { if (self::$instance === null) { - self::$instance = new self(TYPEPHP_ROOT_PATH); + if ($runtime === null) { + throw new \LogicException('The compiler runtime has not been initialized'); + } + self::$instance = new self($runtime->installationRoot, $runtime); } return self::$instance; } @@ -2376,7 +2380,7 @@ CODE; /** @param list $generatedSources @return list */ private function composeNanoRuntimeSources(array $generatedSources): array { - $composition = (new NanoSourceComposer())->compose( + $composition = (new NanoSourceComposer($this->compilerRuntime->installationRoot))->compose( $this->getBuildDir(), $this->targetName, true, diff --git a/src/compiler.php b/src/compiler.php index 7a99cf3b..26d569f9 100644 --- a/src/compiler.php +++ b/src/compiler.php @@ -3,7 +3,7 @@ use TypePhp\Translator; use TypePhp\Build\WasiToolchain; use TypePhp\Build\WasiProjectConfig; use TypePhp\Build\PhpxLocator; -use TypePhp\Build\ExecutableLocator; +use TypePhp\Build\CompilerRuntime; use TypePhp\Build\NativeSourceProjectBuilder; use TypePhp\Build\NativeSourceProjectConfig; use TypePhp\Build\ProjectBuildRunner; @@ -11,23 +11,19 @@ use TypePhp\PythonTools\Command as PythonToolsCommand; use TypePhp\Cli\CompletionCommand; function main(int $argc, array $argv): void +{ + runCompiler($argc, $argv, CompilerRuntime::native($argv[0])); +} + +function runCompiler(int $argc, array $argv, CompilerRuntime $runtime): void { // Compiling a complete project keeps the parsed AST and generated sources in // memory. The default CLI limit (commonly 128M) is too small for larger builds. ini_set('memory_limit', '-1'); - $compilerExecutable = ExecutableLocator::resolve($argv[0]) ?? $argv[0]; - if (!defined('TYPEPHP_COMPILER_EXECUTABLE')) { - define('TYPEPHP_COMPILER_EXECUTABLE', $compilerExecutable); - } - if (!defined('TYPEPHP_ROOT_PATH')) { - $compilerRoot = realpath(dirname($compilerExecutable)); - define('TYPEPHP_ROOT_PATH', $compilerRoot !== false ? $compilerRoot : dirname($compilerExecutable)); - } - // The PHP entrypoint already loaded Composer's project autoloader in // bin/bootstrap.php. The native binary loads its embedded copy here. - if (!defined('TYPEPHP_PHP_SCRIPT_ENTRY')) { + if (!$runtime->sourceEntry) { require_once dirname(__DIR__) . '/vendor/autoload.php'; } @@ -54,12 +50,12 @@ function main(int $argc, array $argv): void exit(1); } if ($nativeSourceProject) { - compileNativeSourceProject($argv); + compileNativeSourceProject($argv, $runtime); return; } if (getenv('TYPEPHP_WASM_INTERNAL_COMPILE') !== '1' && shouldCompileWasm($argv)) { - compileWasmProgram($argv, $compilerExecutable); + compileWasmProgram($argv, $runtime); return; } @@ -69,7 +65,7 @@ function main(int $argc, array $argv): void return; } - (new ProjectBuildRunner(Translator::getInstance()))->run($argv); + (new ProjectBuildRunner(Translator::getInstance($runtime)))->run($argv); } function shouldCompileNativeSourceProject(array $argv): bool @@ -87,7 +83,7 @@ function shouldCompileNativeSourceProject(array $argv): bool return false; } -function compileNativeSourceProject(array $argv): void +function compileNativeSourceProject(array $argv, CompilerRuntime $runtime): void { $input = null; $buildDir = null; @@ -133,7 +129,7 @@ function compileNativeSourceProject(array $argv): void try { $project = NativeSourceProjectConfig::load($input, $buildDir); - $builder = new NativeSourceProjectBuilder(); + $builder = new NativeSourceProjectBuilder(compilerRuntime: $runtime); $result = $builder->build($project); fwrite( STDOUT, @@ -155,7 +151,7 @@ function compileNativeSourceProject(array $argv): void * The lower-level build scripts are implementation details and are not part of * the user-facing workflow. */ -function compileWasmProgram(array $argv, string $compilerExecutable): void +function compileWasmProgram(array $argv, CompilerRuntime $runtime): void { $input = null; $buildDir = null; @@ -222,7 +218,7 @@ function compileWasmProgram(array $argv, string $compilerExecutable): void $input, $buildDir, $workingDirectory, - TYPEPHP_ROOT_PATH . DIRECTORY_SEPARATOR . 'build', + $runtime->installationRoot . DIRECTORY_SEPARATOR . 'build', $profile, ); } catch (RuntimeException $exception) { @@ -230,7 +226,7 @@ function compileWasmProgram(array $argv, string $compilerExecutable): void exit(1); } - $builder = dirname(__DIR__) . '/wasm/' + $builder = $runtime->installationRoot . '/wasm/' . ($nano ? 'build-nano-program.sh' : 'build-program.sh'); if (!is_executable($builder)) { fwrite(STDERR, "TypePHP WASI builder is not executable: {$builder}\n"); @@ -278,7 +274,7 @@ function compileWasmProgram(array $argv, string $compilerExecutable): void $environment['TYPEPHP_WASM_PACKAGE'] = $project->package; $environment['TYPEPHP_WASM_WORLD'] = $project->world; $environment['TYPEPHP_WASM_NANO'] = $nano ? '1' : '0'; - if (!is_file($compilerExecutable) || !is_executable($compilerExecutable)) { + if (!is_file($runtime->executable) || !is_executable($runtime->executable)) { fwrite(STDERR, "Unable to resolve the current TypePHP compiler executable: {$argv[0]}\n"); exit(1); } @@ -287,7 +283,7 @@ function compileWasmProgram(array $argv, string $compilerExecutable): void } try { - $phpxDir = PhpxLocator::resolve(TYPEPHP_ROOT_PATH); + $phpxDir = PhpxLocator::resolve($runtime->installationRoot); } catch (RuntimeException $exception) { fwrite(STDERR, "Unable to locate PHPX: {$exception->getMessage()}\n"); exit(1); @@ -295,7 +291,7 @@ function compileWasmProgram(array $argv, string $compilerExecutable): void if ($project->mode === 'library') { $environment['TYPEPHP_WIT_BINDGEN'] = $tools['wit-bindgen']; } - $command = [$builder, $project->input, $project->output ?? '-', $phpxDir, $compilerExecutable]; + $command = [$builder, $project->input, $project->output ?? '-', $phpxDir, $runtime->executable]; $process = proc_open( $command,