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