feat(compiler): implement external library import stub generation and handling

- Rename stubLibrary to stubImportLibrary and add externalImportStubFiles property
- Create LibraryImportStubGenerator to generate combined stub files with @import-library annotation
- Update documentation to reflect new import library mechanism without @typephp-library annotation
- Modify build pipeline to exclude generated stub from input files and generate library import stubs
- Add TYPEPHP_LIBRARYNAME_IMPORT macros for importing functions from external libraries
- Update function definition entities to track importLibrary instead of library ownership
- Refactor preprocessing to detect @import-library annotation and manage external stub files
- Implement proper handling of imported functions with different macro prefixes in declarations
pull/34/head
韩天峰 1 month ago
parent 44ba3d4d1c
commit 3ac611220e
  1. 13
      docs/MIXED_CPP_PHP.md
  2. 1
      examples/prime/php-src/vector.stub.php
  3. 1
      phpunit/code/compiler_api/default_argument_abi.stub.php
  4. 13
      phpunit/code/compiler_api/library_import_native.stub.php
  5. 28
      phpunit/code/compiler_api/library_import_php.php
  6. 13
      phpunit/code/compiler_api/prime2.stub.php
  7. 112
      phpunit/src/CompilerBaseApiTest.php
  8. 17
      src/Build/SourcePipelineTrait.php
  9. 7
      src/CompilerBase.php
  10. 4
      src/Entity/FunctionDef.php
  11. 147
      src/Generator/LibraryImportStubGenerator.php
  12. 49
      src/Preprocessor.php
  13. 48
      src/Translator.php

@ -686,25 +686,24 @@ void php_print(php::Str msg); // void 不支持
### 3. .stub.php 文件要求
当 stub 中的函数由一个 TypePHP 库提供时,在文件顶部声明库名
库项目中的 `.stub.php` 用于声明由 C++ 实现的函数,不需要添加库名注解
```php
<?php
/** @typephp-library prime2 */
function vector_new(int $size, bool $init = false): mixed {}
```
- 当当前 target 为 `prime2` 且使用 `-m lib` 构建时,这些 `php_*` 函数按库 ABI 导出。
- 当其他 target 引用该 stub 时,函数按 `prime2` 库 ABI 导入,且链接阶段自动加入 `prime2` 库。
- 注解只允许用于 `.stub.php` 文件;未声明时,stub 函数默认由当前 target 实现。
`-m lib` 会把库项目的 `.php` 和本地 `.stub.php` 接口汇总到 `<target>.stub.php`。该发布 stub 自动带有 `@import-library`,其他项目加载后,其中的所有函数和类方法都按外部库 ABI 导入。库名由文件名推导,例如 `prime2.stub.php` 对应 `prime2` 库。
外部 stub 中的类会在消费项目中生成类注册、属性和常量实体,但不生成 `php_*` 方法本体;方法本体由动态库提供。
Property hook 同样按方法处理:发布 stub 保留 `get`/`set` 的声明并移除实现,消费项目生成属性实体,hook 的 getter/setter `php_*` 实现从动态库导入。
`php_<target>_func_decl.h``php_<target>_data_decl.h` 都是 TypePHP 构建过程的内部生成文件,不是库的对外开发头文件。
`func_decl.h``-m lib` 构建时还会被强制包含,用于给当前 target 的 `php_*` C++ ABI 函数添加平台导出标记;`data_decl.h` 仅在 target 内部声明全局变量、字面量、常量对象和运行时映射等数据。
发布 TypePHP 库时,对外提供:
- 描述 TypePHP 函数接口和所属库的 `.stub.php`
- `-m lib` 自动生成的 `<target>.stub.php`
- Windows 平台的 `.dll` 和导入库 `.lib`
- Linux 等平台的 `.so`

@ -1,5 +1,4 @@
<?php
/** @typephp-library prime2 */
function vector_new(int $size, bool $init = false): mixed
{

@ -1,5 +1,4 @@
<?php
/** @typephp-library prime2 */
const EXPORTED_ABI_INT = 42;
const EXPORTED_ABI_STRING = 'internal';

@ -0,0 +1,13 @@
<?php
namespace LibraryApi;
class NativeCounter
{
public const int INITIAL = 3;
public int $value = self::INITIAL;
public function bump(int $amount): int {}
}
function native_value(string $name = 'typephp'): string {}

@ -0,0 +1,28 @@
<?php
namespace LibraryApi;
class Counter
{
public const int STEP = 2;
public int $value = 1;
public int $doubled {
get {
return $this->value * 2;
}
set(int $value) {
$this->value = intdiv($value, 2);
}
}
public function add(int $amount = self::STEP): int
{
$this->value += $amount;
return $this->value;
}
}
function twice(int $value): int
{
return $value * 2;
}

@ -0,0 +1,13 @@
<?php
/** @import-library */
function exported_defaults(
string $text = 'hello',
array $options = ['mode' => 'fast'],
mixed $value = null,
int $count = 0,
bool $enabled = false
): array {}
function exported_variadic(string ...$values): array {}

@ -883,34 +883,128 @@ YAML);
$this->assertStringContainsString('php::Array php_exported_variadic_arg_0_default_value() {', $extension);
}
public function testTypePhpLibraryStubFunctionsAreImportedOutsideOwningTarget(): void
public function testExternalImportStubFunctionsAreAlwaysImported(): void
{
global $translator;
$translator = $this->compiler;
$this->setPropertyValue('buildMode', CompilerBase::BUILD_MODE_LIB);
$this->compiler->setTargetName('consumer');
$this->compiler->setTargetName('prime2');
$testFile = ROOT_PATH . '/phpunit/code/compiler_api/default_argument_abi.stub.php';
$testFile = ROOT_PATH . '/phpunit/code/compiler_api/prime2.stub.php';
$this->compiler->addFiles([$testFile]);
$this->compiler->prepareFile($testFile);
$this->compiler->convertFile($testFile);
$headerFile = $this->testDir . '/php_consumer_func_decl.h';
$headerFile = $this->testDir . '/php_prime2_func_decl.h';
$this->compiler->genFunctionDeclarations($headerFile);
$header = file_get_contents($headerFile);
$this->assertStringContainsString('TYPEPHP_PRIME2_API __declspec(dllimport)', $header);
$this->assertStringContainsString('TYPEPHP_CONSUMER_API __declspec(dllexport)', $header);
$this->assertStringContainsString('TYPEPHP_PRIME2_IMPORT __declspec(dllimport)', $header);
$this->assertStringContainsString('TYPEPHP_PRIME2_API __declspec(dllexport)', $header);
$this->assertStringContainsString(
'TYPEPHP_PRIME2_API php::Array php_exported_defaults(',
'TYPEPHP_PRIME2_IMPORT php::Array php_exported_defaults(',
$header
);
$this->assertSame(['prime2'], $this->getPropertyValue('linkLibs'));
$this->assertSame('', $this->invokeMethod('genDefaultArgumentHelperDefinitions'));
$options = $this->invokeMethod('getCompileCommandOptions');
$this->assertContains('TYPEPHP_CONSUMER_EXPORTS=1', $options['user_defines']);
$this->assertNotContains('TYPEPHP_PRIME2_EXPORTS=1', $options['user_defines']);
$this->assertContains('TYPEPHP_PRIME2_EXPORTS=1', $options['user_defines']);
}
public function testLibraryImportStubCombinesPhpFunctionsClassesAndNativeStubs(): void
{
global $translator;
$translator = $this->compiler;
$this->setPropertyValue('buildMode', CompilerBase::BUILD_MODE_LIB);
$this->setPropertyValue('outputDir', $this->testDir);
$this->compiler->setTargetName('prime2');
$files = [
ROOT_PATH . '/phpunit/code/compiler_api/library_import_php.php',
ROOT_PATH . '/phpunit/code/compiler_api/library_import_native.stub.php',
];
$this->compiler->addFiles($files);
foreach ($files as $file) {
$this->compiler->prepareFile($file);
$this->compiler->convertFile($file);
}
$stubFile = $this->compiler->genLibraryImportStub($files);
$stub = file_get_contents($stubFile);
$this->assertSame($this->testDir . '/prime2.stub.php', $stubFile);
$this->assertStringContainsString('/** @import-library */', $stub);
$this->assertStringContainsString('namespace LibraryApi;', $stub);
$this->assertStringContainsString('class Counter', $stub);
$this->assertStringContainsString('public const int STEP = 2;', $stub);
$this->assertStringContainsString('public int $value = 1;', $stub);
$this->assertMatchesRegularExpression(
'/public int \$doubled\s*\{\s*get\s*\{\s*\}\s*set\(int \$value\)\s*\{\s*\}\s*\}/s',
$stub,
);
$this->assertStringContainsString('function add(int $amount = self::STEP): int', $stub);
$this->assertStringContainsString('function twice(int $value): int', $stub);
$this->assertStringContainsString('function native_value(string $name = \'typephp\'): string', $stub);
$this->assertStringContainsString('class NativeCounter', $stub);
$this->assertStringContainsString('function bump(int $amount): int', $stub);
$this->assertStringNotContainsString('return $this->value', $stub);
$this->assertStringNotContainsString('intdiv($value, 2)', $stub);
$this->assertStringNotContainsString('return $value * 2', $stub);
$consumerDir = $this->testDir . '/consumer';
mkdir($consumerDir, 0777, true);
$consumer = CompilerTest::create($consumerDir);
$translator = $consumer;
$consumerRef = new \ReflectionClass($consumer);
$buildMode = $consumerRef->getProperty('buildMode');
$buildMode->setAccessible(true);
$buildMode->setValue($consumer, CompilerBase::BUILD_MODE_BIN);
$consumer->setTargetName('consumer');
$consumer->addFiles([$stubFile]);
$consumer->prepareFile($stubFile);
$stubCpp = $consumer->convertFile($stubFile);
$headerFile = $consumerDir . '/php_consumer_func_decl.h';
$consumer->genFunctionDeclarations($headerFile);
$header = file_get_contents($headerFile);
$this->assertStringContainsString(
'TYPEPHP_PRIME2_IMPORT php::Int php_libraryapi__counter__add(',
$header,
);
$this->assertStringContainsString(
'TYPEPHP_PRIME2_IMPORT php::Int php_libraryapi__twice(',
$header,
);
$this->assertStringContainsString(
'TYPEPHP_PRIME2_IMPORT php::Str php_libraryapi__native_value(',
$header,
);
$this->assertStringContainsString(
'TYPEPHP_PRIME2_IMPORT php::Int php_libraryapi__nativecounter__bump(',
$header,
);
$this->assertStringContainsString(
'TYPEPHP_PRIME2_IMPORT php::Int php_libraryapi__counter____typephp_property_get_646f75626c6564(',
$header,
);
$this->assertStringContainsString(
'TYPEPHP_PRIME2_IMPORT void php_libraryapi__counter____typephp_property_set_646f75626c6564(',
$header,
);
$stubCppCode = file_get_contents($stubCpp);
$this->assertStringContainsString('ZEND_METHOD(LibraryApi_Counter, add)', $stubCppCode);
$this->assertStringContainsString('php_libraryapi__counter__add(this_, arg_amount)', $stubCppCode);
$this->assertStringNotContainsString(
'php::Int php_libraryapi__counter__add(php::Object &this_',
$stubCppCode,
);
$arginfoFile = $consumer->getArgInfoHeaderFile($stubFile);
$arginfo = file_get_contents($arginfoFile);
$this->assertStringContainsString('const_STEP_value', $arginfo);
$this->assertStringContainsString('property_value_default_value', $arginfo);
$this->assertSame(['prime2'], $consumer->getLinkLibs());
}
public function testLibraryCompileOptionsExportOnlyPublicApiByDefault(): void

@ -56,6 +56,19 @@ trait SourcePipelineTrait
// 在所有配置加载完成后,应用命令行参数(确保优先级最高)
$this->applyCommandLineArguments();
// The generated public import stub is an output artifact, not an input
// of the library that produced it. Exclude a previous build's copy when
// a project scans its output directory recursively.
if ($this->isBuildModeLib()) {
$generatedStub = realpath($this->getLibraryImportStubFile());
if ($generatedStub !== false) {
$list = array_values(array_filter(
$list,
static fn(string $file): bool => realpath($file) !== $generatedStub,
));
}
}
return $this->filterIgnoredFiles($list);
}
@ -193,6 +206,10 @@ trait SourcePipelineTrait
$this->stop('No valid source file found');
}
if ($this->isBuildModeLib()) {
$this->genLibraryImportStub($files);
}
// 生成构建期内部头文件:函数声明、运行时数据声明
$this->genFunctionDeclarations($this->getIncludeDir() . "/php_{$this->targetName}_func_decl.h");
$this->genDataDeclarations($this->getIncludeDir() . "/php_{$this->targetName}_data_decl.h");

@ -369,7 +369,10 @@ class CompilerBase implements PropertyAccessContext
protected int $debugLine = 0;
protected CLImate $climate;
protected bool $stubFile = false;
protected string $stubLibrary = '';
protected string $stubImportLibrary = '';
/** @var array<string, true> */
protected array $externalImportStubFiles = [];
protected bool $enableProfiler = false;
protected bool $noProgress = false;
protected bool $forTest = false;
@ -1000,7 +1003,7 @@ class CompilerBase implements PropertyAccessContext
$this->classesDefineInFile = [];
$this->interfacesDefineInFile = [];
$this->functionDefineInFile = [];
$this->stubLibrary = '';
$this->stubImportLibrary = '';
}
protected function resetNamespace(): void

@ -25,8 +25,8 @@ class FunctionDef
public string $namespace;
public bool $method = false;
public bool $stub = false;
/** Library that owns this stub function, as declared by @typephp-library. */
public string $library = '';
/** External library imported by the stub containing this function. */
public string $importLibrary = '';
public bool $returnTypeUndeclared = false;
public bool $returnsByRef = false;
public bool $generator = false;

@ -0,0 +1,147 @@
<?php
/**
* This file is part of TypePHP.
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
namespace TypePhp\Generator;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\NameResolver;
use PhpParser\Parser;
use PhpParser\PrettyPrinter;
final class LibraryImportStubGenerator
{
public function __construct(
private readonly Parser $parser,
private readonly PrettyPrinter $printer,
) {
}
/**
* @param array<string> $files
* @param array<string, true> $externalImportStubFiles
*/
public function generate(array $files, array $externalImportStubFiles): string
{
/** @var array<string, array<Node\Stmt>> $namespaces */
$namespaces = [];
foreach ($files as $file) {
$realFile = realpath($file);
if ($realFile === false || isset($externalImportStubFiles[$realFile])) {
continue;
}
if (pathinfo($realFile, PATHINFO_EXTENSION) !== 'php') {
continue;
}
$code = file_get_contents($realFile);
if ($code === false) {
throw new \RuntimeException('Can not read file: ' . $realFile);
}
$ast = $this->parser->parse($code) ?? [];
$traverser = new NodeTraverser();
$traverser->addVisitor(new NameResolver());
$ast = $traverser->traverse($ast);
foreach ($ast as $stmt) {
if ($stmt instanceof Node\Stmt\Namespace_) {
$namespace = $stmt->name?->toString() ?? '';
$this->appendDeclarations($namespaces, $namespace, $stmt->stmts);
continue;
}
$this->appendDeclarations($namespaces, '', [$stmt]);
}
}
$namespaceNodes = [];
foreach ($namespaces as $namespace => $stmts) {
if ($stmts === []) {
continue;
}
$namespaceNodes[] = new Node\Stmt\Namespace_(
$namespace === '' ? null : new Node\Name($namespace),
$stmts,
);
}
$code = "<?php\n\n/** @import-library */\n\n";
if ($namespaceNodes !== []) {
$code .= $this->printer->prettyPrint($namespaceNodes) . "\n";
}
return $code;
}
/**
* @param array<string, array<Node\Stmt>> $namespaces
* @param array<Node\Stmt> $stmts
*/
private function appendDeclarations(array &$namespaces, string $namespace, array $stmts): void
{
foreach ($stmts as $stmt) {
if ($namespace === '' && $stmt instanceof Node\Stmt\Function_
&& strtolower($stmt->name->toString()) === 'main') {
continue;
}
$declaration = $this->makeImportDeclaration($stmt);
if ($declaration !== null) {
$namespaces[$namespace][] = $declaration;
}
}
}
private function makeImportDeclaration(Node\Stmt $stmt): ?Node\Stmt
{
$comments = array_filter(
$stmt->getComments(),
static fn(\PhpParser\Comment $comment): bool => preg_match(
'/@import-library\b/',
$comment->getText(),
) !== 1,
);
$stmt->setAttribute('comments', array_values($comments));
if ($stmt instanceof Node\Stmt\Function_) {
$stmt->stmts = [];
return $stmt;
}
if ($stmt instanceof Node\Stmt\ClassLike) {
$members = [];
foreach ($stmt->stmts as $member) {
if ($member instanceof Node\Stmt\ClassMethod) {
if (!($member->flags & Modifiers::ABSTRACT)
&& !($stmt instanceof Node\Stmt\Interface_)) {
$member->stmts = [];
}
$members[] = $member;
continue;
}
if ($member instanceof Node\Stmt\Property) {
foreach ($member->hooks as $hook) {
if ($hook->body !== null) {
$hook->body = [];
}
}
$members[] = $member;
continue;
}
if ($member instanceof Node\Stmt\ClassConst
|| $member instanceof Node\Stmt\TraitUse
|| $member instanceof Node\Stmt\EnumCase) {
$members[] = $member;
}
}
$stmt->stmts = $members;
return $stmt;
}
return null;
}
}

@ -125,10 +125,14 @@ class Preprocessor extends CompilerBase
throw new SyntaxError($e->getMessage(), $e->getCode());
}
$this->stubLibrary = $this->stubFile ? $this->parseTypePhpLibrary($ast) : '';
if ($this->stubLibrary !== '' && $this->stubLibrary !== $this->targetName
&& !in_array($this->stubLibrary, $this->linkLibs, true)) {
$this->linkLibs[] = $this->stubLibrary;
$this->stubImportLibrary = $this->stubFile && $this->hasLibraryImportAnnotation($ast)
? $this->getExternalImportLibraryName($this->file)
: '';
if ($this->stubImportLibrary !== '') {
$this->externalImportStubFiles[$this->file] = true;
}
if ($this->stubImportLibrary !== '' && !in_array($this->stubImportLibrary, $this->linkLibs, true)) {
$this->linkLibs[] = $this->stubImportLibrary;
}
$traverser = new NodeTraverser();
@ -199,27 +203,28 @@ class Preprocessor extends CompilerBase
}
/** @param array<Node> $stmts */
private function parseTypePhpLibrary(array $stmts): string
private function hasLibraryImportAnnotation(array $stmts): bool
{
$library = '';
foreach ($stmts as $stmt) {
foreach ($stmt->getComments() as $comment) {
if (!preg_match('/@typephp-library\s+([^\s*]+)/', $comment->getText(), $matches)) {
continue;
if (preg_match('/@import-library\b/', $comment->getText()) === 1) {
return true;
}
$candidate = str_replace('-', '_', trim($matches[1]));
if (!preg_match('/^[A-Za-z_][A-Za-z0-9_]*$/', $candidate)) {
$this->fatalError($stmt, 'Invalid @typephp-library name `' . $matches[1] . '`');
}
if ($library !== '' && $library !== $candidate) {
$this->fatalError($stmt, 'A stub file cannot declare multiple @typephp-library values');
}
$library = $candidate;
return false;
}
private function getExternalImportLibraryName(string $stubFile): string
{
$name = basename($stubFile, '.stub.php');
$name = str_replace(['-', '*'], '_', $name);
if (!preg_match('/^[A-Za-z_][A-Za-z0-9_]*$/', $name)) {
throw new SyntaxError('Invalid external import stub filename `' . basename($stubFile) . '`');
}
return $library;
return $name;
}
protected function findSymbolUsing(NodeAbstract $ast)
@ -356,8 +361,9 @@ class Preprocessor extends CompilerBase
}
$phpName = $param->var->name;
$name = $this->escapeVarName($phpName);
// .stub 存根定义 C++ Native 函数,必须设置函数的参数类型
if ($this->stubFile and !$param->type) {
// Local stubs define C++ native functions and require explicit ABI types.
// Generated external stubs may preserve an untyped PHP declaration as php::Var.
if ($this->stubFile && $this->stubImportLibrary === '' && !$param->type) {
throw new \RuntimeException('No type for ' . $phpName);
}
// 构造方法属性定义语法(Constructor Property Promotion)
@ -456,8 +462,9 @@ class Preprocessor extends CompilerBase
protected function parseFunctionDecl(Node\Stmt\Function_|Node\Stmt\ClassMethod $v): FunctionDef
{
// .stub 存根定义 C++ Native 函数,必须设置返回值类型
if ($this->stubFile and !$v->returnType) {
// Local stubs define C++ native functions and require an explicit ABI return type.
// Generated external stubs may preserve an untyped PHP declaration as php::Var.
if ($this->stubFile && $this->stubImportLibrary === '' && !$v->returnType) {
// 以下魔术方法都不能声明返回值类型 __construct()/__destruct()/__clone()
if (($this->method and !in_array($this->method, ['__construct', '__destruct', '__clone'])) or !$this->method) {
$name = $this->class ? $this->class . '::' . $v->name : $v->name;
@ -498,7 +505,7 @@ class Preprocessor extends CompilerBase
// the consuming class when a trait method is flattened into a class.
$functionDef->returnTypeKeyword = $returnTypeKeyword;
$functionDef->stub = $this->stubFile;
$functionDef->library = $this->stubLibrary;
$functionDef->importLibrary = $this->stubImportLibrary;
$functionDef->returnTypeUndeclared = $v->returnType === null;
$functionDef->returnsByRef = $v->byRef;
if ($this->containsYield($v)) {

@ -31,6 +31,7 @@ use TypePhp\Entity\PropertyDef;
use TypePhp\Exception\Redo;
use TypePhp\Exception\Skip;
use TypePhp\Generator\DefaultArgumentGenerator;
use TypePhp\Generator\LibraryImportStubGenerator;
use TypePhp\Generator\Symbol;
use TypePhp\Metadata\Constants;
use TypePhp\Platform\PlatformFactory;
@ -632,6 +633,22 @@ class Translator extends Preprocessor
return $targetFile;
}
public function getLibraryImportStubFile(): string
{
$directory = $this->outputDir !== '' ? $this->outputDir : (getcwd() ?: $this->rootPath);
return rtrim($directory, '/\\') . '/' . $this->targetName . '.stub.php';
}
/** @param array<string> $files */
public function genLibraryImportStub(array $files): string
{
$file = $this->getLibraryImportStubFile();
$generator = new LibraryImportStubGenerator($this->parser, $this->printer);
$this->writeFile($file, $generator->generate($files, $this->externalImportStubFiles));
$this->climate->info('generate library import stub: ' . $this->getRelativePath($file));
return $file;
}
public function preprocessArgvAdvanced(): void
{
global $argv;
@ -1545,17 +1562,17 @@ CODE;
$code .= '#include <phpx.h>' . PHP_EOL;
$code .= '#include <typephp_fiber_generator.h>' . PHP_EOL;
$apiLibraries = [];
if ($this->isBuildModeLib()) {
$apiLibraries[$this->targetName] = true;
$code .= $this->genLibraryApiMacro($this->targetName);
}
$importLibraries = [];
foreach ($this->symbols->functions() as $function) {
if ($this->isImportedFunction($function)) {
$apiLibraries[$function->library] = true;
$importLibraries[$function->importLibrary] = true;
}
}
foreach (array_keys($apiLibraries) as $library) {
$code .= $this->genLibraryApiMacro($library);
foreach (array_keys($importLibraries) as $library) {
$code .= $this->genLibraryImportMacro($library);
}
$code .= $this->genDefaultArgumentHelperDeclarations();
@ -1615,10 +1632,22 @@ CODE;
return $code . "#endif\n\n";
}
protected function genLibraryImportMacro(string $library): string
{
$importMacro = $this->getNamedLibraryImportMacroName($library);
$code = "#if defined(_WIN32)\n";
$code .= "# define {$importMacro} __declspec(dllimport)\n";
$code .= "#elif defined(__GNUC__) && __GNUC__ >= 4\n";
$code .= "# define {$importMacro} __attribute__((visibility(\"default\")))\n";
$code .= "#else\n";
$code .= "# define {$importMacro}\n";
return $code . "#endif\n\n";
}
protected function getFunctionDeclarationPrefix(FunctionDef $function): string
{
if ($this->isImportedFunction($function)) {
return $this->getNamedLibraryApiMacroName($function->library) . ' ';
return $this->getNamedLibraryImportMacroName($function->importLibrary) . ' ';
}
if ($this->isBuildModeLib()) {
return $this->getLibraryApiMacroName() . ' ';
@ -1628,7 +1657,7 @@ CODE;
protected function isImportedFunction(FunctionDef $function): bool
{
return $function->library !== '' && $function->library !== $this->targetName;
return $function->importLibrary !== '';
}
protected function getNamedLibraryApiMacroName(string $library): string
@ -1636,6 +1665,11 @@ CODE;
return 'TYPEPHP_' . strtoupper($library) . '_API';
}
protected function getNamedLibraryImportMacroName(string $library): string
{
return 'TYPEPHP_' . strtoupper($library) . '_IMPORT';
}
protected function getNamedLibraryExportsMacroName(string $library): string
{
return 'TYPEPHP_' . strtoupper($library) . '_EXPORTS';

Loading…
Cancel
Save