Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ jobs:
artifact_name: ${{ needs.Prepare.outputs.artifact_name }}
artifact_run_id: ${{ needs.Prepare.outputs.artifact_run_id }}
archive_name: ${{ needs.Prepare.outputs.archive_name }}
llvm_cache_key_amd64: ${{ needs.Prepare.outputs.llvm_cache_key_amd64 }}
llvm_cache_key_arm64: ${{ needs.Prepare.outputs.llvm_cache_key_arm64 }}
Linux_Emulated:
needs: Prepare
uses: ./.github/workflows/ci_emulated_linux.yaml
Expand Down
47 changes: 47 additions & 0 deletions .github/workflows/ci_native_linux.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ on:
archive_name:
required: true
type: string
llvm_cache_key_amd64:
description: actions/cache key of the pre-fetched LLVM apt packages for amd64 (from ci_prepare.yaml)
required: true
type: string
llvm_cache_key_arm64:
description: actions/cache key of the pre-fetched LLVM apt packages for arm64 (from ci_prepare.yaml)
required: true
type: string
jobs:
Linux:
env:
Expand All @@ -36,6 +44,45 @@ jobs:
ARCHIVE_PATH="${RUNNER_TEMP}/source-archive/${ARCHIVE_NAME}"
tar -xzf "${ARCHIVE_PATH}" --strip-components=1 -C "${GITHUB_WORKSPACE}"
rm -f "${ARCHIVE_PATH}"
- name: Detect toolchain cache keys
if: matrix.distro != 'alpine'
id: toolchain
env:
LLVM_CACHE_KEY_AMD64: ${{ inputs.llvm_cache_key_amd64 }}
LLVM_CACHE_KEY_ARM64: ${{ inputs.llvm_cache_key_arm64 }}
run: |
VALGRIND_VERSION="$(sed -n 's/^ARG VALGRIND_VERSION=//p' "Dockerfile" | head -n 1)"
if test -z "${VALGRIND_VERSION}"; then
echo "Error: VALGRIND_VERSION not found in Dockerfile." >&2
exit 1
fi
ARCH="$(dpkg --print-architecture)"
case "${ARCH}" in
amd64) LLVM_CACHE_KEY="${LLVM_CACHE_KEY_AMD64}";;
arm64) LLVM_CACHE_KEY="${LLVM_CACHE_KEY_ARM64}";;
*) echo "Error: no LLVM cache key for architecture ${ARCH}." >&2; exit 1;;
esac
if test -z "${LLVM_CACHE_KEY}"; then
echo "Error: empty LLVM cache key for architecture ${ARCH}." >&2
exit 1
fi
echo "valgrind_version=${VALGRIND_VERSION}" >> "${GITHUB_OUTPUT}"
echo "arch=${ARCH}" >> "${GITHUB_OUTPUT}"
echo "llvm_cache_key=${LLVM_CACHE_KEY}" >> "${GITHUB_OUTPUT}"
- name: Restore Valgrind tarball from cache
if: matrix.distro != 'alpine'
uses: actions/cache/restore@v6
with:
path: valgrind-${{ steps.toolchain.outputs.valgrind_version }}.tar.bz2
key: valgrind-tarball-${{ steps.toolchain.outputs.valgrind_version }}
fail-on-cache-miss: true
- name: Restore LLVM apt packages from cache
if: matrix.distro != 'alpine'
uses: actions/cache/restore@v6
with:
path: llvm-debs/${{ matrix.distro }}-${{ steps.toolchain.outputs.arch }}
key: ${{ steps.toolchain.outputs.llvm_cache_key }}
fail-on-cache-miss: true
- name: Pull PHP container image
id: pull-image
run: |
Expand Down
99 changes: 98 additions & 1 deletion .github/workflows/ci_prepare.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,23 @@ on:
archive_name:
description: Name of the tar.gz workspace archive
value: ${{ jobs.prepare.outputs.archive_name }}
llvm_cache_key_amd64:
description: actions/cache key of the pre-fetched LLVM apt packages for amd64
value: ${{ jobs.prepare.outputs.llvm_cache_key_amd64 }}
llvm_cache_key_arm64:
description: actions/cache key of the pre-fetched LLVM apt packages for arm64
value: ${{ jobs.prepare.outputs.llvm_cache_key_arm64 }}
jobs:
prepare:
runs-on: ubuntu-24.04
env:
LLVM_APT_CODENAME: trixie
outputs:
artifact_name: ${{ steps.metadata.outputs.artifact_name }}
artifact_run_id: ${{ steps.metadata.outputs.artifact_run_id }}
archive_name: ${{ steps.metadata.outputs.archive_name }}
llvm_cache_key_amd64: ${{ steps.llvm.outputs.cache_key_amd64 }}
llvm_cache_key_arm64: ${{ steps.llvm.outputs.cache_key_arm64 }}
steps:
- name: Checkout repository
uses: actions/checkout@v7
Expand Down Expand Up @@ -50,6 +60,89 @@ jobs:
-o "${VALGRIND_TARBALL}" \
"https://sourceware.org/pub/valgrind/valgrind-${VALGRIND_VERSION}.tar.bz2"
fi
- name: Detect LLVM version and packages
id: llvm
run: |
LLVM_VERSION="$(sed -n 's/^ARG LLVM_VERSION=//p' "Dockerfile" | head -n 1)"
LLVM_PACKAGES="$(sed -n 's/^ARG LLVM_PACKAGES="\(.*\)"$/\1/p' "Dockerfile" | head -n 1)"
if test -z "${LLVM_VERSION}" || test -z "${LLVM_PACKAGES}"; then
echo "Error: LLVM_VERSION or LLVM_PACKAGES not found in Dockerfile." >&2
exit 1
fi
PACKAGES_HASH="$(printf '%s' "${LLVM_PACKAGES}" | sed "s/@/${LLVM_VERSION}/g" | sha256sum | cut -c1-12)"
echo "version=${LLVM_VERSION}" >> "${GITHUB_OUTPUT}"
echo "packages=${LLVM_PACKAGES}" >> "${GITHUB_OUTPUT}"
for ARCH in amd64 arm64; do
CACHE_KEY="llvm-debs-${LLVM_APT_CODENAME}-${ARCH}-${LLVM_VERSION}-${PACKAGES_HASH}"
echo "${ARCH}: ${CACHE_KEY}"
echo "cache_key_${ARCH}=${CACHE_KEY}" >> "${GITHUB_OUTPUT}"
done
- name: Cache LLVM apt packages (amd64)
uses: actions/cache@v6
with:
path: llvm-debs/${{ env.LLVM_APT_CODENAME }}-amd64
key: ${{ steps.llvm.outputs.cache_key_amd64 }}
- name: Cache LLVM apt packages (arm64)
uses: actions/cache@v6
with:
path: llvm-debs/${{ env.LLVM_APT_CODENAME }}-arm64
key: ${{ steps.llvm.outputs.cache_key_arm64 }}
- name: Download LLVM apt packages if not cached
env:
LLVM_VERSION: ${{ steps.llvm.outputs.version }}
LLVM_PACKAGES: ${{ steps.llvm.outputs.packages }}
run: |
LLVM_ARCHITECTURES=""
DOCKER_ARGS=()
for ARCH in amd64 arm64; do
LLVM_DEBS_DIR="llvm-debs/${LLVM_APT_CODENAME}-${ARCH}"
if test -f "${LLVM_DEBS_DIR}/Packages"; then
echo "LLVM apt packages for ${ARCH} restored from cache."
continue
fi
mkdir -p "${LLVM_DEBS_DIR}"
LLVM_ARCHITECTURES="${LLVM_ARCHITECTURES} ${ARCH}"
DOCKER_ARGS+=(-v "${PWD}/${LLVM_DEBS_DIR}:/out/${ARCH}")
done
if test -z "${LLVM_ARCHITECTURES}"; then
exit 0
fi
LLVM_APT_KEYRING="${RUNNER_TEMP}/llvm-snapshot.gpg"
curl -fsSL --retry 5 --retry-delay 10 --retry-all-errors "https://apt.llvm.org/llvm-snapshot.gpg.key" \
| gpg --dearmor --yes -o "${LLVM_APT_KEYRING}"
cat > "${RUNNER_TEMP}/fetch-llvm-debs.sh" <<'EOF'
export DEBIAN_FRONTEND="noninteractive"
LLVM_APT_KEYRING="/usr/share/keyrings/llvm-snapshot.gpg"
LLVM_APT_PACKAGES="$(printf '%s' "${LLVM_PACKAGES}" | sed "s/@/${LLVM_VERSION}/g")"
apt-get update
apt-get install -y --no-install-recommends "ca-certificates" "apt-utils"
for ARCH in ${LLVM_ARCHITECTURES}; do
dpkg --add-architecture "${ARCH}"
done
Comment thread
zeriyoshi marked this conversation as resolved.
Outdated
echo "deb [signed-by=${LLVM_APT_KEYRING}] https://apt.llvm.org/${LLVM_APT_CODENAME}/ llvm-toolchain-${LLVM_APT_CODENAME}-${LLVM_VERSION} main" \
> "/etc/apt/sources.list.d/llvm.list"
apt-get -o Acquire::Retries=5 update
for ARCH in ${LLVM_ARCHITECTURES}; do
echo "[Pskel] Downloading LLVM ${LLVM_VERSION} apt packages for ${ARCH}." >&2
rm -f /var/cache/apt/archives/*.deb
apt-get -o Acquire::Retries=5 install -y --no-install-recommends --download-only \
$(for PACKAGE in ${LLVM_APT_PACKAGES}; do printf '%s:%s ' "${PACKAGE}" "${ARCH}"; done)
find /var/cache/apt/archives -maxdepth 1 -name '*.deb' \
\( -name "*_${ARCH}.deb" -o -name '*_all.deb' \) -exec cp -t "/out/${ARCH}/" {} +
cp "${LLVM_APT_KEYRING}" "/out/${ARCH}/"
cd "/out/${ARCH}"
apt-ftparchive packages . > "Packages"
apt-ftparchive release . > "Release"
ls -la
du -sh .
cd - >/dev/null
done
EOF
docker run --rm -i \
-e LLVM_VERSION -e LLVM_PACKAGES -e LLVM_APT_CODENAME -e "LLVM_ARCHITECTURES=${LLVM_ARCHITECTURES}" \
-v "${LLVM_APT_KEYRING}:/usr/share/keyrings/llvm-snapshot.gpg:ro" \
"${DOCKER_ARGS[@]}" \
"debian:${LLVM_APT_CODENAME}-slim" sh -eu -s < "${RUNNER_TEMP}/fetch-llvm-debs.sh"
- name: Record artifact metadata
id: metadata
run: |
Expand All @@ -61,10 +154,14 @@ jobs:
id: archive
env:
ARCHIVE_NAME: ${{ steps.metadata.outputs.archive_name }}
VALGRIND_VERSION: ${{ steps.valgrind.outputs.version }}
run: |
ARCHIVE_PATH="${RUNNER_TEMP}/${ARCHIVE_NAME}"
REPO_DIR="$(basename "${GITHUB_WORKSPACE}")"
tar -czf "${ARCHIVE_PATH}" -C "$(dirname "${GITHUB_WORKSPACE}")" "${REPO_DIR}"
tar -czf "${ARCHIVE_PATH}" \
--exclude="${REPO_DIR}/valgrind-${VALGRIND_VERSION}.tar.bz2" \
--exclude="${REPO_DIR}/llvm-debs" \
-C "$(dirname "${GITHUB_WORKSPACE}")" "${REPO_DIR}"
echo "archive-path=${ARCHIVE_PATH}" >> "${GITHUB_OUTPUT}"
- name: Upload prepared workspace
uses: actions/upload-artifact@v7
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/ext/*.dep
/ext/*~
/valgrind-*.tar.bz2
/llvm-debs
*.DS_Store
lcov.info
30 changes: 22 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@ ARG TAG=8.5-cli-trixie
ARG SKIP_VALGRIND=0
# renovate: datasource=github-releases depName=llvm/llvm-project
ARG LLVM_VERSION=23
# LLVM apt packages installed into the base image ("@" is replaced with LLVM_VERSION).
ARG LLVM_PACKAGES="clang-@ libclang-rt-@-dev lld-@ libc++-@-dev libc++abi-@-dev llvm-@ llvm-@-dev llvm-@-runtime"

FROM --platform=${PLATFORM} ${IMAGE}:${TAG} AS base

ARG LLVM_VERSION
ARG LLVM_PACKAGES

ENV USE_ZEND_ALLOC=0
ENV USE_TRACKED_ALLOC=1
ENV ZEND_DONT_UNLOAD_MODULES=1
ENV LC_ALL="C"

RUN docker-php-source extract \
RUN --mount=type=bind,source=.,target=/build_context \
docker-php-source extract \
&& if test -f "/etc/debian_version"; then \
apt-get update && \
DEBIAN_FRONTEND="noninteractive" apt-get install -y "bison" "re2c" "zlib1g-dev" "libsqlite3-dev" "libxml2-dev" \
Expand All @@ -24,15 +28,25 @@ RUN docker-php-source extract \
"unzip" && \
LLVM_APT_CODENAME="$(. "/etc/os-release" && printf '%s' "${VERSION_CODENAME}")" && \
test -n "${LLVM_APT_CODENAME}" && \
LLVM_APT_KEYRING="/usr/share/keyrings/llvm-snapshot.gpg" && \
LLVM_APT_SOURCE="deb [signed-by=${LLVM_APT_KEYRING}] https://apt.llvm.org/${LLVM_APT_CODENAME}/ llvm-toolchain-${LLVM_APT_CODENAME}-${LLVM_VERSION} main" && \
LLVM_DEBS_DIR="/build_context/llvm-debs/${LLVM_APT_CODENAME}-$(dpkg --print-architecture)" && \
mkdir -p "/usr/share/keyrings" && \
curl -fsSL "https://apt.llvm.org/llvm-snapshot.gpg.key" | gpg --dearmor --yes -o "/usr/share/keyrings/llvm-snapshot.gpg" && \
echo "deb [signed-by=/usr/share/keyrings/llvm-snapshot.gpg] https://apt.llvm.org/${LLVM_APT_CODENAME}/ llvm-toolchain-${LLVM_APT_CODENAME}-${LLVM_VERSION} main" > "/etc/apt/sources.list.d/llvm.list" && \
if test -f "${LLVM_DEBS_DIR}/llvm-snapshot.gpg"; then \
cp "${LLVM_DEBS_DIR}/llvm-snapshot.gpg" "${LLVM_APT_KEYRING}"; \
else \
curl -fsSL "https://apt.llvm.org/llvm-snapshot.gpg.key" | gpg --dearmor --yes -o "${LLVM_APT_KEYRING}"; \
fi && \
if test -f "${LLVM_DEBS_DIR}/Packages"; then \
echo "[Pskel] Installing LLVM ${LLVM_VERSION} from pre-fetched apt packages in ${LLVM_DEBS_DIR}." >&2 && \
echo "deb [trusted=yes] file:${LLVM_DEBS_DIR} ./" > "/etc/apt/sources.list.d/llvm.list"; \
else \
echo "[Pskel] Installing LLVM ${LLVM_VERSION} from apt.llvm.org." >&2 && \
echo "${LLVM_APT_SOURCE}" > "/etc/apt/sources.list.d/llvm.list"; \
fi && \
Comment thread
zeriyoshi marked this conversation as resolved.
Outdated
apt-get update && \
apt-get install --no-install-recommends -y \
"clang-${LLVM_VERSION}" \
"libclang-rt-${LLVM_VERSION}-dev" "lld-${LLVM_VERSION}" \
"libc++-${LLVM_VERSION}-dev" "libc++abi-${LLVM_VERSION}-dev" \
"llvm-${LLVM_VERSION}" "llvm-${LLVM_VERSION}-dev" "llvm-${LLVM_VERSION}-runtime" && \
apt-get install --no-install-recommends -y $(printf '%s' "${LLVM_PACKAGES}" | sed "s/@/${LLVM_VERSION}/g") && \
echo "${LLVM_APT_SOURCE}" > "/etc/apt/sources.list.d/llvm.list" && \
update-alternatives --install "/usr/bin/clang" clang "/usr/bin/clang-${LLVM_VERSION}" 100 && \
update-alternatives --install "/usr/bin/clang++" clang++ "/usr/bin/clang++-${LLVM_VERSION}" 100 && \
update-alternatives --install "/usr/bin/ld.lld" ld.lld "/usr/bin/ld.lld-${LLVM_VERSION}" 100 && \
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ $ gdb --args <php_binary> -dextension=./modules/your_extension_name.so example.p
### Q: How is Valgrind installed?
A: Valgrind is downloaded from [sourceware.org](https://sourceware.org/pub/valgrind/) and built from source when the container image is built. The version is pinned by the `VALGRIND_VERSION` build argument in the `Dockerfile`. If you place a pre-fetched `valgrind-<version>.tar.bz2` in the repository root (excluded from Git via `.gitignore`), it is used instead of downloading. Passing the `SKIP_VALGRIND=1` build argument skips building Valgrind entirely. Valgrind is only supported on Debian-based images: building a non-Debian (e.g. Alpine) image fails unless `SKIP_VALGRIND=1` is passed explicitly.

### Q: How is LLVM installed?
A: On Debian-based images, the LLVM toolchain is installed from [apt.llvm.org](https://apt.llvm.org/) when the container image is built. The major version is pinned by the `LLVM_VERSION` build argument and the package list by the `LLVM_PACKAGES` build argument in the `Dockerfile`. If a directory `llvm-debs/<codename>-<arch>/` (e.g. `llvm-debs/trixie-amd64/`) containing the `.deb` files together with `Packages` and `Release` indexes exists in the repository root (excluded from Git via `.gitignore`), it is used as a local apt repository instead of apt.llvm.org. The CI Prepare workflow pre-fetches these packages once per architecture and shares them with the Linux build jobs through the GitHub Actions cache, so that the build jobs never download from apt.llvm.org.
Comment thread
zeriyoshi marked this conversation as resolved.
Outdated

### Q: Can I use editors other than Visual Studio Code?
A: While not recommended, you can use any editor that supports [Development Containers](https://containers.dev).

Expand Down
3 changes: 3 additions & 0 deletions README_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ $ gdb --args <php_binary> -dextension=./modules/your_extension_name.so example.p
### Q: Valgrind はどのようにインストールされますか?
A: コンテナイメージのビルド時に [sourceware.org](https://sourceware.org/pub/valgrind/) からソースをダウンロードしてビルドされます。バージョンは `Dockerfile` のビルド引数 `VALGRIND_VERSION` で固定されています。事前に取得した `valgrind-<version>.tar.bz2` をリポジトリのトップレベルに配置すると (`.gitignore` により Git 管理外)、ダウンロードの代わりにそれが使用されます。また、ビルド引数 `SKIP_VALGRIND=1` を指定することで Valgrind のビルド自体をスキップできます。Valgrind は Debian ベースのイメージでのみサポートされるため、非 Debian (Alpine など) のイメージをビルドする場合は `SKIP_VALGRIND=1` を明示的に指定しない限りビルドは失敗します。

### Q: LLVM はどのようにインストールされますか?
A: Debian ベースのイメージでは、コンテナイメージのビルド時に [apt.llvm.org](https://apt.llvm.org/) から LLVM ツールチェインをインストールします。メジャーバージョンは `Dockerfile` のビルド引数 `LLVM_VERSION`、パッケージ一覧はビルド引数 `LLVM_PACKAGES` で固定されています。リポジトリのトップレベルに `.deb` ファイルと `Packages` / `Release` インデックスを含む `llvm-debs/<codename>-<arch>/` ディレクトリ (例: `llvm-debs/trixie-amd64/`) を配置すると (`.gitignore` により Git 管理外)、apt.llvm.org の代わりにそれがローカル apt リポジトリとして使用されます。CI の Prepare ワークフローはこのパッケージ群をアーキテクチャごとに 1 回だけ事前取得し、GitHub Actions cache 経由で Linux のビルド job に共有するため、ビルド job は apt.llvm.org からダウンロードを行いません。
Comment thread
zeriyoshi marked this conversation as resolved.
Outdated

### Q: Visual Studio Code 以外のエディタは使用できますか?
A: 推奨はしませんが、 [Development Containers](https://containers.dev) 対応のエディタであれば使用可能です。

Expand Down
Loading