feat: create a static build of FrankenPHP (#198)

* ci: create a static build of FrankenPHP

* try to use alpine

* path mapping

* cache and fixes

* fix

* fix include path

* fix include path

* fix include path

* switch to Docker

* fix github token

* cleanup

* various improvements

* macOS instructions

* fix GHA

* docs

* feat: mac static builds

* minor fix

* fix wd

* Apple silicon build

* Revert "Apple silicon build"

This reverts commit 7a2997e092.

* add opcache

* update builder

* upgrade to PHP 8.2

* switch to upstream static-php-cli, add intl
This commit is contained in:
Kévin Dunglas 2023-09-12 22:08:19 +02:00 committed by GitHub
parent a6603b58cc
commit 1f3007337d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 274 additions and 7 deletions

View File

@ -20,7 +20,7 @@ jobs:
platforms: ${{ steps.matrix.outputs.platforms }}
metadata: ${{ steps.matrix.outputs.metadata }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
@ -57,7 +57,7 @@ jobs:
- platform: linux/386
qemu: false
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up QEMU
if: matrix.qemu

97
.github/workflows/static.yml vendored Normal file
View File

@ -0,0 +1,97 @@
name: Build binary releases
on:
pull_request:
branches:
- main
push:
branches:
- main
tags:
- v*
workflow_dispatch:
inputs: {}
jobs:
build-linux:
name: Build Linux x86_64 binary
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
version: latest
- name: Build
id: build
uses: docker/bake-action@v3
with:
pull: true
load: true
targets: static-builder
set: |
*.cache-from=type=gha,scope=${{github.ref}}-static-builder
*.cache-from=type=gha,scope=refs/heads/main-static-builder
*.cache-to=type=gha,scope=${{github.ref}}-static-builder
env:
VERSION: ${{github.ref_name}}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Copy binary
run: docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/caddy/frankenphp/frankenphp frankenphp ; docker rm static-builder
- name: Upload binary
uses: actions/upload-artifact@v3
with:
name: frankenphp-linux-x86_64-dev
path: frankenphp
build-mac:
name: Build macOS binaries
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: crazywhalecc/static-php-cli
path: static-php-cli
- uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Install static-php-cli dependencies
working-directory: static-php-cli/
run: composer install --no-dev -a
- name: Install missing system dependencies
run: ./bin/spc doctor --auto-fix
working-directory: static-php-cli/
- name: Fetch libraries sources
working-directory: static-php-cli/
run: ./bin/spc fetch --with-php=8.2 -A
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build static libphp
working-directory: static-php-cli/
run: ./bin/spc build --enable-zts --build-embed --debug "bcmath,calendar,ctype,curl,dba,dom,exif,filter,fileinfo,gd,iconv,intl,mbstring,mbregex,mysqli,mysqlnd,opcache,openssl,pcntl,pdo,pdo_mysql,pdo_pgsql,pdo_sqlite,pgsql,phar,posix,readline,redis,session,simplexml,sockets,sqlite3,tokenizer,xml,xmlreader,xmlwriter,zip,zlib,apcu"
- name: Set CGO flags
working-directory: static-php-cli/
run: |
echo "CGO_CFLAGS=$(./buildroot/bin/php-config --includes | sed s#-I/#-I$PWD/buildroot/#g)" >> "$GITHUB_ENV"
echo "CGO_LDFLAGS=-framework CoreFoundation -framework SystemConfiguration $(./buildroot/bin/php-config --ldflags) $(./buildroot/bin/php-config --libs)" >> "$GITHUB_ENV"
- name: Build FrankenPHP
working-directory: caddy/frankenphp/
run: go build -buildmode=pie -tags "cgo netgo osusergo static_build" -ldflags "-linkmode=external -extldflags -static-pie -w -s"
- name: Upload binary
uses: actions/upload-artifact@v3
with:
name: frankenphp-mac-x86_64-dev
path: caddy/frankenphp/frankenphp

View File

@ -7,7 +7,7 @@ jobs:
matrix:
php-versions: ['8.2', '8.3']
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-go@v4
with:

View File

@ -6,6 +6,10 @@ variable "VERSION" {
default = "dev"
}
variable "GO_VERSION" {
default = "1.21"
}
variable "SHA" {}
variable "LATEST" {
@ -59,7 +63,7 @@ target "default" {
}
contexts = {
php-base = "docker-image://php:${php-version}-zts-${os}"
golang-base = "docker-image://golang:1.21-${os}"
golang-base = "docker-image://golang:${GO_VERSION}-${os}"
}
dockerfile = os == "alpine" ? "alpine.Dockerfile" : "Dockerfile"
context = "./"
@ -91,3 +95,16 @@ target "default" {
FRANKENPHP_VERSION = VERSION
}
}
target "static-builder" {
contexts = {
golang-base = "docker-image://golang:${GO_VERSION}-alpine"
}
dockerfile = "static-builder.Dockerfile"
context = "./"
tags = ["${IMAGE_NAME}:static-builder"]
args = {
FRANKENPHP_VERSION = VERSION
}
secret = ["id=github-token,env=GITHUB_TOKEN"]
}

View File

@ -1,5 +1,10 @@
# Compile From Sources
This document explain how to create a FrankenPHP build that will load PHP as a dymanic library.
This is the recommended method.
Alternatively, [creating static builds](static.md) is also possible.
## Install PHP
FrankenPHP is compatible with the PHP 8.2 and superior.

64
docs/static.md Normal file
View File

@ -0,0 +1,64 @@
# Create a Static Build
Instead of using a local installation of the PHP library,
it's possible to create a static build of FrankenPHP thanks to the great [static-php-cli project](https://github.com/crazywhalecc/static-php-cli) (despite its name, this project support all SAPIs, not only CLI).
With this method, a single, portable, binary will contain the PHP interpreter, the Caddy web server and FrankenPHP!
## Linux
We provide a Docker image to build a Linux static binary:
```console
docker buildx bake --load static-builder
docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/caddy/frankenphp/frankenphp frankenphp ; docker rm static-builder
```
The resulting static binary is named `frankenphp` and is available in the current directory.
If you want to build the static binary without Docker, take a look to the `static-builder.Dockerfile` file.
### Custom Extensions
By default, most popular PHP extensions are compiled.
To reduce the size of the binary and to reduce the attack surface, you can choose the list of extensions to build using the `PHP_EXTENSIONS` Docker ARG.
For instance, run the following command to only build the `opcache` extension:
```console
docker buildx bake --load --set static-builder.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder
# ...
```
See [the list of supported extensions](https://static-php-cli.zhamao.me/en/guide/extensions.html).
### GitHub Token
If you hit the GitHub API rate limit, set a GitHub Personal Access Token in an environment variable named `GITHUB_TOKEN`:
```console
GITHUB_TOKEN="xxx" docker --load buildx bake static-builder
# ...
```
## macOS
Run the following command to create a static binary for macOS:
```console
git clone --depth=1 https://github.com/crazywhalecc/static-php-cli.git
cd static-php-cli
composer install --no-dev -a
./bin/spc doctor --auto-fix
./bin/spc fetch --with-php=8.2 -A
./bin/spc build --enable-zts --build-embed --debug "bcmath,calendar,ctype,curl,dba,dom,exif,filter,fileinfo,gd,iconv,intl,mbstring,mbregex,mysqli,mysqlnd,opcache,openssl,pcntl,pdo,pdo_mysql,pdo_pgsql,pdo_sqlite,pgsql,phar,posix,readline,redis,session,simplexml,sockets,sqlite3,tokenizer,xml,xmlreader,xmlwriter,zip,zlib,apcu"
export CGO_CFLAGS="$(./buildroot/bin/php-config --includes | sed s#-I/#-I$PWD/buildroot/#g)"
export CGO_LDFLAGS="-framework CoreFoundation -framework SystemConfiguration $(./buildroot/bin/php-config --ldflags) $(./buildroot/bin/php-config --libs)"
git clone --depth=1 https://github.com/dunglas/frankenphp.git
cd frankenphp/caddy/frankenphp
go build -buildmode=pie -tags "cgo netgo osusergo static_build" -ldflags "-linkmode=external -extldflags -static-pie"
```
See [the list of supported extensions](https://static-php-cli.zhamao.me/en/guide/extensions.html).

View File

@ -12,13 +12,18 @@ package frankenphp
// Use PHP includes corresponding to your PHP installation by running:
//
// export CGO_CFLAGS=$(php-config --includes)
// export CGO_LDFLAGS=$(php-config --ldflags --libs)
//
// We also set these flags for hardening: https://github.com/docker-library/php/blob/master/8.2/bookworm/zts/Dockerfile#L57-L59
// #cgo pkg-config: libxml-2.0 sqlite3
// #cgo CFLAGS: -Wall -Werror
// #cgo darwin pkg-config: libxml-2.0 sqlite3
// #cgo CFLAGS: -Wall -Werror -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
// #cgo CFLAGS: -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib
// #cgo linux CFLAGS: -D_GNU_SOURCE
// #cgo CPPFLAGS: -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
// #cgo darwin LDFLAGS: -L/opt/homebrew/opt/libiconv/lib -liconv
// #cgo LDFLAGS: -L/usr/local/lib -L/usr/lib -lphp -lresolv -ldl -lm -lutil
// #cgo linux LDFLAGS: -Wl,-O1
// #cgo LDFLAGS: -pie -L/usr/local/lib -L/usr/lib -lphp -lresolv -ldl -lm -lutil
// #include <stdlib.h>
// #include <stdint.h>
// #include <php_variables.h>

79
static-builder.Dockerfile Normal file
View File

@ -0,0 +1,79 @@
# syntax=docker/dockerfile:1
FROM golang-base
ARG FRANKENPHP_VERSION='dev'
ARG PHP_VERSION='8.2'
ARG PHP_EXTENSIONS='bcmath,calendar,ctype,curl,dba,dom,exif,filter,fileinfo,gd,iconv,intl,mbstring,mbregex,mysqli,mysqlnd,opcache,openssl,pcntl,pdo,pdo_mysql,pdo_pgsql,pdo_sqlite,pgsql,phar,posix,readline,redis,session,simplexml,sockets,sqlite3,tokenizer,xml,xmlreader,xmlwriter,zip,zlib,apcu'
RUN apk update; \
apk add --no-cache \
autoconf \
automake \
bash \
binutils \
bison \
build-base \
cmake \
curl \
file \
flex \
g++ \
gcc \
git \
jq \
libgcc \
libstdc++ \
linux-headers \
m4 \
make \
php82 \
php82-common \
php82-dom \
php82-mbstring \
php82-openssl \
php82-pcntl \
php82-phar \
php82-posix \
php82-tokenizer \
php82-xml \
php82-xmlwriter \
pkgconfig \
wget \
xz ; \
ln -sf /usr/bin/php82 /usr/bin/php
# https://getcomposer.org/doc/03-cli.md#composer-allow-superuser
ENV COMPOSER_ALLOW_SUPERUSER=1
ENV PATH="${PATH}:/root/.composer/vendor/bin"
COPY --from=composer/composer:2-bin --link /composer /usr/bin/composer
WORKDIR /static-php-cli
RUN git clone --depth=1 https://github.com/crazywhalecc/static-php-cli . && \
composer install --no-cache --no-dev --classmap-authoritative
RUN --mount=type=secret,id=github-token GITHUB_TOKEN=$(cat /run/secrets/github-token) ./bin/spc download --with-php=$PHP_VERSION --all
RUN ./bin/spc build --build-embed --enable-zts --debug "$PHP_EXTENSIONS"
ENV PATH="/static-php-cli/buildroot/bin:/static-php-cli/buildroot/usr/bin:$PATH"
WORKDIR /go/src/app
COPY go.mod go.sum ./
RUN go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get
RUN mkdir caddy && cd caddy
COPY caddy/go.mod caddy/go.sum ./caddy/
RUN cd caddy && go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get
COPY *.* ./
COPY caddy caddy
COPY C-Thread-Pool C-Thread-Pool
RUN cd caddy/frankenphp && \
CGO_CFLAGS="$(/static-php-cli/buildroot/bin/php-config --includes | sed s#-I/#-I/static-php-cli/buildroot/#g)" \
CGO_LDFLAGS="$(/static-php-cli/buildroot/bin/php-config --ldflags) $(/static-php-cli/buildroot/bin/php-config --libs | sed -e 's/-lgcc_s//g')" \
go build -buildmode=pie -tags "cgo netgo osusergo static_build" -ldflags "-linkmode=external -extldflags -static-pie -s -w -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP $FRANKENPHP_VERSION Caddy'"