From 35795be03727bb9cffba8f69182f61ffc5f66621 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 10 Jul 2026 18:29:56 +0800 Subject: [PATCH] fix(backend): ensure response file cleanup and improve link error handling - Add cleanupResponseFile call after successful builds in BackendTest - Implement proper exception handling for link failures with TestError - Add test case for response file cleanup when linking fails - Replace php_aot_static_int_ref with typephp_static_int_ref in assertions - Refactor Translator to always clean up response files in finally block - Improve error reporting for both link failures and missing target files - Update FunctionTest return type declaration and method implementation --- phpunit/src/Backend/BackendTest.php | 37 +++++++++++++++++++++++++++ phpunit/src/FunctionTest.php | 4 +-- phpunit/src/NativePropertyTest.php | 8 +++--- src/Translator.php | 39 ++++++++++++++++------------- 4 files changed, 64 insertions(+), 24 deletions(-) diff --git a/phpunit/src/Backend/BackendTest.php b/phpunit/src/Backend/BackendTest.php index 46089bcc..fa21d536 100644 --- a/phpunit/src/Backend/BackendTest.php +++ b/phpunit/src/Backend/BackendTest.php @@ -10,6 +10,8 @@ use TypePhp\Backend\Msvc; use TypePhp\Backend\Gcc; use TypePhp\Backend\Clang; use TypePhp\Backend\CompilerFactory; +use TypePhp\CompilerTest; +use TypePhp\Exception\TestError; class BackendTest extends TestCase { @@ -104,6 +106,7 @@ class BackendTest extends TestCase $this->assertStringContainsString('/OUT:', $cmd); $this->assertStringContainsString('output.exe', $cmd); $this->assertStringContainsString('/LIBPATH:', $cmd); + $compiler->cleanupResponseFile(); } /** @@ -183,6 +186,7 @@ class BackendTest extends TestCase $this->assertStringContainsString('/DEBUG', $cmd); $this->assertStringContainsString('/NODEFAULTLIB:LIBCMT', $cmd); $this->assertStringContainsString('/nologo', $cmd); + $compiler->cleanupResponseFile(); } /** @@ -352,6 +356,39 @@ class BackendTest extends TestCase $this->assertStringContainsString('-shared', $cmd); $this->assertStringContainsString('-lphpx', $cmd); $this->assertStringContainsString('-lphp', $cmd); + $compiler->cleanupResponseFile(); + } + + public function testBuildCleansResponseFileWhenLinkFails(): void + { + $dir = $this->createTemporaryDirectory('backend_link_failure'); + $target = $dir . '/app'; + $backend = new Gcc(new Linux(), 'false'); + $compiler = new class(ROOT_PATH, $target, $backend) extends CompilerTest { + public function __construct( + string $rootPath, + private readonly string $testTarget, + \TypePhp\Backend\CompilerBackend $backend + ) { + parent::__construct($rootPath); + $this->forTest = true; + $this->compilerBackend = $backend; + } + + protected function getTargetFileName(): string + { + return $this->testTarget; + } + }; + + try { + $compiler->build([$dir . '/missing.o']); + $this->fail('The failing linker command should abort the build'); + } catch (TestError $e) { + $this->assertStringContainsString('link failed', $e->getMessage()); + } + + $this->assertFileDoesNotExist($target . '.rsp'); } public function testResponseFileArgumentIsEscapedForPathsWithSpaces(): void diff --git a/phpunit/src/FunctionTest.php b/phpunit/src/FunctionTest.php index c52d6e9a..680c26cb 100644 --- a/phpunit/src/FunctionTest.php +++ b/phpunit/src/FunctionTest.php @@ -2,9 +2,9 @@ class FunctionTest extends \BaseTest { - public function testReturnRef() + public function testReturnRef(): void { - $this->exec('The return type of the function `test` cannot be a reference type', 'function-return-ref.php'); + $this->compile('function-return-ref.php'); } public function testNativeCallUnknownNamedArgument() diff --git a/phpunit/src/NativePropertyTest.php b/phpunit/src/NativePropertyTest.php index 2defc314..9febc6f4 100644 --- a/phpunit/src/NativePropertyTest.php +++ b/phpunit/src/NativePropertyTest.php @@ -51,9 +51,9 @@ class NativePropertyTest extends \BaseTest } $code = file_get_contents($outputFile); - $this->assertStringContainsString('php_aot_static_int_ref(this_.attr(', $code); - $this->assertStringContainsString('php_aot_static_int_ref(box.attr(', $code); - $this->assertSame(2, substr_count($code, 'php_aot_static_int_ref(')); + $this->assertStringContainsString('typephp_static_int_ref(this_.attr(', $code); + $this->assertStringContainsString('typephp_static_int_ref(box.attr(', $code); + $this->assertSame(2, substr_count($code, 'typephp_static_int_ref(')); $this->assertStringNotContainsString('this_.attr(php_get_prop(0, _literal_strings[0], 0, _literal_strings[1]), true) +=', $code); $this->assertStringNotContainsString('box.attr(php_get_prop(0, _literal_strings[0], 0, _literal_strings[1]), true) +=', $code); } @@ -67,7 +67,7 @@ class NativePropertyTest extends \BaseTest } $code = file_get_contents($outputFile); - $this->assertStringContainsString('php_aot_static_int_ref(this_.attr(', $code); + $this->assertStringContainsString('typephp_static_int_ref(this_.attr(', $code); $this->assertStringContainsString('&= (~php::toInt(php::constant(', $code); } diff --git a/src/Translator.php b/src/Translator.php index aa21c21c..eb76c0f2 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -1750,32 +1750,35 @@ CODE; } } - $linkCmd = $this->buildLinkCommand($objectFiles, $targetFile); - $this->climate->comment($linkCmd); + $backend = $this->getCompilerBackend(); + $buildError = null; + try { + $linkCmd = $this->buildLinkCommand($objectFiles, $targetFile); + $this->climate->comment($linkCmd); - // 执行链接并捕获输出 - exec($linkCmd . ' 2>&1', $output, $ret); + // 执行链接并捕获输出 + exec($linkCmd . ' 2>&1', $output, $ret); - // 显示输出(如果有) - if (!empty($output)) { - foreach ($output as $line) { - $this->climate->out($line); + // 显示输出(如果有) + if (!empty($output)) { + foreach ($output as $line) { + $this->climate->out($line); + } } - } - // 检查链接是否成功 - if ($ret !== 0) { - $this->error('link failed: ' . $targetFile); + if ($ret !== 0) { + $buildError = 'link failed: ' . $targetFile; + } elseif (!file_exists($targetFile)) { + $buildError = 'target file not generated: ' . $targetFile; + } + } finally { + $backend->cleanupResponseFile(); } - // 验证目标文件是否生成 - if (!file_exists($targetFile)) { - $this->error('target file not generated: ' . $targetFile); + if ($buildError !== null) { + $this->error($buildError); } - // 删除 Response File 临时文件 - $this->getCompilerBackend()->cleanupResponseFile(); - $this->climate->green('Build successful: ' . $targetFile); return $targetFile;