refactor(cli): remove legacy runtime path switches

master
韩天峰 3 weeks ago
parent 28413e0924
commit 9f26e8d06d
  1. 1
      bin/bootstrap.php
  2. 1
      phpunit/BinaryEntrypointTest.php
  3. 73
      phpunit/src/Build/ExecutableLocatorTest.php
  4. 47
      src/Backend/CompilerFactory.php
  5. 77
      src/Build/ExecutableLocator.php
  6. 6
      src/CompilerBase.php
  7. 25
      src/compiler.php

@ -7,7 +7,6 @@
*/ */
define('TYPEPHP_ROOT_PATH', dirname(__DIR__)); define('TYPEPHP_ROOT_PATH', dirname(__DIR__));
define('TYPEPHP_DEBUG', true);
// Composer bin proxies provide the consuming project's autoloader. A source // Composer bin proxies provide the consuming project's autoloader. A source
// checkout and a packaged compiler keep their own autoloader below TYPEPHP_ROOT_PATH. // checkout and a packaged compiler keep their own autoloader below TYPEPHP_ROOT_PATH.

@ -19,7 +19,6 @@ final class BinaryEntrypointTest extends TestCase
self::assertFalse(defined('ROOT_PATH')); self::assertFalse(defined('ROOT_PATH'));
self::assertFalse(defined('DEBUG')); self::assertFalse(defined('DEBUG'));
self::assertSame(realpath(__DIR__ . '/..'), TYPEPHP_ROOT_PATH); self::assertSame(realpath(__DIR__ . '/..'), TYPEPHP_ROOT_PATH);
self::assertTrue(TYPEPHP_DEBUG);
} }
public function testStubGeneratorDoesNotPolluteTheGlobalSymbolTable(): void public function testStubGeneratorDoesNotPolluteTheGlobalSymbolTable(): void

@ -0,0 +1,73 @@
<?php
/**
* This file is part of TypePHP(AOT).
*
* @link https://www.swoole.com/aot/
* @contact service@swoole.com
*/
namespace TypePhpTest\Build;
use PHPUnit\Framework\TestCase;
use TypePhp\Build\ExecutableLocator;
/**
* @internal
* @coversNothing
*/
final class ExecutableLocatorTest extends TestCase
{
private string|false $originalPath;
private string $directory;
protected function setUp(): void
{
$this->originalPath = getenv('PATH');
$this->directory = sys_get_temp_dir() . '/typephp-executable-' . bin2hex(random_bytes(6));
mkdir($this->directory, 0777, true);
}
protected function tearDown(): void
{
if ($this->originalPath === false) {
putenv('PATH');
} else {
putenv('PATH=' . $this->originalPath);
}
foreach (glob($this->directory . '/*') ?: [] as $file) {
unlink($file);
}
rmdir($this->directory);
}
public function testResolvesDirectExecutablePath(): void
{
$executable = $this->createExecutable('tpc');
self::assertSame(realpath($executable), ExecutableLocator::resolve($executable));
}
public function testResolvesExecutableFromPath(): void
{
$executable = $this->createExecutable('tpc');
putenv('PATH=' . $this->directory);
self::assertSame(realpath($executable), ExecutableLocator::resolve('tpc'));
}
public function testReturnsNullForUnknownExecutable(): void
{
putenv('PATH=' . $this->directory);
self::assertNull(ExecutableLocator::resolve('missing-tpc'));
}
private function createExecutable(string $name): string
{
$path = $this->directory . DIRECTORY_SEPARATOR . $name;
file_put_contents($path, '#!/bin/sh');
chmod($path, 0755);
return $path;
}
}

@ -2,10 +2,11 @@
namespace TypePhp\Backend; namespace TypePhp\Backend;
use TypePhp\Platform\PlatformBase; use TypePhp\Build\ExecutableLocator;
use TypePhp\Platform\Windows;
use TypePhp\Platform\Linux; use TypePhp\Platform\Linux;
use TypePhp\Platform\Macos; use TypePhp\Platform\Macos;
use TypePhp\Platform\PlatformBase;
use TypePhp\Platform\Windows;
/** /**
* Compiler factory. * Compiler factory.
@ -101,37 +102,7 @@ class CompilerFactory
return false; return false;
} }
if (self::isPathLikeCommand($program)) { return ExecutableLocator::resolve($program) !== null;
return is_file($program) && is_executable($program);
}
$path = getenv('PATH');
if ($path === false || $path === '') {
return false;
}
$extensions = [''];
if (DIRECTORY_SEPARATOR === '\\') {
$pathext = getenv('PATHEXT') ?: '.COM;.EXE;.BAT;.CMD';
$extensions = array_filter(array_map('strtolower', explode(';', $pathext)));
if (preg_match('/\.[A-Za-z0-9]+$/', $program)) {
array_unshift($extensions, '');
}
}
foreach (explode(PATH_SEPARATOR, $path) as $dir) {
if ($dir === '') {
continue;
}
foreach ($extensions as $extension) {
$candidate = rtrim($dir, DIRECTORY_SEPARATOR . '/\\') . DIRECTORY_SEPARATOR . $program . $extension;
if (is_file($candidate) && is_executable($candidate)) {
return true;
}
}
}
return false;
} }
/** /**
@ -190,8 +161,7 @@ class CompilerFactory
} }
} }
$firstToken = strtok($command, " \t\r\n"); return strtok($command, " \t\r\n");
return $firstToken === false ? '' : $firstToken;
} }
private static function normalizeCompilerName(string $compilerName): string private static function normalizeCompilerName(string $compilerName): string
@ -206,11 +176,4 @@ class CompilerFactory
return preg_replace('/\.exe$/', '', $name); return preg_replace('/\.exe$/', '', $name);
} }
private static function isPathLikeCommand(string $program): bool
{
return str_contains($program, '/')
|| str_contains($program, '\\')
|| preg_match('/^[A-Za-z]:[\/\\\\]/', $program) === 1;
}
} }

@ -0,0 +1,77 @@
<?php
/**
* This file is part of TypePHP(AOT).
*
* @link https://www.swoole.com/aot/
* @contact service@swoole.com
*/
namespace TypePhp\Build;
final class ExecutableLocator
{
public static function resolve(string $program): ?string
{
if ($program === '') {
return null;
}
if (self::isPath($program)) {
return self::resolveCandidate($program);
}
$path = getenv('PATH');
if ($path === false || $path === '') {
return null;
}
foreach (explode(PATH_SEPARATOR, $path) as $directory) {
if ($directory === '') {
continue;
}
foreach (self::executableExtensions($program) as $extension) {
$candidate = rtrim($directory, '/\\') . DIRECTORY_SEPARATOR . $program . $extension;
$resolved = self::resolveCandidate($candidate);
if ($resolved !== null) {
return $resolved;
}
}
}
return null;
}
private static function resolveCandidate(string $path): ?string
{
$resolved = realpath($path);
if ($resolved === false || !is_file($resolved)) {
return null;
}
if (!is_executable($resolved)) {
return null;
}
return $resolved;
}
private static function isPath(string $program): bool
{
return str_contains($program, '/')
|| str_contains($program, '\\')
|| preg_match('/^[A-Za-z]:/', $program) === 1;
}
/**
* @return list<string>
*/
private static function executableExtensions(string $program): array
{
if (DIRECTORY_SEPARATOR !== '\\' || pathinfo($program, PATHINFO_EXTENSION) !== '') {
return [''];
}
$pathExtensions = getenv('PATHEXT') ?: '.COM;.EXE;.BAT;.CMD';
return array_values(array_filter(explode(';', $pathExtensions), static fn (string $extension): bool => $extension !== ''));
}
}

@ -762,10 +762,8 @@ class CompilerBase implements PropertyAccessContext
$message = 'Error: Unsupported ' . $this->getLang() . ' Syntax,'; $message = 'Error: Unsupported ' . $this->getLang() . ' Syntax,';
$message .= ' Line: ' . $this->getLine($node) . ', Type: ' . $this->getType($node) . PHP_EOL; $message .= ' Line: ' . $this->getLine($node) . ', Type: ' . $this->getType($node) . PHP_EOL;
if ($this->mode === 'cli') { if ($this->mode === 'cli') {
if (defined('TYPEPHP_DEBUG') && TYPEPHP_DEBUG) { var_dump($node);
var_dump($node); debug_print_backtrace();
debug_print_backtrace();
}
} else { } else {
header('Content-Type: application/json'); header('Content-Type: application/json');
echo json_encode($node, JSON_PRETTY_PRINT); echo json_encode($node, JSON_PRETTY_PRINT);

@ -3,6 +3,7 @@ use TypePhp\Translator;
use TypePhp\Build\WasiToolchain; use TypePhp\Build\WasiToolchain;
use TypePhp\Build\WasiProjectConfig; use TypePhp\Build\WasiProjectConfig;
use TypePhp\Build\PhpxLocator; use TypePhp\Build\PhpxLocator;
use TypePhp\Build\ExecutableLocator;
use TypePhp\Build\NativeSourceProjectBuilder; use TypePhp\Build\NativeSourceProjectBuilder;
use TypePhp\Build\NativeSourceProjectConfig; use TypePhp\Build\NativeSourceProjectConfig;
use TypePhp\Build\ProjectBuildRunner; use TypePhp\Build\ProjectBuildRunner;
@ -15,18 +16,13 @@ function main(int $argc, array $argv): void
// memory. The default CLI limit (commonly 128M) is too small for larger builds. // memory. The default CLI limit (commonly 128M) is too small for larger builds.
ini_set('memory_limit', '-1'); ini_set('memory_limit', '-1');
if (!defined('TYPEPHP_ROOT_PATH')) { $compilerExecutable = ExecutableLocator::resolve($argv[0]) ?? $argv[0];
define('TYPEPHP_ROOT_PATH', getenv("TYPEPHP_HOME") ?: getcwd());
}
if (!defined('TYPEPHP_DEBUG')) {
define('TYPEPHP_DEBUG', true);
}
if (!defined('TYPEPHP_COMPILER_EXECUTABLE')) { if (!defined('TYPEPHP_COMPILER_EXECUTABLE')) {
$compilerExecutable = realpath($argv[0]); define('TYPEPHP_COMPILER_EXECUTABLE', $compilerExecutable);
define( }
'TYPEPHP_COMPILER_EXECUTABLE', if (!defined('TYPEPHP_ROOT_PATH')) {
$compilerExecutable !== false ? $compilerExecutable : $argv[0], $compilerRoot = realpath(dirname($compilerExecutable));
); define('TYPEPHP_ROOT_PATH', $compilerRoot !== false ? $compilerRoot : dirname($compilerExecutable));
} }
// The PHP entrypoint already loaded Composer's project autoloader in // The PHP entrypoint already loaded Composer's project autoloader in
@ -63,7 +59,7 @@ function main(int $argc, array $argv): void
} }
if (getenv('TYPEPHP_WASM_INTERNAL_COMPILE') !== '1' && shouldCompileWasm($argv)) { if (getenv('TYPEPHP_WASM_INTERNAL_COMPILE') !== '1' && shouldCompileWasm($argv)) {
compileWasmProgram($argv); compileWasmProgram($argv, $compilerExecutable);
return; return;
} }
@ -159,7 +155,7 @@ function compileNativeSourceProject(array $argv): void
* The lower-level build scripts are implementation details and are not part of * The lower-level build scripts are implementation details and are not part of
* the user-facing workflow. * the user-facing workflow.
*/ */
function compileWasmProgram(array $argv): void function compileWasmProgram(array $argv, string $compilerExecutable): void
{ {
$input = null; $input = null;
$buildDir = null; $buildDir = null;
@ -282,8 +278,7 @@ function compileWasmProgram(array $argv): void
$environment['TYPEPHP_WASM_PACKAGE'] = $project->package; $environment['TYPEPHP_WASM_PACKAGE'] = $project->package;
$environment['TYPEPHP_WASM_WORLD'] = $project->world; $environment['TYPEPHP_WASM_WORLD'] = $project->world;
$environment['TYPEPHP_WASM_NANO'] = $nano ? '1' : '0'; $environment['TYPEPHP_WASM_NANO'] = $nano ? '1' : '0';
$compilerExecutable = realpath($argv[0]); if (!is_file($compilerExecutable) || !is_executable($compilerExecutable)) {
if ($compilerExecutable === false || !is_executable($compilerExecutable)) {
fwrite(STDERR, "Unable to resolve the current TypePHP compiler executable: {$argv[0]}\n"); fwrite(STDERR, "Unable to resolve the current TypePHP compiler executable: {$argv[0]}\n");
exit(1); exit(1);
} }

Loading…
Cancel
Save