diff --git a/build-static-glibc.sh b/build-static-glibc.sh new file mode 100644 index 000000000..8929505b6 --- /dev/null +++ b/build-static-glibc.sh @@ -0,0 +1,235 @@ +#!/bin/bash + +set -o errexit +set -x + +if ! type "git" >/dev/null 2>&1; then + echo "The \"git\" command must be installed." + exit 1 +fi + +arch="$(uname -m)" +os="$(uname -s | tr '[:upper:]' '[:lower:]')" +spcCommand="./bin/spc" +md5binary="md5sum" + +if ! type "cmake" >/dev/null 2>&1; then + echo "The \"cmake\" command must be installed." + exit 1 +fi + +fpic="-fPIC" +fpie="-fPIE" + +if [[ "${arch}" =~ "aarch" ]] || [[ "${arch}" =~ "arm" ]]; then + if [ -z "${DEBUG_SYMBOLS}" ]; then + export SPC_PHP_DEFAULT_OPTIMIZE_CFLAGS="-g -fstack-protector-strong -fPIC -fPIE -Os -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64" + fi +else + fpic="-fpic" + fpie="-fpie" +fi + + +if [ -z "${PHP_EXTENSIONS}" ]; then + if [ -n "${EMBED}" ] && [ -f "${EMBED}/composer.json" ]; then + cd "${EMBED}" + # read the composer.json file and extract the required PHP extensions + # remove internal extensions from the list: https://github.com/crazywhalecc/static-php-cli/blob/4b16631d45a57370b4747df15c8f105130e96d03/src/globals/defines.php#L26-L34 + PHP_EXTENSIONS="$(composer check-platform-reqs --no-dev 2>/dev/null | grep ^ext | sed -e 's/^ext-core//' -e 's/^ext-hash//' -e 's/^ext-json//' -e 's/^ext-pcre//' -e 's/^ext-reflection//' -e 's/^ext-spl//' -e 's/^ext-standard//' -e 's/^ext-//' -e 's/ .*//' | xargs | tr ' ' ',')" + export PHP_EXTENSIONS + cd - + else + export PHP_EXTENSIONS="apcu,bcmath,bz2,calendar,ctype,curl,dba,dom,exif,ffi,fileinfo,filter,ftp,gd,gmp,gettext,iconv,igbinary,imagick,intl,ldap,mbregex,mbstring,mysqli,mysqlnd,opcache,openssl,parallel,pcntl,pdo,pdo_mysql,pdo_pgsql,pdo_sqlite,pgsql,phar,posix,protobuf,readline,redis,session,shmop,simplexml,soap,sockets,sodium,sqlite3,ssh2,sysvmsg,sysvsem,sysvshm,tidy,tokenizer,xlswriter,xml,xmlreader,xmlwriter,zip,zlib,yaml,zstd" + fi +fi + +if [ -z "${PHP_EXTENSION_LIBS}" ]; then + export PHP_EXTENSION_LIBS="bzip2,libavif,libjpeg,liblz4,libwebp,libzip,nghttp2" +fi + +# The Brotli library must always be built as it is required by http://github.com/dunglas/caddy-cbrotli +if ! echo "${PHP_EXTENSION_LIBS}" | grep -q "\bbrotli\b"; then + export PHP_EXTENSION_LIBS="${PHP_EXTENSION_LIBS},brotli" +fi + +if [ -z "${PHP_VERSION}" ]; then + export PHP_VERSION="8.4" +fi + +if [ -z "${FRANKENPHP_VERSION}" ]; then + FRANKENPHP_VERSION="$(git rev-parse --verify HEAD)" + export FRANKENPHP_VERSION +elif [ -d ".git/" ]; then + CURRENT_REF="$(git rev-parse --abbrev-ref HEAD)" + export CURRENT_REF + + if echo "${FRANKENPHP_VERSION}" | grep -F -q "."; then + # Tag + + # Trim "v" prefix if any + FRANKENPHP_VERSION=${FRANKENPHP_VERSION#v} + export FRANKENPHP_VERSION + + git checkout "v${FRANKENPHP_VERSION}" + else + git checkout "${FRANKENPHP_VERSION}" + fi +fi + +bin="frankenphp-${os}-${arch}" + +if [ -n "${CLEAN}" ]; then + rm -Rf dist/ + go clean -cache +fi + +cache_key="${PHP_VERSION}-${PHP_EXTENSIONS}-${PHP_EXTENSION_LIBS}" + +# Build libphp if necessary +if [ -f dist/cache_key ] && [ "$(cat dist/cache_key)" = "${cache_key}" ] && [ -f "dist/static-php-cli/buildroot/lib/libphp.a" ]; then + cd dist/static-php-cli +else + mkdir -p dist/ + cd dist/ + echo -n "${cache_key}" >cache_key + + if [ -d "static-php-cli/" ]; then + cd static-php-cli/ + git pull + else + git clone --depth 1 https://github.com/crazywhalecc/static-php-cli + cd static-php-cli/ + fi + + composer install --no-dev -a + + if [ -n "${DEBUG_SYMBOLS}" ]; then + extraOpts="${extraOpts} --no-strip" + fi + + # ${spcCommand} doctor --auto-fix + ${spcCommand} download --with-php="${PHP_VERSION}" --for-extensions="${PHP_EXTENSIONS}" --for-libs="${PHP_EXTENSION_LIBS}" --ignore-cache-sources=php-src + # shellcheck disable=SC2086 + ${spcCommand} build --debug --enable-zts --build-embed ${extraOpts} "${PHP_EXTENSIONS}" --with-libs="${PHP_EXTENSION_LIBS}" +fi + +if ! type "go" >/dev/null 2>&1; then + echo "The \"go\" command must be installed." + exit 1 +fi + +XCADDY_COMMAND="xcaddy" +if ! type "$XCADDY_COMMAND" >/dev/null 2>&1; then + go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest + XCADDY_COMMAND="$(go env GOPATH)/bin/xcaddy" +fi + +curlGitHubHeaders=(--header "X-GitHub-Api-Version: 2022-11-28") +if [ "${GITHUB_TOKEN}" ]; then + curlGitHubHeaders+=(--header "Authorization: Bearer ${GITHUB_TOKEN}") +fi + +# Compile e-dant/watcher as a static library +mkdir -p watcher +cd watcher +curl -f --retry 5 "${curlGitHubHeaders[@]}" https://api.github.com/repos/e-dant/watcher/releases/latest | + grep tarball_url | + awk '{ print $2 }' | + sed 's/,$//' | + sed 's/"//g' | + xargs curl -fL --retry 5 "${curlGitHubHeaders[@]}" | + tar xz --strip-components 1 +cd watcher-c +$CC -c -o libwatcher-c.o ./src/watcher-c.cpp -I ./include -I ../include -std=c++17 -Wall -Wextra "${fpic}" +ar rcs libwatcher-c.a libwatcher-c.o +cp libwatcher-c.a ../../buildroot/lib/libwatcher-c.a +mkdir -p ../../buildroot/include/wtr +cp -R include/wtr/watcher-c.h ../../buildroot/include/wtr/watcher-c.h +cd ../../ + +# See https://github.com/docker-library/php/blob/master/8.3/alpine3.20/zts/Dockerfile#L53-L55 +CGO_CFLAGS="-DFRANKENPHP_VERSION=${FRANKENPHP_VERSION} -I${PWD}/buildroot/include/ $(${spcCommand} spc-config "${PHP_EXTENSIONS}" --with-libs="${PHP_EXTENSION_LIBS}" --includes)" +if [ -n "${DEBUG_SYMBOLS}" ]; then + CGO_CFLAGS="-g ${CGO_CFLAGS}" +else + CGO_CFLAGS="-fstack-protector-strong ${fpic} ${fpie} -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 ${CGO_CFLAGS}" +fi +export CGO_CFLAGS +export CGO_CPPFLAGS="${CGO_CFLAGS}" + +if [ "${os}" = "mac" ]; then + export CGO_LDFLAGS="-framework CoreFoundation -framework SystemConfiguration" +elif [ "${os}" = "linux" ] && [ -z "${DEBUG_SYMBOLS}" ]; then + CGO_LDFLAGS="-Wl,-O1 -pie -Wl,--allow-multiple-definition -Wl,--export-dynamic" +fi + +CGO_LDFLAGS="${CGO_LDFLAGS} ${PWD}/buildroot/lib/libbrotlicommon.a ${PWD}/buildroot/lib/libbrotlienc.a ${PWD}/buildroot/lib/libbrotlidec.a ${PWD}/buildroot/lib/libwatcher-c.a $(${spcCommand} spc-config "${PHP_EXTENSIONS}" --with-libs="${PHP_EXTENSION_LIBS}" --libs)" +if [ "${os}" = "linux" ]; then + if echo "${PHP_EXTENSIONS}" | grep -qE "\b(intl|imagick|grpc|v8js|protobuf|mongodb|tbb)\b"; then + CGO_LDFLAGS="${CGO_LDFLAGS} -lstdc++" + fi + CGO_LDFLAGS=$(echo "$CGO_LDFLAGS" | sed 's|-lphp|-Wl,--whole-archive -lphp -Wl,--no-whole-archive|g') + ar d ${PWD}/buildroot/lib/libphp.a $(ar t ${PWD}/buildroot/lib/libphp.a | grep '\.a$') +fi + +export CGO_LDFLAGS + +LIBPHP_VERSION="$(./buildroot/bin/php-config --version)" + +cd ../ + +if [ -z "${DEBUG_SYMBOLS}" ]; then + extraLdflags="-w -s" +fi + +cd ../ + +# Embed PHP app, if any +if [ -n "${EMBED}" ] && [ -d "${EMBED}" ]; then + tar -cf app.tar -C "${EMBED}" . + ${md5binary} app.tar | awk '{printf $1}' >app_checksum.txt +fi + +if [ -z "${XCADDY_ARGS}" ]; then + XCADDY_ARGS="--with github.com/dunglas/caddy-cbrotli --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy" +fi + +XCADDY_DEBUG=0 +if [ -n "${DEBUG_SYMBOLS}" ]; then + XCADDY_DEBUG=1 +fi + +go env +cd caddy/ +# shellcheck disable=SC2086 +CGO_ENABLED=1 \ + XCADDY_GO_BUILD_FLAGS="-buildmode=pie -tags cgo,netgo,osusergo,nobadger,nomysql,nopgx \ + -ldflags \"-linkmode=external -extldflags '-pie ${extraExtldflags} ${extraLdflags}' \ + -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ${FRANKENPHP_VERSION} PHP ${LIBPHP_VERSION} Caddy'\"" \ + XCADDY_DEBUG="${XCADDY_DEBUG}" \ + ${XCADDY_COMMAND} build \ + --output "../dist/${bin}" \ + ${XCADDY_ARGS} \ + --with github.com/dunglas/frankenphp=.. \ + --with github.com/dunglas/frankenphp/caddy=. +cd .. + +if [ -d "${EMBED}" ]; then + truncate -s 0 app.tar + truncate -s 0 app_checksum.txt +fi + +if type "upx" >/dev/null 2>&1 && [ -z "${DEBUG_SYMBOLS}" ] && [ -z "${NO_COMPRESS}" ]; then + upx --best "dist/${bin}" +fi + +"dist/${bin}" version + +if [ -n "${RELEASE}" ]; then + gh release upload "v${FRANKENPHP_VERSION}" "dist/${bin}" --repo dunglas/frankenphp --clobber +fi + +if [ -n "${CURRENT_REF}" ]; then + git checkout "${CURRENT_REF}" +fi diff --git a/static-glibc-builder.Dockerfile b/static-glibc-builder.Dockerfile new file mode 100644 index 000000000..b7b0645a5 --- /dev/null +++ b/static-glibc-builder.Dockerfile @@ -0,0 +1,91 @@ +FROM centos:7 + +ARG TARGETARCH +ARG GOARCH + +ARG FRANKENPHP_VERSION='' +ENV FRANKENPHP_VERSION=${FRANKENPHP_VERSION} + +ARG PHP_VERSION='' +ENV PHP_VERSION=${PHP_VERSION} + +ARG PHP_EXTENSIONS='' +ARG PHP_EXTENSION_LIBS='' +ARG XCADDY_ARGS='' +ARG CLEAN='' +ARG EMBED='' +ARG DEBUG_SYMBOLS='' +ARG MIMALLOC='' +ARG NO_COMPRESS='' + +RUN BASE_ARCH=$(uname -m); \ + if [ "$BASE_ARCH" = "arm64" ]; then \ + TARGETARCH=aarch64; \ + GOARCH=arm64; \ + else \ + TARGETARCH=x86_64; \ + GOARCH=amd64; \ + fi + +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 install -y centos-release-scl + +RUN if [ "$TARGETARCH" = "aarch64" ]; then \ + sed -i 's|mirror.centos.org/centos|vault.centos.org/altarch|g' /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo ; \ + sed -i 's|mirror.centos.org/centos|vault.centos.org/altarch|g' /etc/yum.repos.d/CentOS-SCLo-scl.repo ; \ + else \ + sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/*.repo ; \ + fi +RUN 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 && \ + yum install -y devtoolset-10-gcc-* && \ + localedef -c -i en_US -f UTF-8 en_US.UTF-8 + +RUN echo "source scl_source enable devtoolset-10" >> /etc/bashrc +RUN source /etc/bashrc + +# Install CMake +RUN curl -o cmake.tgz -fsSL https://github.com/Kitware/CMake/releases/download/v3.31.4/cmake-3.31.4-linux-$TARGETARCH.tar.gz && \ + mkdir /cmake && \ + tar -xzf cmake.tgz -C /cmake --strip-components 1 + +RUN curl -o make.tgz -fsSL https://ftp.gnu.org/gnu/make/make-4.4.tar.gz && \ + tar -zxvf make.tgz && \ + cd make-4.4 && \ + ./configure && \ + make && \ + make install && \ + ln -sf /usr/local/bin/make /usr/bin/make + +RUN curl -o automake.tgz -fsSL https://ftp.gnu.org/gnu/automake/automake-1.17.tar.xz && \ + tar -xvf automake.tgz && \ + cd automake-1.17 && \ + ./configure && \ + make && \ + make install && \ + ln -sf /usr/local/bin/automake /usr/bin/automake + +# Install Go +RUN curl -o go.tgz -fsSL https://go.dev/dl/go1.24.1.linux-$GOARCH && \ + rm -rf /usr/local/go && tar -C /usr/local -xzf go.tgz \ + +ENV PATH="/cmake/bin:/usr/local/go/bin:$PATH" +ENV CC=/opt/rh/devtoolset-10/root/usr/bin/gcc +ENV CXX=/opt/rh/devtoolset-10/root/usr/bin/g++ +ENV AR=/opt/rh/devtoolset-10/root/usr/bin/ar +ENV LD=/opt/rh/devtoolset-10/root/usr/bin/ld +ENV SPC_DEFAULT_C_FLAGS="-fPIE -fPIC" +ENV SPC_LIBC=glibc +ENV SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS_PROGRAM="-Wl,-O1 -pie" +ENV SPC_CMD_VAR_PHP_MAKE_EXTRA_LIBS="-ldl -lpthread -lm -lresolv -lutil -lrt" + +RUN --mount=type=secret,id=github-token GITHUB_TOKEN=$(cat /run/secrets/github-token) ./build-static-glibc.sh && \ + rm -Rf dist/static-php-cli/source/* +