Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add embed sanity check and spc-config command #573

Merged
merged 11 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,7 @@ jobs:

- name: "Run Build Tests (build)"
run: php src/globals/test-extensions.php build_cmd ${{ matrix.os }} ${{ matrix.php }}

- name: "Run Build Tests (build - embed for non-windows)"
if: matrix.os != 'windows-latest'
run: php src/globals/test-extensions.php build_embed_cmd ${{ matrix.os }} ${{ matrix.php }}
1 change: 0 additions & 1 deletion config/lib.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@
"source": "grpc",
"static-libs-unix": [
"libgrpc.a",
"libboringssl.a",
"libcares.a"
],
"lib-depends": [
Expand Down
73 changes: 73 additions & 0 deletions docs/en/guide/manual-build.md
Original file line number Diff line number Diff line change
Expand Up @@ -553,3 +553,76 @@ If you need to build multiple times locally, the following method can save you t
- If you want to rebuild once, but do not re-download the source code, you can first `rm -rf buildroot source` to delete the compilation directory and source code directory, and then rebuild.
- If you want to update a version of a dependency, you can use `bin/spc del-download <source-name>` to delete the specified source code, and then use `download <source-name>` to download it again.
- If you want to update all dependent versions, you can use `bin/spc download --clean` to delete all downloaded sources, and then download them again.

## embed usage

If you want to embed static-php into other C language programs, you can use `--build-embed` to build an embed version of PHP.

```bash
bin/spc build {your extensions} --build-embed --debug
```

Under normal circumstances, PHP embed will generate `php-config` after compilation.
For static-php, we provide `spc-config` to obtain the parameters during compilation.
In addition, when using embed SAPI (libphp.a), you need to use the same compiler as libphp, otherwise there will be a link error.

Here is the basic usage of spc-config:

```bash
# output all flags and options
bin/spc spc-config curl,zlib,phar,openssl

# output libs
bin/spc spc-config curl,zlib,phar,openssl --libs

# output includes
bin/spc spc-config curl,zlib,phar,openssl --includes
```

By default, static-php uses the following compilers on different systems:

- macOS: `clang`
- Linux (Alpine Linux): `gcc`
- Linux (glibc based distros, x86_64): `/usr/local/musl/bin/x86_64-linux-musl-gcc`
- Linux (glibc based distros, aarch64): `/usr/local/musl/bin/aarch64-linux-musl-gcc`
- FreeBSD: `clang`

Here is an example of using embed SAPI:

```c
// embed.c
#include <sapi/embed/php_embed.h>

int main(int argc,char **argv){

PHP_EMBED_START_BLOCK(argc,argv)

zend_file_handle file_handle;

zend_stream_init_filename(&file_handle,"embed.php");

if(php_execute_script(&file_handle) == FAILURE){
php_printf("Failed to execute PHP script.\n");
}

PHP_EMBED_END_BLOCK()
return 0;
}
```


```php
<?php
// embed.php
echo "Hello world!\n";
```

```bash
# compile in debian/ubuntu x86_64
/usr/local/musl/bin/x86_64-linux-musl-gcc embed.c $(bin/spc spc-config bcmath,zlib) -static -o embed
# compile in macOS/FreeBSD
clang embed.c $(bin/spc spc-config bcmath,zlib) -o embed

./embed
# out: Hello world!
```
72 changes: 72 additions & 0 deletions docs/zh/guide/manual-build.md
Original file line number Diff line number Diff line change
Expand Up @@ -493,3 +493,75 @@ static-php-cli 开放的方法非常多,文档中无法一一列举,但只
- 如果你想重新构建一次,但不重新下载源码,可以先 `rm -rf buildroot source` 删除编译目录和源码目录,然后重新构建。
- 如果你想更新某个依赖的版本,可以使用 `bin/spc del-download <source-name>` 删除指定的源码,然后使用 `download <source-name>` 重新下载。
- 如果你想更新所有依赖的版本,可以使用 `bin/spc download --clean` 删除所有下载的源码,然后重新下载。

## embed 使用

如果你想将 static-php 嵌入到其他 C 语言程序中,可以使用 `--build-embed` 构建一个 embed 版本的 PHP。

```bash
bin/spc build {your extensions} --build-embed --debug
```

在通常的情况下,PHP embed 编译后会生成 `php-config`。对于 static-php,我们提供了 `spc-config`,用于获取编译时的参数。
另外,在使用 embed SAPI(libphp.a)时,你需要使用和编译 libphp 相同的编译器,否则会出现链接错误。

下面是 spc-config 的基本用法:

```bash
# output all flags and options
bin/spc spc-config curl,zlib,phar,openssl

# output libs
bin/spc spc-config curl,zlib,phar,openssl --libs

# output includes
bin/spc spc-config curl,zlib,phar,openssl --includes
```

默认情况下,static-php 在不同系统使用的编译器分别是:

- macOS: `clang`
- Linux (Alpine Linux): `gcc`
- Linux (glibc based distros, x86_64): `/usr/local/musl/bin/x86_64-linux-musl-gcc`
- Linux (glibc based distros, aarch64): `/usr/local/musl/bin/aarch64-linux-musl-gcc`
- FreeBSD: `clang`

下面是一个使用 embed SAPI 的例子:

```c
// embed.c
#include <sapi/embed/php_embed.h>

int main(int argc,char **argv){

PHP_EMBED_START_BLOCK(argc,argv)

zend_file_handle file_handle;

zend_stream_init_filename(&file_handle,"embed.php");

if(php_execute_script(&file_handle) == FAILURE){
php_printf("Failed to execute PHP script.\n");
}

PHP_EMBED_END_BLOCK()
return 0;
}
```


```php
<?php
// embed.php
echo "Hello world!\n";
```

```bash
# compile in debian/ubuntu x86_64
/usr/local/musl/bin/x86_64-linux-musl-gcc embed.c $(bin/spc spc-config bcmath,zlib) -static -o embed
# compile in macOS/FreeBSD
clang embed.c $(bin/spc spc-config bcmath,zlib) -o embed

./embed
# out: Hello world!
```
4 changes: 3 additions & 1 deletion src/SPC/ConsoleApplication.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use SPC\command\ExtractCommand;
use SPC\command\InstallPkgCommand;
use SPC\command\MicroCombineCommand;
use SPC\command\SPCConfigCommand;
use SPC\command\SwitchPhpVersionCommand;
use Symfony\Component\Console\Application;

Expand All @@ -30,7 +31,7 @@
*/
final class ConsoleApplication extends Application
{
public const VERSION = '2.4.1';
public const VERSION = '2.4.2';

public function __construct()
{
Expand All @@ -52,6 +53,7 @@ public function __construct()
new ExtractCommand(),
new MicroCombineCommand(),
new SwitchPhpVersionCommand(),
new SPCConfigCommand(),

// Dev commands
new AllExtCommand(),
Expand Down
9 changes: 8 additions & 1 deletion src/SPC/builder/BuilderBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ abstract class BuilderBase
/** @var array<string, Extension> extensions */
protected array $exts = [];

/** @var array<int, string> extension names */
protected array $ext_list = [];

/** @var array<int, string> library names */
protected array $lib_list = [];

/** @var bool compile libs only (just mark it) */
protected bool $libs_only = false;

Expand Down Expand Up @@ -161,7 +167,7 @@ public function setLibsOnly(bool $status = true): void
* @throws FileSystemException
* @throws RuntimeException
* @throws \ReflectionException
* @throws WrongUsageException
* @throws \Throwable|WrongUsageException
* @internal
*/
public function proveExts(array $extensions, bool $skip_check_deps = false): void
Expand Down Expand Up @@ -191,6 +197,7 @@ public function proveExts(array $extensions, bool $skip_check_deps = false): voi
foreach ($this->exts as $ext) {
$ext->checkDependency();
}
$this->ext_list = $extensions;
}

/**
Expand Down
8 changes: 0 additions & 8 deletions src/SPC/builder/extension/pgsql.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,6 @@ class pgsql extends Extension
*/
public function patchBeforeConfigure(): bool
{
if ($this->builder->getPHPVersionID() >= 80400) {
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/configure',
'LIBS="-lpq',
'LIBS="-lpq -lpgport -lpgcommon -lssl -lcrypto -lz -lm'
);
return true;
}
FileSystem::replaceFileRegex(
SOURCE_PATH . '/php-src/configure',
'/-lpq/',
Expand Down
28 changes: 28 additions & 0 deletions src/SPC/builder/unix/UnixBuilderBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use SPC\store\Config;
use SPC\store\FileSystem;
use SPC\util\DependencyUtil;
use SPC\util\SPCConfigUtil;

abstract class UnixBuilderBase extends BuilderBase
{
Expand Down Expand Up @@ -128,6 +129,7 @@ public function proveLibs(array $sorted_libraries): void
foreach ($this->libs as $lib) {
$lib->calcDependency();
}
$this->lib_list = $sorted_libraries;
}

/**
Expand Down Expand Up @@ -170,6 +172,32 @@ protected function sanityCheck(int $build_target): void
}
}
}

// sanity check for embed
if (($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED) {
logger()->info('running embed sanity check');
$sample_file_path = SOURCE_PATH . '/embed-test';
if (!is_dir($sample_file_path)) {
@mkdir($sample_file_path);
}
// copy embed test files
copy(ROOT_DIR . '/src/globals/common-tests/embed.c', $sample_file_path . '/embed.c');
copy(ROOT_DIR . '/src/globals/common-tests/embed.php', $sample_file_path . '/embed.php');
$util = new SPCConfigUtil($this);
$config = $util->config($this->ext_list, $this->lib_list, $this->getOption('with-suggested-exts'), $this->getOption('with-suggested-libs'));
$lens = "{$config['cflags']} {$config['ldflags']} {$config['libs']}";
if (PHP_OS_FAMILY === 'Linux') {
$lens .= ' -static';
}
[$ret, $out] = shell()->cd($sample_file_path)->execWithResult(getenv('CC') . ' -o embed embed.c ' . $lens);
if ($ret !== 0) {
throw new RuntimeException('embed failed sanity check: build failed. Error message: ' . implode("\n", $out));
}
[$ret, $output] = shell()->cd($sample_file_path)->execWithResult('./embed');
if ($ret !== 0 || trim(implode('', $output)) !== 'hello') {
throw new RuntimeException('embed failed sanity check: run failed. Error message: ' . implode("\n", $output));
}
}
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/SPC/builder/unix/library/gettext.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ protected function build(): void
'--disable-java ' .
'--disable-c+ ' .
$extra .
'--with-included-gettext ' .
'--with-libiconv-prefix=' . BUILD_ROOT_PATH . ' ' .
'--prefix=' . BUILD_ROOT_PATH
)
Expand Down
1 change: 1 addition & 0 deletions src/SPC/builder/windows/WindowsBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ public function proveLibs(array $sorted_libraries): void
foreach ($this->libs as $lib) {
$lib->calcDependency();
}
$this->lib_list = $sorted_libraries;
}

/**
Expand Down
4 changes: 3 additions & 1 deletion src/SPC/command/BuildCliCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,9 @@ public function handle(): int

// compile stopwatch :P
$time = round(microtime(true) - START_TIME, 3);
logger()->info('Build complete, used ' . $time . ' s !');
logger()->info('');
logger()->info(' Build complete, used ' . $time . ' s !');
logger()->info('');

// ---------- When using bin/spc-alpine-docker, the build root path is different from the host system ----------
$build_root_path = BUILD_ROOT_PATH;
Expand Down
53 changes: 53 additions & 0 deletions src/SPC/command/SPCConfigCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace SPC\command;

use SPC\exception\RuntimeException;
use SPC\util\SPCConfigUtil;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;

#[AsCommand('spc-config', 'Build dependencies')]
class SPCConfigCommand extends BuildCommand
{
protected bool $no_motd = true;

public function configure(): void
{
$this->addArgument('extensions', InputArgument::OPTIONAL, 'The extensions will be compiled, comma separated');
$this->addOption('with-libs', null, InputOption::VALUE_REQUIRED, 'add additional libraries, comma separated', '');
$this->addOption('with-suggested-libs', 'L', null, 'Build with suggested libs for selected exts and libs');
$this->addOption('with-suggested-exts', 'E', null, 'Build with suggested extensions for selected exts');
$this->addOption('includes', null, null, 'Add additional include path');
$this->addOption('libs', null, null, 'Add additional libs path');
}

/**
* @throws RuntimeException
*/
public function handle(): int
{
// transform string to array
$libraries = array_map('trim', array_filter(explode(',', $this->getOption('with-libs'))));
// transform string to array
$extensions = $this->getArgument('extensions') ? $this->parseExtensionList($this->getArgument('extensions')) : [];
$include_suggest_ext = $this->getOption('with-suggested-exts');
$include_suggest_lib = $this->getOption('with-suggested-libs');

$util = new SPCConfigUtil(null, $this->input);
$config = $util->config($extensions, $libraries, $include_suggest_ext, $include_suggest_lib);

if ($this->getOption('includes')) {
$this->output->writeln($config['cflags']);
} elseif ($this->getOption('libs')) {
$this->output->writeln("{$config['ldflags']} {$config['libs']}");
} else {
$this->output->writeln("{$config['cflags']} {$config['ldflags']} {$config['libs']}");
}

return 0;
}
}
Loading
Loading