Skip to content

Commit e16bfbf

Browse files
author
Tinywan
committed
feat: make builder fully independent and self-contained from standard base images
1 parent fc3e89a commit e16bfbf

8 files changed

Lines changed: 270 additions & 35 deletions

File tree

‎.gitattributes‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
* text=auto
2+
*.sh text eol=lf
3+
Dockerfile* text eol=lf

‎.github/workflows/docker-publish.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ name: Build and Publish Docker Images
22

33
on:
44
push:
5+
branches:
6+
- main
57
tags:
68
- 'v*'
79
workflow_dispatch:

‎alpine/Dockerfile‎

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,54 @@
1-
ARG BASE_IMAGE=tinywan/typephp-linux-x64-static:v0.9.4
2-
FROM ${BASE_IMAGE}
1+
# ============================================================
2+
# TypePHP AOT Builder (Alpine Musl Full-Static)
3+
# Linux x86_64 Alpine musl full-static single binary builder
4+
# ============================================================
5+
6+
FROM alpine:edge
7+
8+
ARG TYPEPHP_VERSION=v0.9.4
39

410
LABEL maintainer="Tinywan <gwmpgc@gmail.com>"
511
LABEL org.opencontainers.image.title="TypePHP AOT Builder (Alpine Musl Full-Static)"
612
LABEL org.opencontainers.image.description="Linux x86_64 Alpine musl full-static single binary builder for Webman & TypePHP projects"
713
LABEL org.opencontainers.image.source="https://github.com/Tinywan/docker-typephp-builder"
814

15+
# 1. 完整工具链: Clang + musl + SDK 构建依赖 + 编译依赖
16+
RUN apk add --no-cache \
17+
bash clang lld build-base g++ cmake binutils \
18+
musl-dev linux-headers pcre2-dev \
19+
gmp-dev gmp-static mpfr-dev zlib-static oniguruma-dev \
20+
curl xz bison re2c libxml2-dev \
21+
git unzip file \
22+
php85 php85-dev php85-embed \
23+
php85-phar php85-openssl php85-tokenizer \
24+
php85-ctype php85-mbstring php85-dom php85-xml \
25+
php85-simplexml php85-pcntl php85-posix php85-curl php85-session \
26+
&& ln -sf /usr/bin/php85 /usr/bin/php
27+
28+
# 2. 安装 Composer
29+
RUN curl -sS https://getcomposer.org/installer | php -- \
30+
--install-dir=/usr/local/bin --filename=composer
31+
32+
ENV COMPOSER_NO_INTERACTION=1 \
33+
COMPOSER_PROCESS_TIMEOUT=0
34+
35+
# 3. 内置 TypePHP 源码与全局命令 tpc
36+
WORKDIR /opt/typephp
37+
RUN composer require swoole/typephp:${TYPEPHP_VERSION} --prefer-dist --no-progress \
38+
&& ln -sf /opt/typephp/vendor/bin/tpc.php /usr/local/bin/tpc \
39+
&& chmod +x /usr/local/bin/tpc
40+
41+
# 4. 组装并内置 Full-Static SDK
42+
COPY build-sdk-internal.sh /tmp/build-sdk-internal.sh
43+
RUN chmod +x /tmp/build-sdk-internal.sh && /tmp/build-sdk-internal.sh && rm -f /tmp/build-sdk-internal.sh
44+
45+
# 5. 配置 Entrypoint 脚本
946
COPY entrypoint.sh /entrypoint.sh
1047
RUN chmod 0755 /entrypoint.sh
1148

49+
ENV PATH="/opt/typephp/vendor/bin:${PATH}" \
50+
TYPEPHP_HOME="/opt/typephp" \
51+
PHPX_HOME="/opt/typephp/vendor/swoole/phpx"
52+
1253
WORKDIR /workspace
1354
ENTRYPOINT ["/entrypoint.sh"]

‎alpine/build-sdk-internal.sh‎

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#!/bin/sh
2+
set -ex
3+
4+
SDK_DIR="/opt/typephp/vendor/swoole/phpx/full-static/sdk"
5+
mkdir -p "$SDK_DIR/lib/musl" "$SDK_DIR/include/php" "$SDK_DIR/include/phpx" "$SDK_DIR/include"
6+
7+
# 1. 拷贝 musl 启动目标文件
8+
cp -f /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtn.o "$SDK_DIR/lib/musl/"
9+
10+
# 2. 编译 static embed libphp.a
11+
PHP_BUILD_DIR="/tmp/php-src"
12+
mkdir -p "$PHP_BUILD_DIR"
13+
cd "$PHP_BUILD_DIR"
14+
echo "[SDK] Downloading PHP 8.5.0 source for static embed build..."
15+
curl -sSL "https://www.php.net/distributions/php-8.5.0.tar.xz" | tar -xJ --strip-components=1
16+
17+
./configure \
18+
--prefix=/usr \
19+
--enable-embed=static \
20+
--disable-all \
21+
--enable-cli \
22+
--enable-session \
23+
--enable-filter \
24+
--enable-json \
25+
--enable-ctype \
26+
--enable-tokenizer \
27+
--enable-posix \
28+
--enable-pcntl \
29+
--enable-sockets \
30+
--with-zlib \
31+
--enable-mbstring \
32+
--enable-fileinfo \
33+
--enable-phar \
34+
--enable-bcmath \
35+
--without-pear
36+
37+
make -j$(nproc) libphp.la
38+
39+
LIBPHP_STATIC=$(find "$PHP_BUILD_DIR" -name "libphp.a" | head -n 1)
40+
make install-headers INSTALL_ROOT=/tmp/php-install
41+
cp -r /tmp/php-install/usr/include/php/. "$SDK_DIR/include/php/"
42+
43+
# 3. 生成 gmp 桩代码
44+
cat << 'EOF' > /tmp/gmp_compat_stubs.c
45+
#include <stddef.h>
46+
const char * const __gmp_version = "6.3.0";
47+
const char * const mpfr_version = "4.2.1";
48+
int __gmpz_init(void *x) { return 0; }
49+
int __gmpz_init_set(void *x, const void *y) { return 0; }
50+
int __gmpz_init_set_ui(void *x, unsigned long y) { return 0; }
51+
int __gmpz_init_set_si(void *x, long y) { return 0; }
52+
int __gmpz_init_set_d(void *x, double y) { return 0; }
53+
int __gmpz_init_set_str(void *x, const char *s, int base) { return 0; }
54+
void __gmpz_clear(void *x) {}
55+
void __gmpz_clears(void *x, ...) {}
56+
void __gmpz_inits(void *x, ...) {}
57+
int __gmpz_set(void *x, const void *y) { return 0; }
58+
int __gmpz_set_ui(void *x, unsigned long y) { return 0; }
59+
int __gmpz_set_si(void *x, long y) { return 0; }
60+
int __gmpz_set_d(void *x, double y) { return 0; }
61+
int __gmpz_set_str(void *rop, const char *str, int base) { return 0; }
62+
int __gmpz_cmp(const void *x, const void *y) { return 0; }
63+
int __gmpz_cmp_si(const void *x, long y) { return 0; }
64+
int __gmpz_cmp_ui(const void *x, unsigned long y) { return 0; }
65+
int __gmpz_cmp_d(const void *x, double y) { return 0; }
66+
void __gmpz_add(void *r, const void *a, const void *b) {}
67+
void __gmpz_sub(void *r, const void *a, const void *b) {}
68+
void __gmpz_mul(void *r, const void *a, const void *b) {}
69+
void __gmpz_mul_si(void *r, const void *a, long b) {}
70+
void __gmpz_mul_ui(void *r, const void *a, unsigned long b) {}
71+
void __gmpz_mul_2exp(void *r, const void *a, unsigned long b) {}
72+
void __gmpz_tdiv_q(void *q, const void *a, const void *b) {}
73+
void __gmpz_tdiv_r(void *r, const void *a, const void *b) {}
74+
void __gmpz_tdiv_qr(void *q, void *r, const void *a, const void *b) {}
75+
void __gmpz_mod(void *r, const void *a, const void *b) {}
76+
void __gmpz_neg(void *r, const void *a) {}
77+
void __gmpz_abs(void *r, const void *a) {}
78+
int __gmpz_sizeinbase(const void *a, int base) { return 0; }
79+
char *__gmpz_get_str(char *s, int base, const void *a) { return (char*)0; }
80+
unsigned long __gmpz_get_ui(const void *a) { return 0; }
81+
long __gmpz_get_si(const void *a) { return 0; }
82+
double __gmpz_get_d(const void *a) { return 0.0; }
83+
void *__gmp_default_allocate(size_t s) { return (void*)0; }
84+
void *__gmp_default_reallocate(void *p, size_t os, size_t ns) { return (void*)0; }
85+
void __gmp_default_free(void *p, size_t s) {}
86+
void (*__gmp_allocate_func)(size_t) = (void*)0;
87+
void *(*__gmp_reallocate_func)(void *, size_t, size_t) = (void*)0;
88+
void (*__gmp_free_func)(void *, size_t) = (void*)0;
89+
EOF
90+
gcc -fPIC -c /tmp/gmp_compat_stubs.c -o /tmp/gmp_compat_stubs.o
91+
92+
# 4. 合并 fat archive
93+
MERGE_DIR="/tmp/merge_objects"
94+
mkdir -p "$MERGE_DIR"
95+
for lib in "$LIBPHP_STATIC" /usr/lib/libc.a /usr/lib/libstdc++.a /usr/lib/libz.a /usr/lib/libonig.a; do
96+
if [ -f "$lib" ]; then
97+
libname="$(basename "$lib" .a)"
98+
subdir="$MERGE_DIR/$libname"
99+
mkdir -p "$subdir"
100+
(
101+
cd "$subdir"
102+
ar x "$lib"
103+
for f in *.o; do
104+
[ -f "$f" ] && mv "$f" "${libname}_${f}"
105+
done
106+
) || true
107+
fi
108+
done
109+
cp /tmp/gmp_compat_stubs.o "$MERGE_DIR/"
110+
find "$MERGE_DIR" -name "*.o" | sort | xargs ar rcs "$SDK_DIR/lib/libphp.a"
111+
ranlib "$SDK_DIR/lib/libphp.a"
112+
113+
# 5. 编译 static libphpx.a
114+
cmake -S /opt/typephp/vendor/swoole/phpx/full-static -B /tmp/phpx-static-build \
115+
-DCMAKE_BUILD_TYPE=Release \
116+
-DPHPX_PHP_INCLUDE_DIR="$SDK_DIR/include/php" \
117+
-DPHPX_GMP_INCLUDE_DIR=/usr/include \
118+
-DPHPX_GMP_LIB_DIR=/usr/lib \
119+
-DPHPX_MPFR_INCLUDE_DIR=/usr/include \
120+
-DPHPX_MPFR_LIB_DIR=/usr/lib
121+
122+
cmake --build /tmp/phpx-static-build --parallel $(nproc)
123+
cp -f /tmp/phpx-static-build/libphpx.a "$SDK_DIR/lib/libphpx.a"
124+
125+
# 6. 头文件同步
126+
cp -r /opt/typephp/vendor/swoole/phpx/include/. "$SDK_DIR/include/phpx/"
127+
cp -f /opt/typephp/vendor/swoole/phpx/thirdparty/mpdecimal/libmpdec++/decimal.hh "$SDK_DIR/include/phpx/" 2>/dev/null || true
128+
cp -f /opt/typephp/vendor/swoole/phpx/thirdparty/mpdecimal/libmpdec/mpdecimal.h "$SDK_DIR/include/phpx/" 2>/dev/null || true
129+
cp -f /usr/include/gmp.h /usr/include/gmpxx.h /usr/include/mpfr.h "$SDK_DIR/include/" 2>/dev/null || true
130+
cp -f /usr/include/gmp.h /usr/include/gmpxx.h /usr/include/mpfr.h "$SDK_DIR/include/phpx/" 2>/dev/null || true
131+
cp -f /usr/include/pcre2*.h "$SDK_DIR/include/" 2>/dev/null || true
132+
cp -f /usr/include/pcre2*.h "$SDK_DIR/include/php/" 2>/dev/null || true
133+
134+
# 清理临时编译缓存
135+
rm -rf /tmp/* "$PHP_BUILD_DIR"
136+
echo "[SDK] Static SDK built and staged successfully at $SDK_DIR"

‎alpine/entrypoint.sh‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ if [[ -e "$final_dir" ]]; then
208208
echo "[ERROR] Output '$output_dir' already exists; use --force to overwrite it." >&2
209209
exit 1
210210
fi
211-
mv "$final_dir" "$real_workspace/.typephp/previous-$build_id"
211+
rm -rf "$final_dir" 2>/dev/null || mv "$final_dir" "$real_workspace/.typephp/previous-$build_id" 2>/dev/null || true
212212
fi
213213

214214
cp -a "$stage_dir" "$final_dir"

‎docker-compose.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ services:
77
context: ./alpine
88
dockerfile: Dockerfile
99
args:
10-
BASE_IMAGE: tinywan/typephp-linux-x64-static:v0.9.4
10+
TYPEPHP_VERSION: v0.9.4
1111
image: tinywan/typephp-builder:alpine
1212
container_name: typephp-builder-alpine
1313
platform: linux/amd64
@@ -25,7 +25,7 @@ services:
2525
context: ./glibc
2626
dockerfile: Dockerfile
2727
args:
28-
BASE_IMAGE: tinywan/typephp-linux-x64:v0.9.4
28+
TYPEPHP_VERSION: v0.9.4
2929
image: tinywan/typephp-builder:glibc
3030
container_name: typephp-builder-glibc
3131
platform: linux/amd64

‎glibc/Dockerfile‎

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,81 @@
1-
ARG BASE_IMAGE=tinywan/typephp-linux-x64:v0.9.4
2-
FROM ${BASE_IMAGE}
1+
# ============================================================
2+
# TypePHP AOT Builder (Linux Glibc Portable)
3+
# Linux x86_64 glibc portable-dir builder for Webman & TypePHP projects
4+
# ============================================================
5+
6+
FROM ubuntu:22.04
7+
8+
ARG TYPEPHP_VERSION=v0.9.4
39

410
LABEL maintainer="Tinywan <gwmpgc@gmail.com>"
511
LABEL org.opencontainers.image.title="TypePHP AOT Builder (Linux Glibc Portable)"
612
LABEL org.opencontainers.image.description="Linux x86_64 glibc portable-dir builder for Webman & TypePHP projects"
713
LABEL org.opencontainers.image.source="https://github.com/Tinywan/docker-typephp-builder"
814

15+
ENV DEBIAN_FRONTEND=noninteractive \
16+
COMPOSER_NO_INTERACTION=1 \
17+
COMPOSER_PROCESS_TIMEOUT=0 \
18+
PHP_HOME=/usr
19+
20+
# 1. 系统构建基础依赖 + GPG 工具链
21+
RUN apt-get update && apt-get install -y --no-install-recommends \
22+
build-essential cmake pkg-config python3-dev patchelf \
23+
libgmp-dev libmpfr-dev libevent-dev \
24+
ca-certificates curl git unzip gnupg gpg-agent file \
25+
&& rm -rf /var/lib/apt/lists/*
26+
27+
# 2. 配置官方 Ondrej PHP 源
28+
RUN curl -fsSL 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x4f4ea0aae5267a6c' | gpg --dearmor -o /etc/apt/trusted.gpg.d/ondrej-php.gpg \
29+
&& echo 'deb [signed-by=/etc/apt/trusted.gpg.d/ondrej-php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main' > /etc/apt/sources.list.d/ondrej-php.list
30+
31+
# 3. 安装 PHP 8.4 及全部所需扩展与 embed/dev 开发库
32+
RUN apt-get update && apt-get install -y --no-install-recommends \
33+
php8.4-cli php8.4-dev php8.4-embed \
34+
php8.4-curl php8.4-mbstring php8.4-zip php8.4-mysql \
35+
php8.4-xml php8.4-gd php8.4-redis php8.4-igbinary \
36+
php8.4-msgpack php8.4-posix \
37+
&& rm -rf /var/lib/apt/lists/*
38+
39+
# 4. 安装 Composer
40+
RUN curl -sS https://getcomposer.org/installer | php -- \
41+
--install-dir=/usr/local/bin --filename=composer
42+
43+
# 5. Patch php_hash.h for C++ compatibility
44+
RUN PHP_INC="$(php-config --include-dir 2>/dev/null || echo '')" \
45+
&& if [ -n "$PHP_INC" ]; then \
46+
find "$PHP_INC" /usr/include -name "php_hash.h" \
47+
-exec sed -i \
48+
's/static inline const php_hash_ops\* php_hash_fetch_ops/static inline const struct _php_hash_ops\* php_hash_fetch_ops/g' \
49+
{} + 2>/dev/null || true; \
50+
fi
51+
52+
# 6. 内置 TypePHP 源码与全局命令 tpc
53+
WORKDIR /opt/typephp
54+
RUN composer require swoole/typephp:${TYPEPHP_VERSION} --prefer-dist --no-progress \
55+
&& ln -sf /opt/typephp/vendor/bin/tpc.php /usr/local/bin/tpc \
56+
&& chmod +x /usr/local/bin/tpc
57+
58+
# 7. 预编译 PHPX 动态运行时库 (libphpx.so),并注册到系统动态链接器
59+
RUN PHPX_DIR="/opt/typephp/vendor/swoole/phpx" \
60+
&& cd "$PHPX_DIR" \
61+
&& sed -i 's/include_directories(include tests\/include src\/misc)/include_directories(include tests\/include src\/misc \/usr\/include \/usr\/local\/include \/usr\/include\/x86_64-linux-gnu)/g' CMakeLists.txt \
62+
&& cmake . -Dphp_dir="/usr" -DBUILD_TESTS=OFF -DBUILD_EXT=OFF \
63+
&& make phpx -j$(nproc) \
64+
&& mkdir -p "$PHPX_DIR/lib" \
65+
&& cp -f "$PHPX_DIR/libphpx.so" "$PHPX_DIR/lib/" 2>/dev/null || true \
66+
&& echo "$PHPX_DIR/lib" > /etc/ld.so.conf.d/typephp.conf \
67+
&& echo "$PHPX_DIR" >> /etc/ld.so.conf.d/typephp.conf \
68+
&& ldconfig \
69+
&& echo "[OK] Precompiled libphpx.so created and registered to system ldconfig successfully"
70+
71+
# 8. 配置 Entrypoint 脚本
972
COPY entrypoint.sh /entrypoint.sh
1073
RUN chmod 0755 /entrypoint.sh
1174

75+
ENV PATH="/opt/typephp/vendor/bin:${PATH}" \
76+
TYPEPHP_HOME="/opt/typephp" \
77+
PHPX_HOME="/opt/typephp/vendor/swoole/phpx" \
78+
LD_LIBRARY_PATH="/opt/typephp/vendor/swoole/phpx/lib:/opt/typephp/vendor/swoole/phpx:/usr/lib"
79+
1280
WORKDIR /workspace
1381
ENTRYPOINT ["/entrypoint.sh"]

‎glibc/entrypoint.sh‎

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ done
201201

202202
# Ensure core runtime libraries libphpx and libphp exist
203203
for required_library in 'libphpx.so*' 'libphp.so*'; do
204-
found_lib="$(find /opt /usr "$stage_dir/lib" -name "$required_library" -type f 2>/dev/null | head -n 1 || true)"
204+
found_lib="$(find /opt /usr -name "$required_library" -type f 2>/dev/null | head -n 1 || true)"
205205
if [[ -n "$found_lib" && -f "$found_lib" ]]; then
206206
libname="$(basename "$found_lib")"
207207
clean_libname="${libname%%.*}.so"
@@ -238,35 +238,20 @@ error_reporting=E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED
238238
display_errors=stderr
239239
display_startup_errors=Off
240240
241-
; 核心进程管理与网络扩展
242-
extension=ctype.so
243-
extension=tokenizer.so
244-
extension=phar.so
245-
extension=iconv.so
246-
extension=posix.so
247-
extension=mbstring.so
248-
extension=mysqlnd.so
249-
extension=pdo.so
250-
extension=pdo_mysql.so
251-
extension=mysqli.so
252-
extension=curl.so
253-
extension=fileinfo.so
254-
extension=zip.so
255-
extension=igbinary.so
256-
extension=msgpack.so
257-
extension=redis.so
258-
extension=sockets.so
259-
extension=event.so
260241
EOF
261242

262-
while IFS= read -r line || [[ -n "$line" ]]; do
263-
if echo "$line" | grep -q "^extension="; then
264-
ext_file=$(echo "$line" | cut -d'=' -f2 | tr -d '[:space:]')
265-
if [[ ! -f "$stage_dir/ext/$ext_file" ]]; then
266-
sed -i "s/^extension=$ext_file$/; extension=$ext_file ; (not found)/" "$stage_dir/php.ini"
243+
# 遵循官方 conf.d 依赖顺序自动追加当前打包扩展
244+
for inifile in $(find /etc/php -path "*/cli/conf.d/*.ini" 2>/dev/null | sort -V); do
245+
grep -E '^\s*(extension|zend_extension)\s*=' "$inifile" 2>/dev/null | while read -r line; do
246+
ext_target=$(echo "$line" | cut -d'=' -f2 | tr -d '[:space:]')
247+
ext_filename=$(basename "$ext_target")
248+
if [[ "$ext_filename" != "opcache.so" ]]; then
249+
if [[ -f "$stage_dir/ext/$ext_filename" ]] && ! grep -q "^extension=$ext_filename$" "$stage_dir/php.ini"; then
250+
echo "extension=$ext_filename" >> "$stage_dir/php.ini"
251+
fi
267252
fi
268-
fi
269-
done < "$stage_dir/php.ini"
253+
done
254+
done
270255

271256
# 5. Generate launcher scripts
272257
install -m 0755 /dev/stdin "$stage_dir/$output_name" <<SCRIPT
@@ -326,7 +311,7 @@ if [[ -e "$final_dir" ]]; then
326311
echo "[ERROR] Output '$output_dir' already exists; use --force to overwrite it." >&2
327312
exit 1
328313
fi
329-
mv "$final_dir" "$real_workspace/.typephp/previous-$build_id"
314+
rm -rf "$final_dir" 2>/dev/null || mv "$final_dir" "$real_workspace/.typephp/previous-$build_id" 2>/dev/null || true
330315
fi
331316

332317
cp -a "$stage_dir" "$final_dir"

0 commit comments

Comments
 (0)