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

[feat] Add gnu based static binary support #592

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
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
104 changes: 104 additions & 0 deletions bin/spc-gnu-docker
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/env bash

# This file is using docker to run commands
set -e

# Detect docker can run
if ! which docker >/dev/null; then
echo "Docker is not installed, please install docker first !"
exit 1
fi
DOCKER_EXECUTABLE="docker"
# shellcheck disable=SC2046
if [ $(id -u) -ne 0 ]; then
if ! docker info > /dev/null 2>&1; then
if [ "$SPC_USE_SUDO" != "yes" ]; then
echo "Docker command requires sudo"
# shellcheck disable=SC2039
echo -n 'To use sudo to run docker, run "export SPC_USE_SUDO=yes" and run command again'
exit 1
fi
DOCKER_EXECUTABLE="sudo docker"
fi
fi



# to check if qemu-docker run
if [ "$SPC_USE_ARCH" = "" ]; then
SPC_USE_ARCH=current
fi
case $SPC_USE_ARCH in
current)
BASE_ARCH=$(uname -m)
if [ "$BASE_ARCH" = "arm64" ]; then
BASE_ARCH=aarch64
fi
;;
aarch64)
BASE_ARCH=aarch64
# shellcheck disable=SC2039
echo -e "\e[033m* Using different arch needs to setup qemu-static for docker !\e[0m"
$DOCKER_EXECUTABLE run --rm --privileged multiarch/qemu-user-static:register --reset > /dev/null
;;
*)
echo "Current arch is not supported to run in docker: $SPC_USE_ARCH"
exit 1
;;
esac

# Detect docker env is setup
if ! $DOCKER_EXECUTABLE images | grep -q cwcc-spc-gnu-$SPC_USE_ARCH; then
echo "Docker container does not exist. Building docker image ..."
$DOCKER_EXECUTABLE build -t cwcc-spc-gnu-$SPC_USE_ARCH -f- . <<EOF
FROM centos:7
RUN sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/*.repo && \
sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/*.repo && \
sed -i 's/^mirrorlist=http/#mirrorlist=http/g' /etc/yum.repos.d/*.repo
RUN yum clean all && \
yum makecache && \
yum update -y

RUN curl -o cmake.tgz -fsSL https://github.com/Kitware/CMake/releases/download/v3.31.4/cmake-3.31.4-linux-$BASE_ARCH.tar.gz && \
mkdir /cmake && \
tar -xzf cmake.tgz -C /cmake --strip-components 1

WORKDIR /app
ADD ./src /app/src
COPY ./composer.* /app/
ADD ./bin/setup-runtime /app/bin/setup-runtime
ADD ./bin/spc /app/bin/spc
RUN /app/bin/setup-runtime
RUN /app/bin/php /app/bin/composer install --no-dev --classmap-authoritative
ENV PATH="/app/bin:/cmake/bin:$PATH"
ENV SPC_SKIP_DOCTOR_CHECK_ITEMS="if musl-wrapper is installed,if musl-cross-make is installed"


ADD ./config/env.ini /app/config/env.ini
RUN bin/spc doctor --auto-fix --debug
EOF
fi

# Check if in ci (local terminal can execute with -it)
if [ -t 0 ]; then
INTERACT=-it
else
INTERACT=''
fi

# Mounting volumes
MOUNT_LIST=""
# shellcheck disable=SC2089
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/config:/app/config"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/src:/app/src"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/buildroot:/app/buildroot"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/source:/app/source"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/dist:/app/dist"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/downloads:/app/downloads"
MOUNT_LIST="$MOUNT_LIST -v ""$(pwd)""/pkgroot:/app/pkgroot"

# Run docker
# shellcheck disable=SC2068
# shellcheck disable=SC2086
# shellcheck disable=SC2090
$DOCKER_EXECUTABLE run --rm $INTERACT -e SPC_FIX_DEPLOY_ROOT="$(pwd)" $MOUNT_LIST cwcc-spc-gnu-$SPC_USE_ARCH bin/spc $@
12 changes: 7 additions & 5 deletions docs/.vitepress/sidebar.en.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
export default {
'/en/guide/': [
{
text: 'Guide',
text: 'Basic Build Guides',
items: [
{text: 'Guide', link: '/en/guide/'},
{text: 'Actions Build', link: '/en/guide/action-build'},
{text: 'Manual Build', link: '/en/guide/manual-build'},
{text: 'Extension List', link: '/en/guide/extensions'},
{text: 'Build (Local)', link: '/en/guide/manual-build'},
{text: 'Build (CI)', link: '/en/guide/action-build'},
{text: 'Supported Extensions', link: '/en/guide/extensions'},
{text: 'Extension Notes', link: '/en/guide/extension-notes'},
{text: 'Command Generator', link: '/en/guide/cli-generator'},
{text: 'Build Command Generator', link: '/en/guide/cli-generator'},
{text: 'Environment Variables', link: '/en/guide/env-vars', collapsed: true,},
{text: 'Dependency Table', link: '/en/guide/deps-map'},
]
},
{
text: 'Extended Build Guides',
items: [
{text: 'Troubleshooting', link: '/en/guide/troubleshooting'},
{text: 'Build on Windows', link: '/en/guide/build-on-windows'},
{text: 'Build with GNU libc', link: '/en/guide/build-with-glibc'},
],
}
],
Expand Down
4 changes: 3 additions & 1 deletion docs/.vitepress/sidebar.zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ export default {
text: '构建指南',
items: [
{text: '指南', link: '/zh/guide/'},
{text: 'Actions 构建', link: '/zh/guide/action-build'},
{text: '本地构建', link: '/zh/guide/manual-build'},
{text: 'Actions 构建', link: '/zh/guide/action-build'},
{text: '扩展列表', link: '/zh/guide/extensions'},
{text: '扩展注意事项', link: '/zh/guide/extension-notes'},
{text: '编译命令生成器', link: '/zh/guide/cli-generator'},
Expand All @@ -14,9 +14,11 @@ export default {
]
},
{
text: '扩展构建指南',
items: [
{text: '故障排除', link: '/zh/guide/troubleshooting'},
{text: '在 Windows 上构建', link: '/zh/guide/build-on-windows'},
{text: '构建 GNU libc 兼容的二进制', link: '/zh/guide/build-with-glibc'},
],
}
],
Expand Down
87 changes: 87 additions & 0 deletions docs/en/guide/build-with-glibc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Build glibc Compatible Linux Binary

## Why Build glibc Compatible Binary

Currently, the binaries built by static-php-cli on Linux by default are based on musl-libc (statically linked).
musl-libc is a lightweight libc implementation
that aims to be compatible with glibc and provides good support for pure static linking.
This means that the compiled static PHP executable can be used on almost any Linux distribution without worrying about the versions of libc, libstdc++, etc.

However, there are some issues with pure static linking of musl-libc binaries on Linux:

- The `dl()` function in PHP cannot be used to load dynamic libraries and external PHP extensions.
- The FFI extension in PHP cannot be used.
- In some extreme cases, performance issues may occur. See [musl-libc performance issues](https://github.com/php/php-src/issues/13648).

Different Linux distributions use different default libc.
For example, Alpine Linux uses musl libc, while most Linux distributions use glibc.
However, even so, we cannot directly use any distribution using glibc to build portable static binaries because glibc has some issues:

- Binaries built with gcc and other tools on newer versions of distributions cannot run on older versions of distributions.
- glibc is not recommended to be statically linked because some of its features require the support of dynamic libraries.

However, we can use Docker to solve this problem.
The final output is a binary **dynamically linked with glibc** and some necessary libraries,
but **statically linked with all other dependencies**.

1. Use an older version of a Linux distribution (such as CentOS 7.x), which has an older version of glibc but can run on most modern Linux distributions.
2. Build the static binary of PHP in this container so that it can run on most modern Linux distributions.

> Using glibc static binaries can run on most modern Linux distributions but cannot run on musl libc distributions, such as CentOS 6, Alpine Linux, etc.

## Build glibc Compatible Linux Binary

The latest version of static-php-cli includes the `bin/spc-gnu-docker` script,
which can create a CentOS 7.x (glibc-2.17) Docker container with one click and build a glibc compatible PHP static binary in the container.

First, clone the repository of this project and add the following content to the `config/env.custom.ini` file:

```ini
; Modify this file name to `env.custom.ini`, and run `bin/spc-gnu-docker`,
; you can compile a GNU libc based static binary !
[global]
SPC_SKIP_DOCTOR_CHECK_ITEMS="if musl-wrapper is installed,if musl-cross-make is installed"

[linux]
CC=gcc
CXX=g++
AR=ar
LD=ld
SPC_DEFAULT_C_FLAGS=-fPIC
SPC_NO_MUSL_PATH=yes
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-Wl,-O1 -pie"
SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil"
```

Then, run the following command once.
The first run will take a long time because it needs to download the CentOS 7.x image and some build tools.

```bash
bin/spc-gnu-docker
```

After the image is built, you will see the same command help menu as `bin/spc`, which means the container is ready.

After the container is ready, you can refer to the [local build](./manual-build) section to build your PHP static binary.
Just replace `bin/spc` or `./spc` with `bin/spc-gnu-docker`.

Unlike the default build, when building in the glibc environment, you **must** add the parameter `--libc=glibc`, such as:

```bash
bin/spc-gnu-docker --libc=glibc build bcmath,ctype,openssl,pdo,phar,posix,session,tokenizer,xml,zip --build-cli --debug
```

## Notes

In rare cases, glibc-based static PHP may encounter segment faults and other errors, but there are currently few examples.
If you encounter any issues, please submit an issue.

glibc build is an extended feature and is not part of the default static-php support.
If you have related issues or requirements, please indicate that you are building based on glibc when submitting an issue.

If you need to build glibc-based binaries without using Docker,
please refer to the `bin/spc-gnu-docker` script to manually create a similar environment.

Since glibc binaries are not the main goal of the project,
we generally do not test the compatibility of various libraries and extensions under glibc.
If any specific library builds successfully on musl-libc but fails on glibc, please submit an issue.
15 changes: 15 additions & 0 deletions docs/en/guide/env-vars.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@ Or, if you need to modify an environment variable for a long time, you can modif

For example, if you need to modify the `./configure` command for compiling PHP, you can find the `SPC_CMD_PREFIX_PHP_CONFIGURE` environment variable in the `config/env.ini` file, and then modify its value.

If your build conditions are more complex and require multiple `env.ini` files to switch,
we recommend that you use the `config/env.custom.ini` file.
In this way, you can specify your environment variables by writing additional override items
without modifying the default `config/env.ini` file.

```ini
; This is an example of `config/env.custom.ini` file,
; we modify the `SPC_CONCURRENCY` and linux default CFLAGS passing to libs and PHP
[global]
SPC_CONCURRENCY=4

[linux]
SPC_DEFAULT_C_FLAGS="-O3"
```

## Library environment variables (Unix only)

Starting from 2.2.0, static-php-cli supports custom environment variables for all compilation dependent library commands of macOS, Linux, FreeBSD and other Unix systems.
Expand Down
8 changes: 2 additions & 6 deletions docs/en/guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@ currently supporting Linux and macOS systems.

In the guide section, you will learn how to use static php cli to build standalone PHP programs.

- [GitHub Action Build](./action-build)
- [Manual Build](./manual-build)
- [Build (local)](./manual-build)
- [Build (GitHub Actions)](./action-build)
- [Supported Extensions](./extensions)

::: tip
If you are a native English speaker, some corrections to the documentation are welcome.
:::

## Compilation Environment

The following is the architecture support situation, where :gear: represents support for GitHub Action build,
Expand Down
2 changes: 2 additions & 0 deletions docs/en/guide/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@ and then determine the command that reported the error.
The error terminal output is very important for fixing compilation errors.
When submitting an issue, please upload the last error fragment of the terminal log (or the entire terminal log output),
and include the `spc` command and parameters used.

If you are rebuilding, please refer to the [Local Build - Multiple Builds](./manual-build#multiple-builds) section.
76 changes: 76 additions & 0 deletions docs/zh/guide/build-with-glibc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# 构建 glibc 兼容的 Linux 二进制

## 为什么要构建 glibc 兼容的二进制

目前,static-php-cli 在默认条件下在 Linux 系统构建的二进制都是基于 musl-libc(静态链接)的。
musl-libc 是一个轻量级的 libc 实现,它的目标是与 glibc 兼容,并且提供良好的纯静态链接支持。
这意味着,编译出来的静态 PHP 可执行文件在几乎任何 Linux 发行版都可以使用,而不需要考虑 libc、libstdc++ 等库的版本问题。

但是,Linux 系统的纯静态链接 musl-libc 二进制文件存在以下问题:

- 无法使用 PHP 的 `dl()` 函数加载动态链接库和外部 PHP 扩展。
- 无法使用 PHP 的 FFI 扩展。
- 部分极端情况下,可能会出现性能问题,参见 [musl-libc 的性能问题](https://github.com/php/php-src/issues/13648)。

对于不同的 Linux 发行版,它们使用的默认 libc 可能不同,比如 Alpine Linux 使用 musl libc,而大多数 Linux 发行版使用 glibc。
但即便如此,我们也不能直接使用任意的发行版和 glibc 构建便携的静态二进制文件,因为 glibc 有一些问题:

- 基于新版本的发行版在使用 gcc 等工具构建的二进制,无法在旧版本的发行版上运行。
- glibc 不推荐被静态链接,因为它的一些特性需要动态链接库的支持。

但是,我们可以使用 Docker 容器来解决这个问题,最终输出的结果是一个动态链接 glibc 和一些必要库的二进制,但它静态链接所有其他依赖。

1. 使用一个旧版本的 Linux 发行版(如 CentOS 7.x),它的 glibc 版本比较旧,但是可以在大多数现代 Linux 发行版上运行。
2. 在这个容器中构建 PHP 的静态二进制文件,这样就可以在大多数现代 Linux 发行版上运行了。

> 使用 glibc 的静态二进制文件,可以在大多数现代 Linux 发行版上运行,但是不能在 musl libc 的发行版上运行,如 CentOS 6、Alpine Linux 等。

## 构建 glibc 兼容的 Linux 二进制

最新版的 static-php-cli 内置了 `bin/spc-gnu-docker` 脚本,可以一键创建一个 CentOS 7.x (glibc-2.17) 的 Docker 容器,并在容器中构建 glibc 兼容的 PHP 静态二进制文件。

请先克隆本项目的仓库,并将下面的内容添加到 `config/env.custom.ini` 文件中:

```ini
; Modify this file name to `env.custom.ini`, and run `bin/spc-gnu-docker`,
; you can compile a GNU libc based static binary !
[global]
SPC_SKIP_DOCTOR_CHECK_ITEMS="if musl-wrapper is installed,if musl-cross-make is installed"

[linux]
CC=gcc
CXX=g++
AR=ar
LD=ld
SPC_DEFAULT_C_FLAGS=-fPIC
SPC_NO_MUSL_PATH=yes
SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-Wl,-O1 -pie"
SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil"
```

然后,先运行一次以下命令。首次运行时时间较长,因为需要下载 CentOS 7.x 的镜像和一些编译工具。

```bash
bin/spc-gnu-docker
```

构建镜像完成后,你将看到和 `bin/spc` 一样的命令帮助菜单,这时说明容器已经准备好了。

在容器准备好后,你可以参考 [本地构建](./manual-build) 章节的内容,构建你的 PHP 静态二进制文件。仅需要把 `bin/spc` 或 `./spc` 替换为 `bin/spc-gnu-docker` 即可。

与默认构建不同的是,在 glibc 环境构建时**必须添加**参数 `--libc=glibc`,如:

```bash
bin/spc-gnu-docker --libc=glibc build bcmath,ctype,openssl,pdo,phar,posix,session,tokenizer,xml,zip --build-cli --debug
```

## 注意事项

极少数情况下,基于 glibc 的静态 PHP 可能会出现 segment fault 等错误,但目前例子较少,如果遇到问题请提交 issue。

glibc 构建为扩展的特性,不属于默认 static-php 的支持范围。如果有相关问题或需求,请在提交 Issue 时注明你是基于 glibc 构建的。

如果你需要不使用 Docker 构建基于 glibc 的二进制,请参考 `bin/spc-gnu-docker` 脚本,手动构建一个类似的环境。

由于 glibc 二进制不是项目的主要目标,一般情况下我们不会额外测试 glibc 下的各个库和扩展的兼容性。
任何特定库如果在 musl-libc 上构建成功,但在 glibc 上构建失败,请提交 issue,我们将会单独解决。
13 changes: 13 additions & 0 deletions docs/zh/guide/env-vars.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ SPC_CONCURRENCY=4 bin/spc build mbstring,pcntl --build-cli

例如,你需要修改编译 PHP 的 `./configure` 命令,你可以在 `config/env.ini` 文件中找到 `SPC_CMD_PREFIX_PHP_CONFIGURE` 环境变量,然后修改其值即可。

但如果你的构建条件比较复杂,需要多种 env.ini 进行切换,我们推荐你使用 `config/env.custom.ini` 文件,这样你可以在不修改默认的 `config/env.ini` 文件的情况下,
通过写入额外的重载项目指定你的环境变量。

```ini
; This is an example of `config/env.custom.ini` file,
; we modify the `SPC_CONCURRENCY` and linux default CFLAGS passing to libs and PHP
[global]
SPC_CONCURRENCY=4

[linux]
SPC_DEFAULT_C_FLAGS="-O3"
```

## 编译依赖库的环境变量(仅限 Unix 系统)

从 2.2.0 开始,static-php-cli 对所有 macOS、Linux、FreeBSD 等 Unix 系统的编译依赖库的命令均支持自定义环境变量。
Expand Down
2 changes: 2 additions & 0 deletions docs/zh/guide/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@

遇到编译错误时,如果没有开启 `--debug` 日志,请先开启调试日志,然后确定报错的命令。
报错的终端输出对于修复编译错误非常重要,请在提交 Issue 时一并将终端日志的最后报错片段(或整个终端日志输出)上传,并且包含使用的 `spc` 命令和参数。

如果你是重复构建,请参考 [本地构建 - 多次构建](./manual-build#多次构建) 章节,清理构建缓存后再次构建。
Loading