From ea77b6d746e35d37f973f82e9275df4c53ee1e9f Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Sat, 24 Jan 2026 15:49:43 +0800 Subject: [PATCH] =?UTF-8?q?feat(compiler):=20=E6=B7=BB=E5=8A=A0=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E6=A8=A1=E5=BC=8F=E6=94=AF=E6=8C=81=E5=92=8C=E7=9B=AE?= =?UTF-8?q?=E6=A0=87=E5=90=8D=E7=A7=B0=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现二进制文件和PHP扩展两种编译模式 - 添加目标名称配置功能 - 支持通过命令行参数指定编译模式 - 更新编译选项以支持不同的构建模式 - 添加main函数存在性检查确保二进制构建完整性 - 修改链接过程以支持共享库生成 - 添加示例文件array_append.php和ref.php用于测试 - 更新帮助文档和命令行参数说明 --- bin/compiler.php | 13 +++++---- examples/array_append.php | 8 ++++++ examples/ref.php | 11 ++++++++ src/Php/CompilerBase.php | 14 ++++++++-- src/Php/Translator.php | 50 ++++++++++++++++++++++++++++++----- src/template/extension.cc.php | 12 +++++---- 6 files changed, 89 insertions(+), 19 deletions(-) create mode 100644 examples/array_append.php create mode 100644 examples/ref.php diff --git a/bin/compiler.php b/bin/compiler.php index d3248409..38852197 100755 --- a/bin/compiler.php +++ b/bin/compiler.php @@ -24,11 +24,12 @@ $path = $realpath; if (is_dir($path)) { $scanner = new FileScanner($path); $list = $scanner->scan(); - $targetFile = basename($path); + $targetName = basename($path); } else { $list = [$path]; - $targetFile = FileScanner::getFileName($path); + $targetName = FileScanner::getFileName($path); } +$translator->setTargetName($targetName); $sourceFiles = []; $objectFiles = []; @@ -78,11 +79,13 @@ $translator->genExternGlobalVars($translator->getIncludeDir() . '/php_global_var // 生成所有全局变量源文件 $extensionSourceFile = $translator->getBuildDir() . '/extension.cc'; -$translator->genExtension($extensionSourceFile); +$translator->genExtension($extensionSourceFile, $targetName); $sourceFiles[] = $extensionSourceFile; // 添加 main.cc 文件 -$sourceFiles[] = ROOT_PATH . '/src/cpp/main.cc'; +if ($translator->getBuildMode() == 'bin') { + $sourceFiles[] = ROOT_PATH . '/src/cpp/main.cc'; +} // 编译所有 C++ 文件 foreach ($sourceFiles as $cppFile) { @@ -95,4 +98,4 @@ foreach ($sourceFiles as $cppFile) { } // 连接所有目标文件,生成可执行文件 -$translator->compileBinary($targetFile, $objectFiles); +$translator->build($objectFiles); diff --git a/examples/array_append.php b/examples/array_append.php new file mode 100644 index 00000000..18ad87ee --- /dev/null +++ b/examples/array_append.php @@ -0,0 +1,8 @@ +parseIncludes(); $cmd .= ' -O' . $this->optimizeLevel; $cmd .= ' -g'; $cmd .= ' -Wall'; - if ($this->climate->arguments->defined('profile')) { + if ($this->enableProfiler) { $cmd .= ' -lprofiler'; $cmd .= ' -DPPROF_ON=1'; } + + if ($this->buildMode === 'ext') { + if ($link) { + $cmd .= ' -shared'; + } else { + $cmd .= ' -fPIC -D BUILD_PHP_EXTENSION=1'; + } + } } protected function parseBinaryOpConcat(mixed $expr): string diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 7eaf0601..c9209b29 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -13,6 +13,7 @@ class Translator extends Preprocessor { use MagicMethodDetector; + protected string $targetName = 'app'; protected bool $verbose = false; protected array $unsupportedFunctions = [ 'compact', @@ -64,13 +65,22 @@ class Translator extends Preprocessor 'required' => false, 'noValue' => true, ], + 'mode' => [ + 'longPrefix' => 'mode', + 'prefix' => 'm', + 'description' => 'Build mode, -m bin(binary) or -m ext(extension), default: bin', + 'required' => false, + 'defaultValue' => 'bin', + ], ]); $this->preprocessArgvAdvanced(); $this->climate->arguments->parse(); $this->optimizeLevel = $this->climate->arguments->get('optimize'); + $this->buildMode = $this->climate->arguments->get('mode'); // $this->noLiteralStrings = $this->climate->arguments->get('noLiteralStrings'); $this->noLiteralStrings = true; + $this->enableProfiler = $this->climate->arguments->defined('profile'); $this->internalFunctions = array_flip(get_defined_functions()['internal']); if ($this->climate->arguments->defined('help')) { $this->showUsage(); @@ -99,13 +109,15 @@ class Translator extends Preprocessor $climate->tab()->out('-v, --verbose Verbose output'); $climate->tab()->out('-h, --help Show this help message'); $climate->tab()->out('-f, --force Force compile even if cache exists'); + $climate->tab()->out('-m, --mode Compilation mode, -m bin(binary) or -m ext(extension), default: bin'); $climate->tab()->out('--no-literal-strings Disable literal strings optimization'); $climate->br(); $climate->bold('EXAMPLES:'); $climate->tab()->out('./bin/compiler.php examples/hello.php'); $climate->tab()->out('./bin/compiler.php examples/bench.php -O2'); - $climate->tab()->out('./bin/compiler.php examples/bench.php -O2 -p'); + $climate->tab()->out('./bin/compiler.php examples/bench.php -O2 '); + $climate->tab()->out('./bin/compiler.php examples/extension -O2 -o myapp -m ext'); $climate->tab()->out('./bin/compiler.php examples/app.php -O3 -o myapp -v'); $climate->br(); } @@ -170,6 +182,18 @@ class Translator extends Preprocessor return self::PREFIX . 'class_entry_' . $classDef->getNamespacedName(); } + public function setTargetName(string $name): void + { + if ($this->climate->arguments->defined('output')) { + $name = $this->climate->arguments->get('output'); + } + if (!preg_match('/^[a-zA-Z0-9_]+$/', $name)) { + $this->climate->red('The target name must be a valid identifier'); + exit(1); + } + $this->targetName = $name; + } + protected function getInternalCeInfo(string $ce): array { return [ @@ -373,6 +397,12 @@ class Translator extends Preprocessor public function genExtension(string $file): void { + if ($this->buildMode == 'bin') { + if (!isset($this->nativeFunctions['main'])) { + $this->climate->red('When the build mode is a binary executable file, the `main()` function must be defined'); + exit(1); + } + } $this->localHeaders = []; $this->genClassCeList(); $code = $this->render('extension.cc.php'); @@ -399,19 +429,20 @@ class Translator extends Preprocessor return; } $cmd = $this->cppCompiler . ' -c ' . $cppFile . ' -o ' . $objectFile; - $this->addCompilationOption($cmd); + $this->addCompilationOption($cmd, false); $this->climate->comment($cmd); shell_exec($cmd); } - public function compileBinary(string $targetFile, array $objectFiles): void + public function build(array $objectFiles): void { - if ($this->climate->arguments->defined('output')) { - $targetFile = $this->climate->arguments->get('output'); - } $objectList = implode(' ', $objectFiles); + $targetFile = $this->targetName; + if ($this->buildMode == 'ext' and !str_ends_with($targetFile, '.so')) { + $targetFile .= '.so'; + } $linkCmd = $this->cppCompiler . ' ' . $objectList . ' -o ' . $targetFile . ' ' . $this->parseLdflags() . $this->parseLibs(); - $this->addCompilationOption($linkCmd); + $this->addCompilationOption($linkCmd, true); $this->climate->comment($linkCmd); shell_exec($linkCmd); } @@ -569,6 +600,11 @@ class Translator extends Preprocessor return $code; } + public function getBuildMode(): string + { + return $this->buildMode; + } + public function getArgInfoHeaderFile(string $stubFilenameWithoutExtension, bool $relative = false): string { $basename = basename($stubFilenameWithoutExtension); diff --git a/src/template/extension.cc.php b/src/template/extension.cc.php index cd2a7561..66d8ac80 100644 --- a/src/template/extension.cc.php +++ b/src/template/extension.cc.php @@ -66,7 +66,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE_END }; -static PHP_MINIT_FUNCTION(app) { +static PHP_MINIT_FUNCTION(targetName?>) { // class/interface class entries classCeList as $ce): @@ -122,11 +122,11 @@ foreach ($this->nativeConstants as $name => $constant): } -zend_module_entry app_module_entry = { +zend_module_entry targetName?>_module_entry = { STANDARD_MODULE_HEADER, - "app", + "targetName?>", ext_functions, - PHP_MINIT(app), + PHP_MINIT(targetName?>), nullptr, nullptr, nullptr, @@ -135,4 +135,6 @@ zend_module_entry app_module_entry = { STANDARD_MODULE_PROPERTIES, }; -ZEND_GET_MODULE(app); +#ifdef BUILD_PHP_EXTENSION +ZEND_GET_MODULE(targetName?>); +#endif \ No newline at end of file