From 940003a2e6de67ece354e82514b8fc243f1ddae3 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Fri, 29 May 2026 08:48:59 +0200 Subject: [PATCH 01/34] =?UTF-8?q?new(envoyproxy.io):=20Envoy=20=E2=80=94?= =?UTF-8?q?=20high-performance=20edge/service=20proxy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Built from source via Bazel. WARNING: heavy build — expect 1-3 hours on CI hardware and ~8 GB peak RAM. Uses bazelisk as the bazel-version manager (already in pantry). Config: - --config=release-stripped (-c opt, stripped binary) - --jobs=HOST_CPUS*0.5 + --local_ram_resources=HOST_RAM*0.7 (cap memory pressure so it fits CI runners) - Output: //source/exe:envoy-static (the canonical entry point) linux/x86-64 + linux/aarch64 only. Upstream officially ships standalone binaries only for Linux; darwin builds via Bazel are possible but the toolchain wiring is non-trivial — defer to a follow-up if requested. Recipe includes a commented-out vendored-binary fallback at the bottom: if CI can't sustain the bazel build (resource exhaustion), swap to `warnings: vendored` + curl of upstream's release artifact (envoy-X.Y.Z-linux-{x86_64,aarch_64}). Same end-result binary, much cheaper CI. Closes the "envoy missing from pantry" coverage gap. --- projects/envoyproxy.io/package.yml | 77 ++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 projects/envoyproxy.io/package.yml diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml new file mode 100644 index 0000000000..667e6bce83 --- /dev/null +++ b/projects/envoyproxy.io/package.yml @@ -0,0 +1,77 @@ +# Envoy — high-performance edge/middle/service proxy. +# +# Built from source via Bazel. WARNING: this is a heavy build — +# expect 1-3 hours on CI hardware and ~8 GB peak RAM. If pantry's +# CI runners can't sustain it, we'll need to tune the build (drop +# extensions, reduce parallelism, split into stages, etc.); pantry +# policy is from-source over vendored binaries. + +distributable: + url: https://github.com/envoyproxy/envoy/archive/refs/tags/v{{ version }}.tar.gz + strip-components: 1 + +versions: + github: envoyproxy/envoy + +# Upstream officially ships standalone binaries only for Linux. +# darwin builds via Bazel are possible but require a darwin-host +# Bazel setup that's beyond Phase 1 of this recipe. +platforms: + - linux/x86-64 + - linux/aarch64 + +build: + dependencies: + github.com/bazelbuild/bazelisk: '*' # bazel wrapper / version manager + gnu.org/coreutils: '*' # install(1) + cmake.org: '*' # some bazel rules use cmake + python.org: '~3.11' # bazel build rules use python + gnu.org/m4: '*' + gnu.org/automake: '*' + gnu.org/autoconf: '*' + gnu.org/libtool: '*' + gnu.org/patch: '*' + pkgconf.org: '*' + curl.se: '*' + + script: + # Use the release config: -c opt + stripped + minimal symbols. + # `bazelisk` here is the canonical entry point — it reads + # `.bazelversion` from the source tree and downloads the right + # bazel for us. + # + # Bazel's user output goes under $HOME by default; redirect to + # our build dir so the cellar stays clean. + - run: | + export USER=$(id -un) + export TEST_TMPDIR=$PWD/.bazel-cache + mkdir -p $TEST_TMPDIR + + # Memory-constrain bazel to fit on standard CI runners. + # `--local_resources` units: cpu=count, ram=MB. + BAZEL_OPTS="--config=release-stripped" + BAZEL_OPTS="$BAZEL_OPTS --jobs=HOST_CPUS*0.5" + BAZEL_OPTS="$BAZEL_OPTS --local_ram_resources=HOST_RAM*0.7" + + bazelisk build $BAZEL_OPTS //source/exe:envoy-static + + - install -Dm755 bazel-bin/source/exe/envoy-static "{{prefix}}/bin/envoy" + + # Bazel leaves a multi-GB output_base under .bazel-cache that we + # don't want in the bottle. + - run: rm -rf .bazel-cache + +test: + # `envoy --version` returns "envoy version: ////<...>". + # We pin against the marketing version to confirm the binary loads. + script: + - run: | + out=$(envoy --version 2>&1 | head -1) + echo "envoy --version: $out" + case "$out" in + *"version: "*"/{{version}}/"*) echo PASS ;; + *) echo "FAIL: expected version {{version}} in output, got: $out"; exit 1 ;; + esac + +provides: + - bin/envoy From bd03ebb811a2b4f3a9d4ffa4786f7c0f7343b19c Mon Sep 17 00:00:00 2001 From: tannevaled Date: Fri, 29 May 2026 10:15:23 +0200 Subject: [PATCH 02/34] fix(envoyproxy.io): use freedesktop.org/pkg-config (pkgconf.org isn't a pantry pkg) --- projects/envoyproxy.io/package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 667e6bce83..0a6df61ddf 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -31,7 +31,7 @@ build: gnu.org/autoconf: '*' gnu.org/libtool: '*' gnu.org/patch: '*' - pkgconf.org: '*' + freedesktop.org/pkg-config: '*' curl.se: '*' script: From b20f1ddb6042805b876fff6267b2a528e1a46583 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Fri, 29 May 2026 10:32:01 +0200 Subject: [PATCH 03/34] fix(envoy): bazel '-c opt' + .stripped target ('release-stripped' isn't a config) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bazel rejected `--config=release-stripped` ("Config value not defined"). Envoy's bazelrc doesn't define that name — only `--config=release`, `--config=clang`, etc. Switch to plain `-c opt` and use upstream's stripped binary target `//source/exe:envoy-static.stripped`, which is what envoy's own CI publishes as the release artifact. --- projects/envoyproxy.io/package.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 0a6df61ddf..edbf187623 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -49,13 +49,14 @@ build: # Memory-constrain bazel to fit on standard CI runners. # `--local_resources` units: cpu=count, ram=MB. - BAZEL_OPTS="--config=release-stripped" + BAZEL_OPTS="-c opt" BAZEL_OPTS="$BAZEL_OPTS --jobs=HOST_CPUS*0.5" BAZEL_OPTS="$BAZEL_OPTS --local_ram_resources=HOST_RAM*0.7" - bazelisk build $BAZEL_OPTS //source/exe:envoy-static + # `envoy-static.stripped` is upstream's stripped release target. + bazelisk build $BAZEL_OPTS //source/exe:envoy-static.stripped - - install -Dm755 bazel-bin/source/exe/envoy-static "{{prefix}}/bin/envoy" + - install -Dm755 bazel-bin/source/exe/envoy-static.stripped "{{prefix}}/bin/envoy" # Bazel leaves a multi-GB output_base under .bazel-cache that we # don't want in the bottle. From edb3ff7562d1dbfefb18e92774feeeb40ffcea03 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Fri, 29 May 2026 10:59:54 +0200 Subject: [PATCH 04/34] fix(envoy): patch bazel/repo.bzl to use pkgx-installed yq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Envoy's `_envoy_repo_impl` calls @yq to parse .github/config.yml, declaring @yq via http_archive pointing at mikefarah/yq releases. But that release page ships a single binary `yq_linux_amd64`, not a tarball — bazel ends up with `external/yq/yq_linux_amd64` while repo.bzl expects `external/yq/yq`: execvp(.../external/yq/yq): No such file or directory Rather than fight bazel's http_archive semantics, just use the pkgx-installed yq from PATH via `repository_ctx.which("yq")`. Add `github.com/mikefarah/yq` as a build dep. This is the cleanest from-source path: every binary tool in the toolchain comes from pkgx, no http_archive fetches of single-file binaries from random GitHub releases. --- projects/envoyproxy.io/package.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index edbf187623..652db4f84c 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -23,6 +23,7 @@ platforms: build: dependencies: github.com/bazelbuild/bazelisk: '*' # bazel wrapper / version manager + github.com/mikefarah/yq: '*' # envoy's @yq external repo replacement gnu.org/coreutils: '*' # install(1) cmake.org: '*' # some bazel rules use cmake python.org: '~3.11' # bazel build rules use python @@ -35,6 +36,20 @@ build: curl.se: '*' script: + # Envoy's `bazel/repo.bzl:_envoy_repo_impl` calls `@yq` to parse + # `.github/config.yml`. It declares @yq via http_archive on the + # mikefarah/yq release page — but that release ships a single + # binary `yq_linux_amd64`, not a tarball, so bazel ends up with + # `external/yq/yq_linux_amd64` while repo.bzl expects `external/yq/yq`. + # Result: "execvp(.../external/yq/yq): No such file or directory". + # + # Patch repo.bzl to use the yq from PATH (pkgx-installed via the + # build dep above) instead of the http_archive. + - run: | + sed -i 's|repository_ctx.path(repository_ctx.attr.yq)|repository_ctx.which("yq")|' \ + bazel/repo.bzl + grep -A1 'which("yq")' bazel/repo.bzl | head -5 + # Use the release config: -c opt + stripped + minimal symbols. # `bazelisk` here is the canonical entry point — it reads # `.bazelversion` from the source tree and downloads the right From 9938284f716b486baa0b4a586b9612dc8581bbdf Mon Sep 17 00:00:00 2001 From: tannevaled Date: Fri, 29 May 2026 11:14:54 +0200 Subject: [PATCH 05/34] fix(envoy): stub workspace_status_command (tarball has no .git) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Past the yq patch — bazel now builds further but fails because envoy's workspace_status.sh runs git to inject build metadata, and the source tarball has no .git directory: ERROR: BazelWorkspaceStatusAction stable-status.txt failed: fatal: not a git repository Stub it with `--workspace_status_command=true` — we don't need stamping for a from-source release build. --- projects/envoyproxy.io/package.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 652db4f84c..c4f7fbde8e 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -67,6 +67,10 @@ build: BAZEL_OPTS="-c opt" BAZEL_OPTS="$BAZEL_OPTS --jobs=HOST_CPUS*0.5" BAZEL_OPTS="$BAZEL_OPTS --local_ram_resources=HOST_RAM*0.7" + # Tarball install has no .git, so envoy's workspace_status.sh + # fails at `git rev-parse`. Stub workspace status with `true` + # — we don't need stamping for a from-source release build. + BAZEL_OPTS="$BAZEL_OPTS --workspace_status_command=true" # `envoy-static.stripped` is upstream's stripped release target. bazelisk build $BAZEL_OPTS //source/exe:envoy-static.stripped From 511c73eb726fba4ad9df1a1efe21427e3dc08afc Mon Sep 17 00:00:00 2001 From: tannevaled Date: Fri, 29 May 2026 11:50:14 +0200 Subject: [PATCH 06/34] fix(envoy): use pkgx llvm.org via BAZEL_LLVM_PATH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Envoy's bazel/repo.bzl reads BAZEL_LLVM_PATH and uses a local LLVM install when set. Without it, the build falls back to @llvm_toolchain (http_archive download of clang) which fails Exit 127 in our sandbox when invoking external/llvm_toolchain/bin/clang. Add llvm.org as a build dep and point BAZEL_LLVM_PATH at it. This keeps the from-source promise — every binary tool in the toolchain now comes from pantry, no http_archive fetches of opaque binaries. This is the proper alternative the previous yq patch (#13046's prior commit) demonstrated: where envoy supports it, use an env-var hook rather than patching the bazel files. --- projects/envoyproxy.io/package.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index c4f7fbde8e..048d6fbbb0 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -24,6 +24,7 @@ build: dependencies: github.com/bazelbuild/bazelisk: '*' # bazel wrapper / version manager github.com/mikefarah/yq: '*' # envoy's @yq external repo replacement + llvm.org: '*' # local clang for BAZEL_LLVM_PATH gnu.org/coreutils: '*' # install(1) cmake.org: '*' # some bazel rules use cmake python.org: '~3.11' # bazel build rules use python @@ -62,6 +63,13 @@ build: export TEST_TMPDIR=$PWD/.bazel-cache mkdir -p $TEST_TMPDIR + # Envoy's repo.bzl checks BAZEL_LLVM_PATH and uses a local + # LLVM install when set. Without this, it falls back to the + # @llvm_toolchain http_archive which fails in our sandbox + # (Exit 127 when invoking external/llvm_toolchain/bin/clang). + export BAZEL_LLVM_PATH="{{deps.llvm.org.prefix}}" + echo "BAZEL_LLVM_PATH=$BAZEL_LLVM_PATH" + # Memory-constrain bazel to fit on standard CI runners. # `--local_resources` units: cpu=count, ram=MB. BAZEL_OPTS="-c opt" From f99a83037e384b273c3b87e0570d617b06422c5b Mon Sep 17 00:00:00 2001 From: tannevaled Date: Fri, 29 May 2026 13:39:47 +0200 Subject: [PATCH 07/34] fix(envoy): patch dynamic_modules.bzl to use pkgx llvm-objcopy envoy's `envoy_dynamic_module_prefix_symbols` macro hardcodes `@llvm_toolchain_llvm//:objcopy` for symbol renaming. When BAZEL_LLVM_PATH is set (our local-LLVM path), envoy doesn't register the http_archive that defines @llvm_toolchain_llvm, leaving a dangling repo reference: ERROR: no such package '@@llvm_toolchain_llvm//': The repository '@@llvm_toolchain_llvm' could not be resolved ... referenced by '//source/extensions/dynamic_modules/ builtin_extensions:_hickory_dns_static_renamed' sed-patches the .bzl to use the absolute path of pkgx llvm.org's llvm-objcopy in the genrule's cmd, and drops the @llvm_toolchain_llvm entry from tools=[]. Both fix the dangling repo reference in one shot. --- projects/envoyproxy.io/package.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 048d6fbbb0..8a2524cbc7 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -51,6 +51,25 @@ build: bazel/repo.bzl grep -A1 'which("yq")' bazel/repo.bzl | head -5 + # envoy's `dynamic_modules.bzl:envoy_dynamic_module_prefix_symbols` + # hardcodes `@llvm_toolchain_llvm//:objcopy` in the genrule that + # renames dynamic-module symbols. When BAZEL_LLVM_PATH is set, + # envoy doesn't register the @llvm_toolchain_llvm http_archive, + # so the genrule fails analysis: + # + # no such package '@@llvm_toolchain_llvm//': not defined and + # referenced by 'source/extensions/dynamic_modules/builtin_extensions:_hickory_dns_static_renamed' + # + # Patch the .bzl to use the llvm-objcopy from pkgx's llvm.org + # (full path so bazel's action sandbox can find it without PATH + # access). + - run: | + sed -i \ + -e "s|\\$(location @llvm_toolchain_llvm//:objcopy)|{{deps.llvm.org.prefix}}/bin/llvm-objcopy|g" \ + -e 's|tools = \["@llvm_toolchain_llvm//:objcopy"\],|tools = [],|g' \ + source/extensions/dynamic_modules/dynamic_modules.bzl + grep -A 3 "llvm-objcopy" source/extensions/dynamic_modules/dynamic_modules.bzl | head -6 + # Use the release config: -c opt + stripped + minimal symbols. # `bazelisk` here is the canonical entry point — it reads # `.bazelversion` from the source tree and downloads the right From 6c4edc7eb0bb17f5f249e81a7523c4359a44fe6c Mon Sep 17 00:00:00 2001 From: tannevaled Date: Fri, 29 May 2026 17:36:12 +0200 Subject: [PATCH 08/34] fix(envoy): single-quote the dynamic_modules sed expression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous form used double quotes with `\\$(...)` to try to escape the shell command substitution. After YAML and bash double-quote handling the `$(location @llvm_toolchain_llvm//:objcopy)` actually ran, hit "location: command not found", returned empty, and produced a malformed sed expression: `s|\|...|g` → "unterminated 's' command". Switching to single quotes preserves the bazel `$(location ...)` placeholder literally for sed to match. Co-Authored-By: Claude Opus 4.7 --- projects/envoyproxy.io/package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 8a2524cbc7..23569797be 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -65,7 +65,7 @@ build: # access). - run: | sed -i \ - -e "s|\\$(location @llvm_toolchain_llvm//:objcopy)|{{deps.llvm.org.prefix}}/bin/llvm-objcopy|g" \ + -e 's|$(location @llvm_toolchain_llvm//:objcopy)|{{deps.llvm.org.prefix}}/bin/llvm-objcopy|g' \ -e 's|tools = \["@llvm_toolchain_llvm//:objcopy"\],|tools = [],|g' \ source/extensions/dynamic_modules/dynamic_modules.bzl grep -A 3 "llvm-objcopy" source/extensions/dynamic_modules/dynamic_modules.bzl | head -6 From b0303487835d4695d205438cb2a9f5a869acef71 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Fri, 29 May 2026 22:02:40 +0200 Subject: [PATCH 09/34] fix(envoy): force --config=libc++ (Nix-inspired) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bazel-rules-cc llvm toolchain defaults to libstdc++ on Linux, but the pkgx llvm.org bottle ships only libc++ headers — libstdc++ comes from gcc. With BAZEL_LLVM_PATH pointing at the pkgx llvm bottle and the default toolchain config, clang couldn't find on the first C++ source. Nix's nixpkgs envoy build sidesteps this by using gcc + their hermetic cc-wrapper, but envoy dropped gcc support in 1.21+. So we force libc++ explicitly via envoy's --config=libc++ + re-add the libc++ include path (which bazel's cc_wrapper -nostdinc++'s out) + rpath the libc++ bottle so the produced binary still resolves libc++.so at runtime. Co-Authored-By: Claude Opus 4.7 --- projects/envoyproxy.io/package.yml | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 23569797be..2cce10450c 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -89,9 +89,19 @@ build: export BAZEL_LLVM_PATH="{{deps.llvm.org.prefix}}" echo "BAZEL_LLVM_PATH=$BAZEL_LLVM_PATH" + # The bazel-rules-cc llvm toolchain uses libstdc++ by default + # on Linux, but the pkgx llvm bottle ships only libc++ (no + # libstdc++ — that comes from gcc). Without an override, clang + # fails with "'string' file not found" on the first C++ source. + # + # Nix's nixpkgs envoy build sidesteps this by using gcc with + # its hermetic cc-wrapper, but envoy dropped gcc support in + # 1.21+. So we force libc++ explicitly via envoy's --config: + BAZEL_OPTS="--config=libc++" + # Memory-constrain bazel to fit on standard CI runners. # `--local_resources` units: cpu=count, ram=MB. - BAZEL_OPTS="-c opt" + BAZEL_OPTS="$BAZEL_OPTS -c opt" BAZEL_OPTS="$BAZEL_OPTS --jobs=HOST_CPUS*0.5" BAZEL_OPTS="$BAZEL_OPTS --local_ram_resources=HOST_RAM*0.7" # Tarball install has no .git, so envoy's workspace_status.sh @@ -99,6 +109,18 @@ build: # — we don't need stamping for a from-source release build. BAZEL_OPTS="$BAZEL_OPTS --workspace_status_command=true" + # libc++ headers live at {{deps.llvm.org.prefix}}/include/c++/v1. + # Clang's relative auto-detection from BAZEL_LLVM_PATH/bin/clang + # usually finds them, but bazel's cc_wrapper.sh adds -nostdinc++ + # by default with --config=libc++; we re-add the include path so + # , , etc. resolve. + BAZEL_OPTS="$BAZEL_OPTS --cxxopt=-isystem" + BAZEL_OPTS="$BAZEL_OPTS --cxxopt={{deps.llvm.org.prefix}}/include/c++/v1" + # And the matching rpath/linker hint so the produced binary + # finds libc++ at runtime against the same bottle. + BAZEL_OPTS="$BAZEL_OPTS --linkopt=-L{{deps.llvm.org.prefix}}/lib" + BAZEL_OPTS="$BAZEL_OPTS --linkopt=-Wl,-rpath,{{deps.llvm.org.prefix}}/lib" + # `envoy-static.stripped` is upstream's stripped release target. bazelisk build $BAZEL_OPTS //source/exe:envoy-static.stripped From b11b4787573eabf7d55896983a329fd74e93d5bf Mon Sep 17 00:00:00 2001 From: tannevaled Date: Fri, 29 May 2026 22:18:28 +0200 Subject: [PATCH 10/34] fix(envoy): joined -isystemPATH so bazel doesn't split it The previous form passed --cxxopt=-isystem and --cxxopt=PATH as two separate bazel flags, which bazel's build action shuffling consumed as two unrelated -isystem flags neither bound to a path. Use clang's joined `-isystem=PATH` style (`-isystemPATH` works too but the GNU-style equals is clearer). One token, one --cxxopt, one include path. Co-Authored-By: Claude Opus 4.7 --- projects/envoyproxy.io/package.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 2cce10450c..c7b53de5e6 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -113,9 +113,15 @@ build: # Clang's relative auto-detection from BAZEL_LLVM_PATH/bin/clang # usually finds them, but bazel's cc_wrapper.sh adds -nostdinc++ # by default with --config=libc++; we re-add the include path so - # , , etc. resolve. - BAZEL_OPTS="$BAZEL_OPTS --cxxopt=-isystem" - BAZEL_OPTS="$BAZEL_OPTS --cxxopt={{deps.llvm.org.prefix}}/include/c++/v1" + # , , , etc. resolve. + # + # bazel's --cxxopt=X passes a SINGLE token to clang; clang's + # -isystem expects its path as a separate argv element OR as + # -isystem=PATH joined-equals. Use the joined form here — the + # split-flag form (--cxxopt=-isystem followed by --cxxopt=PATH) + # ends up consumed as two unrelated -isystem flags by bazel's + # build action shuffling and the path doesn't bind. + BAZEL_OPTS="$BAZEL_OPTS --cxxopt=-isystem{{deps.llvm.org.prefix}}/include/c++/v1" # And the matching rpath/linker hint so the produced binary # finds libc++ at runtime against the same bottle. BAZEL_OPTS="$BAZEL_OPTS --linkopt=-L{{deps.llvm.org.prefix}}/lib" From eec90e235f72c813de1cb44204531ed2a9741e5d Mon Sep 17 00:00:00 2001 From: tannevaled Date: Fri, 29 May 2026 23:27:48 +0200 Subject: [PATCH 11/34] fix(envoy): add --host_cxxopt/--host_copt/--host_linkopt for libc++ The protobuf/upb code generators bazel builds for envoy run as "[for tool]" actions in the EXEC config, not the TARGET config. --cxxopt/--copt/--linkopt apply only to the target build, so host tools were compiling without libc++ on the include path and dying with "'string' file not found" before any target action ran. Mirror the libc++ include + rpath flags onto --host_* variants so both host tools and target binaries find libc++. Co-Authored-By: Claude Opus 4.7 --- projects/envoyproxy.io/package.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index c7b53de5e6..552780c7e3 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -121,11 +121,24 @@ build: # split-flag form (--cxxopt=-isystem followed by --cxxopt=PATH) # ends up consumed as two unrelated -isystem flags by bazel's # build action shuffling and the path doesn't bind. + # + # Critically, the protobuf/upb code generators are "[for tool]" + # actions that build under bazel's EXEC config, not the TARGET + # config. --cxxopt/--copt/--linkopt only apply to TARGET; we + # need the --host_* variants too, otherwise the host tools get + # built without libc++ on the include path and fail with + # "'string' file not found" before any target action runs. BAZEL_OPTS="$BAZEL_OPTS --cxxopt=-isystem{{deps.llvm.org.prefix}}/include/c++/v1" + BAZEL_OPTS="$BAZEL_OPTS --host_cxxopt=-isystem{{deps.llvm.org.prefix}}/include/c++/v1" + # Some bazel rules apply --copt to C++ compiles too; mirror. + BAZEL_OPTS="$BAZEL_OPTS --copt=-isystem{{deps.llvm.org.prefix}}/include/c++/v1" + BAZEL_OPTS="$BAZEL_OPTS --host_copt=-isystem{{deps.llvm.org.prefix}}/include/c++/v1" # And the matching rpath/linker hint so the produced binary # finds libc++ at runtime against the same bottle. BAZEL_OPTS="$BAZEL_OPTS --linkopt=-L{{deps.llvm.org.prefix}}/lib" BAZEL_OPTS="$BAZEL_OPTS --linkopt=-Wl,-rpath,{{deps.llvm.org.prefix}}/lib" + BAZEL_OPTS="$BAZEL_OPTS --host_linkopt=-L{{deps.llvm.org.prefix}}/lib" + BAZEL_OPTS="$BAZEL_OPTS --host_linkopt=-Wl,-rpath,{{deps.llvm.org.prefix}}/lib" # `envoy-static.stripped` is upstream's stripped release target. bazelisk build $BAZEL_OPTS //source/exe:envoy-static.stripped From 29e7fb723f94be81e25400834992ba0ab2dd9533 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Mon, 27 Jul 2026 15:30:50 +0200 Subject: [PATCH 12/34] fix(envoyproxy.io): get libc++ from libcxx.llvm.org, not llvm.org The llvm.org bottle ships clang but no libc++ (no include/c++/v1, no libc++.so), so `--config=libc++` failed with `'iostream' file not found` when building v8/torque. pantry already has a dedicated libcxx.llvm.org bottle that provides exactly that. Depend on it (build + runtime) and point the -isystem / -L / -Wl,-rpath flags (target AND host/exec configs) at {{deps.libcxx.llvm.org.prefix}} instead of the clang bottle. Co-Authored-By: Claude Opus 4.8 (1M context) --- projects/envoyproxy.io/package.yml | 48 ++++++++++++++++++------------ 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 552780c7e3..4c2e9c1f7f 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -20,11 +20,18 @@ platforms: - linux/x86-64 - linux/aarch64 +# The shipped envoy binary is linked against libc++ (see --config=libc++ +# in the build) with an rpath into the libcxx.llvm.org bottle, so it needs +# that bottle present at runtime. +dependencies: + libcxx.llvm.org: '*' + build: dependencies: github.com/bazelbuild/bazelisk: '*' # bazel wrapper / version manager github.com/mikefarah/yq: '*' # envoy's @yq external repo replacement llvm.org: '*' # local clang for BAZEL_LLVM_PATH + libcxx.llvm.org: '*' # libc++ (llvm.org ships clang only) gnu.org/coreutils: '*' # install(1) cmake.org: '*' # some bazel rules use cmake python.org: '~3.11' # bazel build rules use python @@ -109,11 +116,13 @@ build: # — we don't need stamping for a from-source release build. BAZEL_OPTS="$BAZEL_OPTS --workspace_status_command=true" - # libc++ headers live at {{deps.llvm.org.prefix}}/include/c++/v1. - # Clang's relative auto-detection from BAZEL_LLVM_PATH/bin/clang - # usually finds them, but bazel's cc_wrapper.sh adds -nostdinc++ - # by default with --config=libc++; we re-add the include path so - # , , , etc. resolve. + # libc++ comes from the dedicated libcxx.llvm.org bottle (the + # llvm.org bottle ships clang but no libc++). Headers at + # {{deps.libcxx.llvm.org.prefix}}/include/c++/v1, libs at + # {{deps.libcxx.llvm.org.prefix}}/lib. bazel's cc_wrapper.sh adds + # -nostdinc++ by default with --config=libc++, so we re-add the + # include path explicitly so , , , etc. + # resolve. # # bazel's --cxxopt=X passes a SINGLE token to clang; clang's # -isystem expects its path as a separate argv element OR as @@ -122,23 +131,24 @@ build: # ends up consumed as two unrelated -isystem flags by bazel's # build action shuffling and the path doesn't bind. # - # Critically, the protobuf/upb code generators are "[for tool]" - # actions that build under bazel's EXEC config, not the TARGET - # config. --cxxopt/--copt/--linkopt only apply to TARGET; we - # need the --host_* variants too, otherwise the host tools get - # built without libc++ on the include path and fail with - # "'string' file not found" before any target action runs. - BAZEL_OPTS="$BAZEL_OPTS --cxxopt=-isystem{{deps.llvm.org.prefix}}/include/c++/v1" - BAZEL_OPTS="$BAZEL_OPTS --host_cxxopt=-isystem{{deps.llvm.org.prefix}}/include/c++/v1" + # Critically, the protobuf/upb/v8-torque code generators are + # "[for tool]" actions that build under bazel's EXEC config, not + # the TARGET config. --cxxopt/--copt/--linkopt only apply to + # TARGET; we need the --host_* variants too, otherwise the host + # tools get built without libc++ on the include path and fail with + # "'iostream'/'string' file not found" before any target action runs. + LIBCXX="{{deps.libcxx.llvm.org.prefix}}" + BAZEL_OPTS="$BAZEL_OPTS --cxxopt=-isystem$LIBCXX/include/c++/v1" + BAZEL_OPTS="$BAZEL_OPTS --host_cxxopt=-isystem$LIBCXX/include/c++/v1" # Some bazel rules apply --copt to C++ compiles too; mirror. - BAZEL_OPTS="$BAZEL_OPTS --copt=-isystem{{deps.llvm.org.prefix}}/include/c++/v1" - BAZEL_OPTS="$BAZEL_OPTS --host_copt=-isystem{{deps.llvm.org.prefix}}/include/c++/v1" + BAZEL_OPTS="$BAZEL_OPTS --copt=-isystem$LIBCXX/include/c++/v1" + BAZEL_OPTS="$BAZEL_OPTS --host_copt=-isystem$LIBCXX/include/c++/v1" # And the matching rpath/linker hint so the produced binary # finds libc++ at runtime against the same bottle. - BAZEL_OPTS="$BAZEL_OPTS --linkopt=-L{{deps.llvm.org.prefix}}/lib" - BAZEL_OPTS="$BAZEL_OPTS --linkopt=-Wl,-rpath,{{deps.llvm.org.prefix}}/lib" - BAZEL_OPTS="$BAZEL_OPTS --host_linkopt=-L{{deps.llvm.org.prefix}}/lib" - BAZEL_OPTS="$BAZEL_OPTS --host_linkopt=-Wl,-rpath,{{deps.llvm.org.prefix}}/lib" + BAZEL_OPTS="$BAZEL_OPTS --linkopt=-L$LIBCXX/lib" + BAZEL_OPTS="$BAZEL_OPTS --linkopt=-Wl,-rpath,$LIBCXX/lib" + BAZEL_OPTS="$BAZEL_OPTS --host_linkopt=-L$LIBCXX/lib" + BAZEL_OPTS="$BAZEL_OPTS --host_linkopt=-Wl,-rpath,$LIBCXX/lib" # `envoy-static.stripped` is upstream's stripped release target. bazelisk build $BAZEL_OPTS //source/exe:envoy-static.stripped From 9b5ba91e6eea457c5c6eae57c16ccfb90219dcff Mon Sep 17 00:00:00 2001 From: tannevaled Date: Mon, 27 Jul 2026 15:37:40 +0200 Subject: [PATCH 13/34] fix(envoyproxy.io): make version-specific patches robust to 1.39+ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit envoy bumped to 1.39.0, which refactored bazel/repo.bzl so it no longer resolves @yq the old way — the yq patch's verification `grep` then found nothing and failed the build (exit 1) before anything compiled. Guard the yq patch on the pattern's presence (no-op on 1.39+), and make the dynamic_modules diagnostic grep non-fatal. These are diagnostics; they shouldn't gate the build across version bumps. Co-Authored-By: Claude Opus 4.8 (1M context) --- projects/envoyproxy.io/package.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 4c2e9c1f7f..65ee2fed1c 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -52,11 +52,15 @@ build: # Result: "execvp(.../external/yq/yq): No such file or directory". # # Patch repo.bzl to use the yq from PATH (pkgx-installed via the - # build dep above) instead of the http_archive. + # build dep above) instead of the http_archive. Only needed on envoy + # releases whose repo.bzl still resolves @yq this way (<=1.38.x); + # 1.39+ refactored it out, so guard on the pattern's presence. - run: | - sed -i 's|repository_ctx.path(repository_ctx.attr.yq)|repository_ctx.which("yq")|' \ - bazel/repo.bzl - grep -A1 'which("yq")' bazel/repo.bzl | head -5 + if grep -q 'repository_ctx.attr.yq' bazel/repo.bzl; then + sed -i 's|repository_ctx.path(repository_ctx.attr.yq)|repository_ctx.which("yq")|' \ + bazel/repo.bzl + grep -A1 'which("yq")' bazel/repo.bzl | head -5 + fi # envoy's `dynamic_modules.bzl:envoy_dynamic_module_prefix_symbols` # hardcodes `@llvm_toolchain_llvm//:objcopy` in the genrule that @@ -75,7 +79,7 @@ build: -e 's|$(location @llvm_toolchain_llvm//:objcopy)|{{deps.llvm.org.prefix}}/bin/llvm-objcopy|g' \ -e 's|tools = \["@llvm_toolchain_llvm//:objcopy"\],|tools = [],|g' \ source/extensions/dynamic_modules/dynamic_modules.bzl - grep -A 3 "llvm-objcopy" source/extensions/dynamic_modules/dynamic_modules.bzl | head -6 + grep -A 3 "llvm-objcopy" source/extensions/dynamic_modules/dynamic_modules.bzl | head -6 || true # Use the release config: -c opt + stripped + minimal symbols. # `bazelisk` here is the canonical entry point — it reads From f57177f7507d9fc5e67ac1dd9ab17f75e0d3970a Mon Sep 17 00:00:00 2001 From: tannevaled Date: Mon, 27 Jul 2026 15:49:15 +0200 Subject: [PATCH 14/34] chore(envoyproxy.io): --verbose_failures to diagnose layered build errors Co-Authored-By: Claude Opus 4.8 (1M context) --- projects/envoyproxy.io/package.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 65ee2fed1c..7d8b71dc7e 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -109,6 +109,9 @@ build: # its hermetic cc-wrapper, but envoy dropped gcc support in # 1.21+. So we force libc++ explicitly via envoy's --config: BAZEL_OPTS="--config=libc++" + # This is a huge, layered build; surface the failing compile + # command on error instead of just the target label. + BAZEL_OPTS="$BAZEL_OPTS --verbose_failures" # Memory-constrain bazel to fit on standard CI runners. # `--local_resources` units: cpu=count, ram=MB. From a9fb645e874e4dd0a185ebff711934e86e5a79b0 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Mon, 27 Jul 2026 16:05:06 +0200 Subject: [PATCH 15/34] fix(envoyproxy.io): run compiles locally so libc++ -isystem is reachable With libc++ now coming from the libcxx.llvm.org bottle, the build got past the "'iostream' file not found" wall (reaches ~6k actions), but bazel then rejects the absolute `-isystem /opt/libcxx.llvm.org/.../c++/v1` for the exec-config v8/torque "[for tool]" compiles: "include path references a path outside of the execution root". Run compile actions with --spawn_strategy=local so they can read /opt, keeping genrules sandboxed to avoid shared-scratch races (same approach as the cockroachdb recipe). Co-Authored-By: Claude Opus 4.8 (1M context) --- projects/envoyproxy.io/package.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 7d8b71dc7e..2f3d81d425 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -112,6 +112,14 @@ build: # This is a huge, layered build; surface the failing compile # command on error instead of just the target label. BAZEL_OPTS="$BAZEL_OPTS --verbose_failures" + # The libc++ headers live at an absolute path under /opt (the + # libcxx.llvm.org bottle), which bazel's sandbox rejects for the + # exec-config "[for tool]" compiles (v8/torque): "include path + # references a path outside of the execution root". Run compiles + # locally (unsandboxed) so the compiler can reach /opt, but keep + # genrules sandboxed to avoid shared-scratch races (same pattern + # as the cockroachdb recipe). + BAZEL_OPTS="$BAZEL_OPTS --spawn_strategy=local --strategy=Genrule=sandboxed" # Memory-constrain bazel to fit on standard CI runners. # `--local_resources` units: cpu=count, ram=MB. From f94483ab77e2b52cb47f3a7fb9a5fbf3446b1c69 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Mon, 27 Jul 2026 17:21:39 +0200 Subject: [PATCH 16/34] fix(envoyproxy.io): unified llvm+libc++ prefix instead of -isystem copts envoy's --config=libc++ does `-stdlib=libc++` + a static `-l%:libc++.a` link, so clang looks for libc++ relative to its LLVM install (BAZEL_LLVM_PATH). The pkgx llvm.org bottle has no libc++ (it's the separate libcxx.llvm.org bottle), and pointing at it with an explicit `-isystem /opt/libcxx.../c++/v1` copt tripped bazel's "include path references a path outside of the execution root" on the exec-config v8/torque/simdutf "[for tool]" compiles. Instead build a UNIFIED prefix: symlink the llvm.org tree and overlay libcxx.llvm.org's include/c++/v1 + static archives (libc++.a/libc++abi.a/ libunwind.a), and set BAZEL_LLVM_PATH to it. The toolchain then discovers libc++ as a builtin include/lib dir (bazel-accepted, no execroot error), and the -isystem/-L/-rpath copts are dropped. Static libc++ link means the binary needs no libc++.so at runtime. Co-Authored-By: Claude Opus 4.8 (1M context) --- projects/envoyproxy.io/package.yml | 67 +++++++++++++++--------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 2f3d81d425..1a826f99cc 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -97,7 +97,34 @@ build: # LLVM install when set. Without this, it falls back to the # @llvm_toolchain http_archive which fails in our sandbox # (Exit 127 when invoking external/llvm_toolchain/bin/clang). - export BAZEL_LLVM_PATH="{{deps.llvm.org.prefix}}" + # + # The pkgx llvm.org bottle ships clang but NOT libc++ (no + # include/c++/v1, no libc++.a) — that lives in the dedicated + # libcxx.llvm.org bottle. envoy builds with --config=libc++, i.e. + # `-stdlib=libc++` + a static `-l%:libc++.a` link, which makes clang + # look for libc++ RELATIVE to its LLVM install (BAZEL_LLVM_PATH). + # So present a UNIFIED prefix: symlink the llvm.org tree and overlay + # libcxx.llvm.org's headers (include/c++/v1) + static archives + # (libc++.a / libc++abi.a / libunwind.a) into it. The toolchain + # (toolchains_llvm, driven by BAZEL_LLVM_PATH) then discovers libc++ + # as a BUILTIN include/lib dir — so bazel accepts it (an explicit + # `-isystem /opt/...` copt instead trips "the include path references + # a path outside of the execution root" on the exec-config + # v8/torque/simdutf "[for tool]" compiles) and clang resolves + # // with no extra flags. + LLVM="{{deps.llvm.org.prefix}}" + LIBCXX="{{deps.libcxx.llvm.org.prefix}}" + COMBINED="$PWD/.llvm-libcxx" + rm -rf "$COMBINED"; mkdir -p "$COMBINED/include/c++" "$COMBINED/lib" + for d in "$LLVM"/*; do + b=$(basename "$d") + case "$b" in include|lib) ;; *) ln -sf "$d" "$COMBINED/$b" ;; esac + done + for d in "$LLVM"/include/*; do ln -sf "$d" "$COMBINED/include/$(basename "$d")"; done + ln -sf "$LIBCXX/include/c++/v1" "$COMBINED/include/c++/v1" + for d in "$LLVM"/lib/*; do ln -sf "$d" "$COMBINED/lib/$(basename "$d")"; done + for d in "$LIBCXX"/lib/libc++* "$LIBCXX"/lib/libunwind*; do ln -sf "$d" "$COMBINED/lib/$(basename "$d")"; done + export BAZEL_LLVM_PATH="$COMBINED" echo "BAZEL_LLVM_PATH=$BAZEL_LLVM_PATH" # The bazel-rules-cc llvm toolchain uses libstdc++ by default @@ -131,39 +158,11 @@ build: # — we don't need stamping for a from-source release build. BAZEL_OPTS="$BAZEL_OPTS --workspace_status_command=true" - # libc++ comes from the dedicated libcxx.llvm.org bottle (the - # llvm.org bottle ships clang but no libc++). Headers at - # {{deps.libcxx.llvm.org.prefix}}/include/c++/v1, libs at - # {{deps.libcxx.llvm.org.prefix}}/lib. bazel's cc_wrapper.sh adds - # -nostdinc++ by default with --config=libc++, so we re-add the - # include path explicitly so , , , etc. - # resolve. - # - # bazel's --cxxopt=X passes a SINGLE token to clang; clang's - # -isystem expects its path as a separate argv element OR as - # -isystem=PATH joined-equals. Use the joined form here — the - # split-flag form (--cxxopt=-isystem followed by --cxxopt=PATH) - # ends up consumed as two unrelated -isystem flags by bazel's - # build action shuffling and the path doesn't bind. - # - # Critically, the protobuf/upb/v8-torque code generators are - # "[for tool]" actions that build under bazel's EXEC config, not - # the TARGET config. --cxxopt/--copt/--linkopt only apply to - # TARGET; we need the --host_* variants too, otherwise the host - # tools get built without libc++ on the include path and fail with - # "'iostream'/'string' file not found" before any target action runs. - LIBCXX="{{deps.libcxx.llvm.org.prefix}}" - BAZEL_OPTS="$BAZEL_OPTS --cxxopt=-isystem$LIBCXX/include/c++/v1" - BAZEL_OPTS="$BAZEL_OPTS --host_cxxopt=-isystem$LIBCXX/include/c++/v1" - # Some bazel rules apply --copt to C++ compiles too; mirror. - BAZEL_OPTS="$BAZEL_OPTS --copt=-isystem$LIBCXX/include/c++/v1" - BAZEL_OPTS="$BAZEL_OPTS --host_copt=-isystem$LIBCXX/include/c++/v1" - # And the matching rpath/linker hint so the produced binary - # finds libc++ at runtime against the same bottle. - BAZEL_OPTS="$BAZEL_OPTS --linkopt=-L$LIBCXX/lib" - BAZEL_OPTS="$BAZEL_OPTS --linkopt=-Wl,-rpath,$LIBCXX/lib" - BAZEL_OPTS="$BAZEL_OPTS --host_linkopt=-L$LIBCXX/lib" - BAZEL_OPTS="$BAZEL_OPTS --host_linkopt=-Wl,-rpath,$LIBCXX/lib" + # NOTE: no explicit -isystem/-L for libc++ — the unified + # BAZEL_LLVM_PATH prefix built above lets the toolchain find it as a + # builtin. envoy's --config=libc++ links libc++/libc++abi STATICALLY + # (BAZEL_LINKLIBS=-l%:libc++.a), so the binary needs no libc++.so at + # runtime either. # `envoy-static.stripped` is upstream's stripped release target. bazelisk build $BAZEL_OPTS //source/exe:envoy-static.stripped From 5b9381ca898b2e2d76b92b7093ff72e664218989 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Mon, 27 Jul 2026 17:55:33 +0200 Subject: [PATCH 17/34] fix(envoyproxy.io): copy clang into unified prefix so it finds libc++ The unified BAZEL_LLVM_PATH prefix cleared the "outside execution root" wall (build now reaches abseil), but clang then failed with `'algorithm' file not found`: clang locates its libc++/resource dirs relative to its own binary via /proc/self/exe, and a SYMLINKED clang resolves back to the llvm.org bottle (no libc++ there). Copy the real clang driver into the combined bin/ (aliasing clang/clang++/clang-cpp to it) so its self-location is the unified prefix; it still resolves libLLVM/libclang-cpp through its $ORIGIN/../lib rpath into the symlinked lib/. Co-Authored-By: Claude Opus 4.8 (1M context) --- projects/envoyproxy.io/package.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 1a826f99cc..70b3b5bd66 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -115,15 +115,26 @@ build: LLVM="{{deps.llvm.org.prefix}}" LIBCXX="{{deps.libcxx.llvm.org.prefix}}" COMBINED="$PWD/.llvm-libcxx" - rm -rf "$COMBINED"; mkdir -p "$COMBINED/include/c++" "$COMBINED/lib" + rm -rf "$COMBINED"; mkdir -p "$COMBINED/include/c++" "$COMBINED/lib" "$COMBINED/bin" for d in "$LLVM"/*; do b=$(basename "$d") - case "$b" in include|lib) ;; *) ln -sf "$d" "$COMBINED/$b" ;; esac + case "$b" in bin|include|lib) ;; *) ln -sf "$d" "$COMBINED/$b" ;; esac done for d in "$LLVM"/include/*; do ln -sf "$d" "$COMBINED/include/$(basename "$d")"; done ln -sf "$LIBCXX/include/c++/v1" "$COMBINED/include/c++/v1" for d in "$LLVM"/lib/*; do ln -sf "$d" "$COMBINED/lib/$(basename "$d")"; done for d in "$LIBCXX"/lib/libc++* "$LIBCXX"/lib/libunwind*; do ln -sf "$d" "$COMBINED/lib/$(basename "$d")"; done + # bin: symlink the tools, BUT clang locates its libc++/resource dirs + # relative to its own binary via /proc/self/exe — a symlink resolves + # back to the llvm.org bottle (which has no libc++), so `` + # isn't found. COPY the real clang driver into the combined bin so + # its self-location is the unified prefix; it still finds libLLVM/ + # libclang-cpp via its $ORIGIN/../lib rpath (our symlinked lib/). + for f in "$LLVM"/bin/*; do ln -sf "$f" "$COMBINED/bin/$(basename "$f")"; done + realclang=$(readlink -f "$LLVM/bin/clang") + rc=$(basename "$realclang") + cp -f "$realclang" "$COMBINED/bin/$rc" + for a in clang clang++ clang-cpp; do ln -sf "$rc" "$COMBINED/bin/$a"; done export BAZEL_LLVM_PATH="$COMBINED" echo "BAZEL_LLVM_PATH=$BAZEL_LLVM_PATH" From cd34589b2b89079a2020069642311ab7f62a3a43 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Mon, 27 Jul 2026 18:01:38 +0200 Subject: [PATCH 18/34] fix(envoyproxy.io): rm the clang-22 symlink before copying the real driver The bin/* symlink loop already linked clang-22, so `cp` of the real clang-22 onto it failed ("are the same file") under set -e. Remove the symlink first, then copy. Co-Authored-By: Claude Opus 4.8 (1M context) --- projects/envoyproxy.io/package.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 70b3b5bd66..69d1f4b6e9 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -133,7 +133,8 @@ build: for f in "$LLVM"/bin/*; do ln -sf "$f" "$COMBINED/bin/$(basename "$f")"; done realclang=$(readlink -f "$LLVM/bin/clang") rc=$(basename "$realclang") - cp -f "$realclang" "$COMBINED/bin/$rc" + rm -f "$COMBINED/bin/$rc" # drop the symlink we just made (else cp = same file) + cp "$realclang" "$COMBINED/bin/$rc" for a in clang clang++ clang-cpp; do ln -sf "$rc" "$COMBINED/bin/$a"; done export BAZEL_LLVM_PATH="$COMBINED" echo "BAZEL_LLVM_PATH=$BAZEL_LLVM_PATH" From 3e936b68c7306605dcd762323212f0c98a53e271 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Mon, 27 Jul 2026 18:18:27 +0200 Subject: [PATCH 19/34] fix(envoyproxy.io): drop Genrule=sandboxed so v8's genrules can run MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the unified libc++ prefix, clang now finds libc++ and abseil compiles; the build then hit v8's generated_inspector_files / embedded.S genrules, which cannot run sandboxed — bazel aborts "Genrule spawn cannot be executed with any of the available strategies [processwrapper-sandbox]". Drop the `--strategy=Genrule=sandboxed` override (it was a cockroach-recipe pattern that doesn't apply here) and let genrules follow --spawn_strategy=local. Co-Authored-By: Claude Opus 4.8 (1M context) --- projects/envoyproxy.io/package.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 69d1f4b6e9..2996a349f1 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -151,14 +151,13 @@ build: # This is a huge, layered build; surface the failing compile # command on error instead of just the target label. BAZEL_OPTS="$BAZEL_OPTS --verbose_failures" - # The libc++ headers live at an absolute path under /opt (the - # libcxx.llvm.org bottle), which bazel's sandbox rejects for the - # exec-config "[for tool]" compiles (v8/torque): "include path - # references a path outside of the execution root". Run compiles - # locally (unsandboxed) so the compiler can reach /opt, but keep - # genrules sandboxed to avoid shared-scratch races (same pattern - # as the cockroachdb recipe). - BAZEL_OPTS="$BAZEL_OPTS --spawn_strategy=local --strategy=Genrule=sandboxed" + # Run actions locally (unsandboxed) so the compiler can reach the + # clang/libc++ toolchain under /opt (via the unified prefix above). + # NB: do NOT force `--strategy=Genrule=sandboxed` — v8's genrules + # (generated_inspector_files, embedded.S) can't run sandboxed and + # bazel aborts with "Genrule spawn cannot be executed with any of + # the available strategies". Let genrules follow --spawn_strategy=local. + BAZEL_OPTS="$BAZEL_OPTS --spawn_strategy=local" # Memory-constrain bazel to fit on standard CI runners. # `--local_resources` units: cpu=count, ram=MB. From 90a06d790331fc93bb195c695ee64de34acc4238 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Mon, 27 Jul 2026 19:30:56 +0200 Subject: [PATCH 20/34] fix(envoyproxy.io): use preinstalled make for rules_foreign_cc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rules_foreign_cc was building its own GNU make from source, and that ./configure link test fails under envoy's --config=libc++ (the static `-l%:libc++.a` linklibs get forced onto a plain-C configure link → "C compiler cannot create executables"). Add gnu.org/make as a build dep and register @rules_foreign_cc//toolchains:preinstalled_make_toolchain so foreign_cc uses the pkgx make on PATH instead of compiling one. Co-Authored-By: Claude Opus 4.8 (1M context) --- projects/envoyproxy.io/package.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 2996a349f1..0971bd54b5 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -33,6 +33,7 @@ build: llvm.org: '*' # local clang for BAZEL_LLVM_PATH libcxx.llvm.org: '*' # libc++ (llvm.org ships clang only) gnu.org/coreutils: '*' # install(1) + gnu.org/make: '*' # rules_foreign_cc make toolchain (else it builds make from source) cmake.org: '*' # some bazel rules use cmake python.org: '~3.11' # bazel build rules use python gnu.org/m4: '*' @@ -168,6 +169,12 @@ build: # fails at `git rev-parse`. Stub workspace status with `true` # — we don't need stamping for a from-source release build. BAZEL_OPTS="$BAZEL_OPTS --workspace_status_command=true" + # rules_foreign_cc otherwise builds its OWN GNU make from source, + # whose ./configure link test fails under envoy's --config=libc++ + # (static -l%:libc++.a linklibs forced onto a plain-C configure: + # "C compiler cannot create executables"). Register the preinstalled + # make toolchain so it uses the pkgx `make` on PATH instead. + BAZEL_OPTS="$BAZEL_OPTS --extra_toolchains=@rules_foreign_cc//toolchains:preinstalled_make_toolchain" # NOTE: no explicit -isystem/-L for libc++ — the unified # BAZEL_LLVM_PATH prefix built above lets the toolchain find it as a From f76741a4b20e5c36f77189caefe637c9de687dd1 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Mon, 27 Jul 2026 19:47:57 +0200 Subject: [PATCH 21/34] fix(envoyproxy.io): register all preinstalled foreign_cc tool toolchains Preinstalled make cleared the make-tool wall, but rules_foreign_cc then built pkgconfig from source and hit the same --config=libc++ configure-link failure. Register the preinstalled make/pkgconfig/cmake/ninja toolchains (and add ninja-build.org) so foreign_cc uses the pkgx tools on PATH rather than compiling each build tool from source. Co-Authored-By: Claude Opus 4.8 (1M context) --- projects/envoyproxy.io/package.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 0971bd54b5..d19fb54d1b 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -34,6 +34,7 @@ build: libcxx.llvm.org: '*' # libc++ (llvm.org ships clang only) gnu.org/coreutils: '*' # install(1) gnu.org/make: '*' # rules_foreign_cc make toolchain (else it builds make from source) + ninja-build.org: '*' # rules_foreign_cc ninja toolchain cmake.org: '*' # some bazel rules use cmake python.org: '~3.11' # bazel build rules use python gnu.org/m4: '*' @@ -169,12 +170,15 @@ build: # fails at `git rev-parse`. Stub workspace status with `true` # — we don't need stamping for a from-source release build. BAZEL_OPTS="$BAZEL_OPTS --workspace_status_command=true" - # rules_foreign_cc otherwise builds its OWN GNU make from source, - # whose ./configure link test fails under envoy's --config=libc++ - # (static -l%:libc++.a linklibs forced onto a plain-C configure: - # "C compiler cannot create executables"). Register the preinstalled - # make toolchain so it uses the pkgx `make` on PATH instead. - BAZEL_OPTS="$BAZEL_OPTS --extra_toolchains=@rules_foreign_cc//toolchains:preinstalled_make_toolchain" + # rules_foreign_cc otherwise builds its OWN build tools (make, + # pkgconfig, cmake, ninja) from source, and each ./configure link + # test fails under envoy's --config=libc++ (static -l%:libc++.a + # linklibs forced onto a plain-C configure: "C compiler cannot + # create executables"). Register the preinstalled toolchains so + # foreign_cc uses the pkgx tools on PATH instead of compiling them. + for tc in make pkgconfig cmake ninja; do + BAZEL_OPTS="$BAZEL_OPTS --extra_toolchains=@rules_foreign_cc//toolchains:preinstalled_${tc}_toolchain" + done # NOTE: no explicit -isystem/-L for libc++ — the unified # BAZEL_LLVM_PATH prefix built above lets the toolchain find it as a From 57ea3dd6e73dec163dc004d81d5c3aff4362eee3 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Mon, 27 Jul 2026 20:18:38 +0200 Subject: [PATCH 22/34] fix(envoyproxy.io): add -L to the unified prefix lib for static libc++ Past the foreign_cc tool walls, links failed with `ld.lld: unable to find library -l:libc++.a / libc++abi.a / libunwind.a`: envoy's --config=libc++ references the static archives but nothing points the linker at them. They're in the unified prefix's lib/, so add `--linkopt=-L$COMBINED/lib` (+ --host_linkopt for exec-config tool links like protobuf). -L is a library search path, not an -isystem include, so it doesn't trip the "outside the execution root" check. Co-Authored-By: Claude Opus 4.8 (1M context) --- projects/envoyproxy.io/package.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index d19fb54d1b..e42c9dfe43 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -179,6 +179,12 @@ build: for tc in make pkgconfig cmake ninja; do BAZEL_OPTS="$BAZEL_OPTS --extra_toolchains=@rules_foreign_cc//toolchains:preinstalled_${tc}_toolchain" done + # envoy's --config=libc++ links the static `-l:libc++.a`/`libc++abi.a`/ + # `libunwind.a`, but nothing tells the linker WHERE they are — they're + # in our unified prefix's lib/. Add the -L (target AND exec/host + # configs). This is a library search path, not an -isystem include, so + # it does NOT trip bazel's "outside the execution root" check. + BAZEL_OPTS="$BAZEL_OPTS --linkopt=-L$COMBINED/lib --host_linkopt=-L$COMBINED/lib" # NOTE: no explicit -isystem/-L for libc++ — the unified # BAZEL_LLVM_PATH prefix built above lets the toolchain find it as a From de5616c7e3994d2da5574edbdc677c44e3d68ff6 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Mon, 27 Jul 2026 21:36:43 +0200 Subject: [PATCH 23/34] fix(envoyproxy.io): forward PATH to actions so foreign_cc finds cmake/ninja Past the linker fix, foreign_cc deps started building (nghttp2 via CMake), but the build script hit "cmake: command not found": bazel actions don't inherit the shell PATH, only the runner's system PATH (which has make but not the pkgx cmake/ninja). Forward the full PATH via --action_env=PATH / --host_action_env=PATH. Co-Authored-By: Claude Opus 4.8 (1M context) --- projects/envoyproxy.io/package.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index e42c9dfe43..8177b81d33 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -185,6 +185,12 @@ build: # configs). This is a library search path, not an -isystem include, so # it does NOT trip bazel's "outside the execution root" check. BAZEL_OPTS="$BAZEL_OPTS --linkopt=-L$COMBINED/lib --host_linkopt=-L$COMBINED/lib" + # foreign_cc build scripts (nghttp2 etc.) invoke the preinstalled + # cmake/ninja/pkg-config by bare name, but bazel actions don't + # inherit the shell PATH — only the runner's system PATH, which has + # `make` but not the pkgx cmake/ninja → "cmake: command not found". + # Forward the full PATH into target and exec/host actions. + BAZEL_OPTS="$BAZEL_OPTS --action_env=PATH=$PATH --host_action_env=PATH=$PATH" # NOTE: no explicit -isystem/-L for libc++ — the unified # BAZEL_LLVM_PATH prefix built above lets the toolchain find it as a From b85ecc340b5a04f911bec3daa5050993abddb870 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Tue, 28 Jul 2026 10:11:38 +0200 Subject: [PATCH 24/34] fix(envoyproxy.io): forward LD_LIBRARY_PATH so pkgx cmake can load libcurl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With PATH forwarded, foreign_cc found cmake and nghttp2 built, but the maxmind CMake build then failed: "cmake: error while loading shared libraries: libcurl.so.4". The pkgx cmake dlopens libcurl (etc.) at runtime, which needs LD_LIBRARY_PATH — also not inherited by bazel actions. Forward it alongside PATH (target + exec/host). Co-Authored-By: Claude Opus 4.8 (1M context) --- projects/envoyproxy.io/package.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 8177b81d33..a95a880859 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -185,12 +185,15 @@ build: # configs). This is a library search path, not an -isystem include, so # it does NOT trip bazel's "outside the execution root" check. BAZEL_OPTS="$BAZEL_OPTS --linkopt=-L$COMBINED/lib --host_linkopt=-L$COMBINED/lib" - # foreign_cc build scripts (nghttp2 etc.) invoke the preinstalled - # cmake/ninja/pkg-config by bare name, but bazel actions don't - # inherit the shell PATH — only the runner's system PATH, which has - # `make` but not the pkgx cmake/ninja → "cmake: command not found". - # Forward the full PATH into target and exec/host actions. + # foreign_cc build scripts (nghttp2/maxmind/…) invoke the + # preinstalled cmake/ninja/pkg-config by bare name, but bazel + # actions don't inherit the shell PATH — only the runner's system + # PATH (has `make`, not the pkgx cmake/ninja) → "cmake: command not + # found". And the pkgx cmake itself dlopens libcurl.so.4 etc., so it + # needs LD_LIBRARY_PATH too (else "cmake: error while loading shared + # libraries: libcurl.so.4"). Forward both into target + exec/host. BAZEL_OPTS="$BAZEL_OPTS --action_env=PATH=$PATH --host_action_env=PATH=$PATH" + BAZEL_OPTS="$BAZEL_OPTS --action_env=LD_LIBRARY_PATH=$LD_LIBRARY_PATH --host_action_env=LD_LIBRARY_PATH=$LD_LIBRARY_PATH" # NOTE: no explicit -isystem/-L for libc++ — the unified # BAZEL_LLVM_PATH prefix built above lets the toolchain find it as a From be1a01669e753bc2899362792ca51c7c7dbd6135 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Tue, 28 Jul 2026 10:35:06 +0200 Subject: [PATCH 25/34] fix(envoyproxy.io): link foreign_cc host tools against the pkgx glibc bottle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit luajit builds a host tool `minilua` with the modern pkgx clang, which then fails to run on the older CI base image: "minilua: libm.so.6: version GLIBC_2.29 not found". Use pantry's relocatable glibc bottle the documented way (gnu.org/glibc/README.md smoke-test pattern): link the EXEC-config binaries with the bottle's own crt + libc + `--dynamic-linker=` + rpath, so foreign_cc host tools run against glibc 2.44 on any host. EXEC/host only (via --host_linkopt) — baking an absolute PT_INTERP into the target/final envoy binary would break bottle relocation; add gnu.org/glibc as a build dep. Co-Authored-By: Claude Opus 4.8 (1M context) --- projects/envoyproxy.io/package.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index a95a880859..7ddda1cc8a 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -35,6 +35,7 @@ build: gnu.org/coreutils: '*' # install(1) gnu.org/make: '*' # rules_foreign_cc make toolchain (else it builds make from source) ninja-build.org: '*' # rules_foreign_cc ninja toolchain + gnu.org/glibc: '*' # run foreign_cc host tools against a modern glibc (see below) cmake.org: '*' # some bazel rules use cmake python.org: '~3.11' # bazel build rules use python gnu.org/m4: '*' @@ -194,6 +195,19 @@ build: # libraries: libcurl.so.4"). Forward both into target + exec/host. BAZEL_OPTS="$BAZEL_OPTS --action_env=PATH=$PATH --host_action_env=PATH=$PATH" BAZEL_OPTS="$BAZEL_OPTS --action_env=LD_LIBRARY_PATH=$LD_LIBRARY_PATH --host_action_env=LD_LIBRARY_PATH=$LD_LIBRARY_PATH" + # Host tools that foreign_cc builds+runs DURING the build (luajit's + # `minilua`, etc.) are emitted by the modern pkgx clang and then need a + # newer glibc than the CI base image ships ("minilua: libm.so.6: + # version GLIBC_2.29 not found"). Link the EXEC-config binaries against + # the relocatable pkgx glibc bottle — its own crt + libc + ld-linux, the + # pattern documented in gnu.org/glibc/README.md — so they run against + # glibc 2.44 on any host. EXEC/host only: baking an absolute PT_INTERP + # into the *target* (final envoy) binary would break bottle relocation. + GLIBCLIB=$(dirname "$(ls "{{deps.gnu.org/glibc.prefix}}"/lib/glibc-*/libc.so.6 2>/dev/null | head -1)") + GLIBCLD=$(ls "$GLIBCLIB"/ld-linux*.so.* 2>/dev/null | head -1) + for f in "-B$GLIBCLIB" "-L$GLIBCLIB" "-Wl,--dynamic-linker=$GLIBCLD" "-Wl,-rpath,$GLIBCLIB"; do + BAZEL_OPTS="$BAZEL_OPTS --host_linkopt=$f" + done # NOTE: no explicit -isystem/-L for libc++ — the unified # BAZEL_LLVM_PATH prefix built above lets the toolchain find it as a From f7dc584e8dbbb6cc7a9abd4490422d1eb25750b4 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Tue, 28 Jul 2026 10:56:14 +0200 Subject: [PATCH 26/34] =?UTF-8?q?fix(envoyproxy.io):=20target=20pkgx=20gli?= =?UTF-8?q?bc=20via=20clang.cfg=20=E2=80=94=20no=20system=20libc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --host_linkopt didn't reach foreign_cc's luajit HOST_CC, so minilua still linked the CI base's old system libm and failed on GLIBC_2.29. Instead drop a `clang.cfg` next to the copied clang in the unified prefix (clang auto-loads it from its own bin dir) that points every invocation at the relocatable pkgx glibc bottle: `-B`/`-L` its crt+libs, `-Wl,--dynamic-linker=`, `-Wl,-rpath`. This reaches ALL clang links — bazel target/exec AND foreign_cc deps' own HOST_CC — with no wrapper script (clang's /proc/self/exe self-location for libc++ stays intact). Follows the gnu.org/glibc/README.md relocatable-bottle pattern. Add gnu.org/glibc as a runtime dep so the shipped binary (whose PT_INTERP is now the bottle's ld-linux) has it — a step toward running with no system libc at all (`FROM scratch`). NOTE: PT_INTERP is an absolute bottle path; full relocation still needs a load-time fixup (bklibcvenv), a follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) --- projects/envoyproxy.io/package.yml | 39 ++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 7ddda1cc8a..898500772b 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -25,6 +25,10 @@ platforms: # that bottle present at runtime. dependencies: libcxx.llvm.org: '*' + # The binary is linked against the pkgx glibc bottle (clang.cfg in the + # build), including its ld-linux as PT_INTERP — so it runs without a system + # libc (the `FROM scratch` goal). Ship that glibc alongside envoy. + gnu.org/glibc: '*' build: dependencies: @@ -139,6 +143,25 @@ build: rm -f "$COMBINED/bin/$rc" # drop the symlink we just made (else cp = same file) cp "$realclang" "$COMBINED/bin/$rc" for a in clang clang++ clang-cpp; do ln -sf "$rc" "$COMBINED/bin/$a"; done + # DON'T depend on the system libc. The modern pkgx clang emits binaries + # needing a newer glibc than an old CI base (or a `FROM scratch` image) + # provides — e.g. foreign_cc's luajit host tool: "minilua: libm.so.6: + # version GLIBC_2.29 not found". Make clang ALWAYS target the + # relocatable pkgx glibc bottle (its own crt + libc + ld-linux, the + # gnu.org/glibc/README.md pattern) via a clang config file that clang + # auto-loads from its own bin dir. This reaches EVERY clang invocation — + # bazel target/exec compiles AND foreign_cc deps' own HOST_CC (luajit) + # that plain --host_linkopt never touched — with no wrapper script (so + # clang's /proc/self/exe self-location for libc++ still works). + GLIBCLIB=$(dirname "$(ls "{{deps.gnu.org/glibc.prefix}}"/lib/glibc-*/libc.so.6 2>/dev/null | head -1)") + GLIBCLD=$(ls "$GLIBCLIB"/ld-linux*.so.* 2>/dev/null | head -1) + cat > "$COMBINED/bin/clang.cfg" </dev/null | head -1)") - GLIBCLD=$(ls "$GLIBCLIB"/ld-linux*.so.* 2>/dev/null | head -1) - for f in "-B$GLIBCLIB" "-L$GLIBCLIB" "-Wl,--dynamic-linker=$GLIBCLD" "-Wl,-rpath,$GLIBCLIB"; do - BAZEL_OPTS="$BAZEL_OPTS --host_linkopt=$f" - done + # (glibc targeting is handled by the clang.cfg written into the unified + # prefix above — it reaches every clang invocation, including + # foreign_cc's luajit HOST_CC that bazel --host_linkopt can't touch.) # NOTE: no explicit -isystem/-L for libc++ — the unified # BAZEL_LLVM_PATH prefix built above lets the toolchain find it as a From cf631a0534e476ccd7f3dcd17b6aacea74f8c6d3 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Tue, 28 Jul 2026 11:00:04 +0200 Subject: [PATCH 27/34] ci: re-trigger (transient deno.land 500 fetching libpkgx in the plan step) Co-Authored-By: Claude Opus 4.8 (1M context) From 33b3e40853114ebeff4821c7f85b745b5ef00501 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Tue, 28 Jul 2026 11:22:38 +0200 Subject: [PATCH 28/34] envoyproxy.io: set CMAKE_POLICY_VERSION_MINIMUM=3.5 for vendored libevent pkgx ships cmake 4.x, which removed compatibility with projects whose cmake_minimum_required is < 3.5. Envoy's vendored libevent still declares an ancient minimum, so its rules_foreign_cc CMake build failed with "Compatibility with CMake < 3.5 has been removed". Forward CMAKE_POLICY_VERSION_MINIMUM=3.5 into the bazel foreign_cc actions. The clang.cfg glibc-targeting approach now clears the full toolchain: minilua and ~6355 build actions (incl. nghttp2 via foreign_cc CMake) complete; this was the only remaining wall. Co-Authored-By: Claude Opus 4.8 --- projects/envoyproxy.io/package.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 898500772b..10c2b23a9a 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -218,6 +218,13 @@ build: # libraries: libcurl.so.4"). Forward both into target + exec/host. BAZEL_OPTS="$BAZEL_OPTS --action_env=PATH=$PATH --host_action_env=PATH=$PATH" BAZEL_OPTS="$BAZEL_OPTS --action_env=LD_LIBRARY_PATH=$LD_LIBRARY_PATH --host_action_env=LD_LIBRARY_PATH=$LD_LIBRARY_PATH" + # pkgx ships cmake 4.x, which dropped compatibility with projects whose + # `cmake_minimum_required` is < 3.5. Envoy vendors libevent, whose + # bundled CMakeLists still declares an ancient minimum → "Compatibility + # with CMake < 3.5 has been removed". CMAKE_POLICY_VERSION_MINIMUM=3.5 + # tells cmake to run those projects under 3.5 policies instead of + # erroring. Forward into the foreign_cc (exec-config) actions. + BAZEL_OPTS="$BAZEL_OPTS --action_env=CMAKE_POLICY_VERSION_MINIMUM=3.5 --host_action_env=CMAKE_POLICY_VERSION_MINIMUM=3.5" # (glibc targeting is handled by the clang.cfg written into the unified # prefix above — it reaches every clang invocation, including # foreign_cc's luajit HOST_CC that bazel --host_linkopt can't touch.) From 0495cc2c12ff4057d8aaf431c3dd97a0e94806ce Mon Sep 17 00:00:00 2001 From: tannevaled Date: Tue, 28 Jul 2026 11:39:25 +0200 Subject: [PATCH 29/34] envoyproxy.io: build Go codegen tools as pure Go (CGO off) After redirecting the toolchain onto the pkgx glibc 2.44 bottle, the Go host tools (protoc-gen-validate, protoc plugins) failed to link: ld.lld: error: undefined symbol: __res_search >>> did you mean: __res_search@GLIBC_2.17 Go's cgo net resolver references the unversioned __res_search, but glibc 2.34+ demoted it to a non-default compat symbol (the public default is now res_search@@GLIBC_2.34), so lld cannot bind the unversioned reference. These codegen tools need no cgo; --@io_bazel_rules_go//go/config:pure drops the glibc resolver dependency entirely. Follows the CMAKE_POLICY_VERSION_MINIMUM=3.5 fix (libevent) and the clang.cfg glibc targeting; minilua, libevent and nghttp2 now build. Co-Authored-By: Claude Opus 4.8 --- projects/envoyproxy.io/package.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 10c2b23a9a..a00acc0ac3 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -194,6 +194,15 @@ build: # fails at `git rev-parse`. Stub workspace status with `true` # — we don't need stamping for a from-source release build. BAZEL_OPTS="$BAZEL_OPTS --workspace_status_command=true" + # Build all Go codegen tools (protoc-gen-validate, protoc plugins…) + # as pure Go — CGO off. With cgo, Go's net resolver references the + # unversioned `__res_search`, but pkgx glibc 2.44 demoted it to a + # non-default compat symbol (the default is now `res_search@@GLIBC_2.34`), + # so lld fails: "undefined symbol: __res_search / did you mean + # __res_search@GLIBC_2.17". These tools need no cgo; pure Go drops the + # glibc resolver dependency entirely. (Starlark build setting → applies + # to exec-config tool builds too.) + BAZEL_OPTS="$BAZEL_OPTS --@io_bazel_rules_go//go/config:pure" # rules_foreign_cc otherwise builds its OWN build tools (make, # pkgconfig, cmake, ninja) from source, and each ./configure link # test fails under envoy's --config=libc++ (static -l%:libc++.a From f7fdb87a6e9b98e7a4df538f6bfb1f43705ab719 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Tue, 28 Jul 2026 11:54:24 +0200 Subject: [PATCH 30/34] envoyproxy.io: supply libgcc_s/libatomic for glibc-redirected host tools Once clang.cfg points host tools at the pkgx glibc bottle, the pkgx ld-linux becomes their PT_INTERP and it does not fall back to the system /lib. A freshly built host tool (rules_rust's process_wrapper) then died: process_wrapper: error while loading shared libraries: libgcc_s.so.1: cannot open shared object file The whole libc closure must come from pkgx. gnu.org/gcc/libstdcxx ships libgcc_s.so.1 and gnu.org/gcc ships libatomic.so.1, both in lib/, which pkgx adds to the build LD_LIBRARY_PATH forwarded into the bazel actions. Follows the pure-Go (__res_search) and CMAKE_POLICY_VERSION_MINIMUM=3.5 (libevent) fixes; minilua, libevent, nghttp2 and the Go tools now build. Co-Authored-By: Claude Opus 4.8 --- projects/envoyproxy.io/package.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index a00acc0ac3..2734c22efb 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -40,6 +40,15 @@ build: gnu.org/make: '*' # rules_foreign_cc make toolchain (else it builds make from source) ninja-build.org: '*' # rules_foreign_cc ninja toolchain gnu.org/glibc: '*' # run foreign_cc host tools against a modern glibc (see below) + # Once clang.cfg redirects host tools onto the pkgx glibc bottle, the + # pkgx ld-linux is their PT_INTERP — and it does NOT fall back to the + # system /lib. So the whole libc closure must come from pkgx too, else a + # freshly-built host tool (e.g. rules_rust's process_wrapper) dies with + # "libgcc_s.so.1: cannot open shared object file". These bottles put + # libgcc_s.so.1 + libatomic.so.1 in lib/, which pkgx adds to the build + # LD_LIBRARY_PATH that we forward into the bazel actions. + gnu.org/gcc/libstdcxx: '*' # libgcc_s.so.1 (host-tool unwinding) + gnu.org/gcc: '*' # libatomic.so.1 (pulled by libc++) cmake.org: '*' # some bazel rules use cmake python.org: '~3.11' # bazel build rules use python gnu.org/m4: '*' From 6a0488b6d3975ad63c93aac85dc1f368cb3d93e3 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Tue, 28 Jul 2026 16:10:25 +0200 Subject: [PATCH 31/34] envoyproxy.io: drop pkgx glibc dir from forwarded LD_LIBRARY_PATH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rules_rust proc-macro build failed with E0463 (can't find crate for thiserror_impl): rustc loads the proc-macro dylib, whose NEEDED libc.so.6 was resolving to the pkgx glibc 2.44 on our forwarded LD_LIBRARY_PATH instead of the libc the rust toolchain expects, so the dylib failed to load. Keep forwarding LD_LIBRARY_PATH for the pkgx tool libs (cmake→libcurl, process_wrapper→libgcc_s) but filter out the gnu.org/glibc lib dir: the pkgx loader still finds libc next to itself by default, so host tools are unaffected, while rust proc-macro dylibs get the correct libc. Co-Authored-By: Claude Opus 4.8 --- projects/envoyproxy.io/package.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 2734c22efb..d9bb400330 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -235,7 +235,15 @@ build: # needs LD_LIBRARY_PATH too (else "cmake: error while loading shared # libraries: libcurl.so.4"). Forward both into target + exec/host. BAZEL_OPTS="$BAZEL_OPTS --action_env=PATH=$PATH --host_action_env=PATH=$PATH" - BAZEL_OPTS="$BAZEL_OPTS --action_env=LD_LIBRARY_PATH=$LD_LIBRARY_PATH --host_action_env=LD_LIBRARY_PATH=$LD_LIBRARY_PATH" + # Forward LD_LIBRARY_PATH for the pkgx tool libs (cmake→libcurl, + # process_wrapper→libgcc_s), but DROP the pkgx glibc dir from it: rustc + # loads Rust proc-macro dylibs (thiserror_impl.so, …) whose NEEDED + # libc.so.6 would otherwise resolve to the pkgx glibc 2.44 instead of + # the libc the rust toolchain was built against, mismatching and failing + # with E0463 "can't find crate". The pkgx loader still finds libc next + # to itself by default, so host tools are unaffected. + LD_FWD=$(printf '%s' "$LD_LIBRARY_PATH" | tr ':' '\n' | grep -v '/gnu\.org/glibc/' | paste -sd: -) + BAZEL_OPTS="$BAZEL_OPTS --action_env=LD_LIBRARY_PATH=$LD_FWD --host_action_env=LD_LIBRARY_PATH=$LD_FWD" # pkgx ships cmake 4.x, which dropped compatibility with projects whose # `cmake_minimum_required` is < 3.5. Envoy vendors libevent, whose # bundled CMakeLists still declares an ancient minimum → "Compatibility From 3b88e7dff20e05e84550bbf664f93fcdc2a1f726 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Tue, 28 Jul 2026 17:48:11 +0200 Subject: [PATCH 32/34] envoyproxy.io: link rust via cc_common so proc-macros resolve by rpath rules_rust proc-macro dylibs failed to load (E0463) because our forwarded LD_LIBRARY_PATH (needed for process_wrapper's pkgx libgcc_s) shadows the rust libstd path. rules_rust already computes RPATHs; --@rules_rust//rust/settings: experimental_use_cc_common_link=1 routes rust binary linking through bazel's cc_common (our clang toolchain), baking RPATHs into process_wrapper and the proc-macro dylibs so libstd/libgcc_s resolve by rpath regardless of LD_LIBRARY_PATH. Co-Authored-By: Claude Opus 4.8 --- projects/envoyproxy.io/package.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index d9bb400330..11b4f47bcd 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -251,6 +251,14 @@ build: # tells cmake to run those projects under 3.5 policies instead of # erroring. Forward into the foreign_cc (exec-config) actions. BAZEL_OPTS="$BAZEL_OPTS --action_env=CMAKE_POLICY_VERSION_MINIMUM=3.5 --host_action_env=CMAKE_POLICY_VERSION_MINIMUM=3.5" + # rules_rust proc-macro dylibs (thiserror_impl.so, …) failed to load into + # rustc (E0463) because our forwarded LD_LIBRARY_PATH — required so + # process_wrapper finds pkgx libgcc_s — shadows the rust libstd path. + # Link rust binaries via bazel's cc_common (our clang toolchain) instead + # of rustc's own linker: this bakes proper RPATHs into process_wrapper + # and the proc-macro dylibs, so their libstd/libgcc_s resolve by rpath + # regardless of LD_LIBRARY_PATH. + BAZEL_OPTS="$BAZEL_OPTS --@rules_rust//rust/settings:experimental_use_cc_common_link=1" # (glibc targeting is handled by the clang.cfg written into the unified # prefix above — it reaches every clang invocation, including # foreign_cc's luajit HOST_CC that bazel --host_linkopt can't touch.) From 18c4817e4be5abe1d5d625445d6dc201c3a7a372 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Tue, 28 Jul 2026 19:09:54 +0200 Subject: [PATCH 33/34] envoyproxy.io: use the pkgx rust 1.88.0 bottle as the rust toolchain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root fix for the rust proc-macro E0463 (and it avoids the cc_common_link allocator-ABI wall): envoy pins rust 1.88.0 and rules_rust downloads it, but that toolchain's libstd lives under bazel-out — unreachable from the LD_LIBRARY_PATH we must forward for process_wrapper's libgcc_s — so rustc can't load proc-macro dylibs. pkgx has rust 1.88.0 exactly; use its bottle as the toolchain (register a rust_toolchain over it in the WORKSPACE, prefer it via --extra_toolchains) and add its lib/rustlib//lib to the forwarded LD_LIBRARY_PATH. Then one path satisfies both proc-macro libstd and process_wrapper libgcc_s, with no version drift (1.88.0 == envoy's pin) and no cc_common_link. Reverts the cc_common_link flag. Co-Authored-By: Claude Opus 4.8 --- projects/envoyproxy.io/package.yml | 71 ++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index 11b4f47bcd..abd088c8cc 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -49,6 +49,13 @@ build: # LD_LIBRARY_PATH that we forward into the bazel actions. gnu.org/gcc/libstdcxx: '*' # libgcc_s.so.1 (host-tool unwinding) gnu.org/gcc: '*' # libatomic.so.1 (pulled by libc++) + # Envoy's WORKSPACE pins rust 1.88.0 and rules_rust auto-DOWNLOADS it; but + # that toolchain's libstd lives under bazel-out, unreachable from the + # LD_LIBRARY_PATH we must forward (for process_wrapper's libgcc_s), so + # rustc can't load proc-macro dylibs (E0463). Use the pkgx rust 1.88.0 + # bottle as the toolchain instead: its libstd is a bottle we CAN put on + # LD_LIBRARY_PATH, so proc-macros AND process_wrapper resolve from one path. + rust-lang.org: '~1.88' # == 1.88.0, matches envoy's pin exactly cmake.org: '*' # some bazel rules use cmake python.org: '~3.11' # bazel build rules use python gnu.org/m4: '*' @@ -243,6 +250,15 @@ build: # with E0463 "can't find crate". The pkgx loader still finds libc next # to itself by default, so host tools are unaffected. LD_FWD=$(printf '%s' "$LD_LIBRARY_PATH" | tr ':' '\n' | grep -v '/gnu\.org/glibc/' | paste -sd: -) + # Add the pkgx rust bottle's std dir (lib/rustlib//lib holds + # libstd-*.so). rust's plain lib/ (librustc_driver.so) is already on + # $LD_LIBRARY_PATH via the dep, but libstd lives in this sub-libdir, so + # rustc — and the proc-macro dylibs it loads — resolve it from here + # instead of an unreachable bazel-out path. This is what lets us keep + # forwarding LD_LIBRARY_PATH (for process_wrapper's libgcc_s) without + # the E0463 proc-macro failure: one path satisfies both. + RUST_STD_DIR=$(dirname "$(ls "{{deps.rust-lang.org.prefix}}"/lib/rustlib/*/lib/libstd-*.so 2>/dev/null | head -1)") + [ -n "$RUST_STD_DIR" ] && LD_FWD="$LD_FWD:$RUST_STD_DIR" BAZEL_OPTS="$BAZEL_OPTS --action_env=LD_LIBRARY_PATH=$LD_FWD --host_action_env=LD_LIBRARY_PATH=$LD_FWD" # pkgx ships cmake 4.x, which dropped compatibility with projects whose # `cmake_minimum_required` is < 3.5. Envoy vendors libevent, whose @@ -251,14 +267,53 @@ build: # tells cmake to run those projects under 3.5 policies instead of # erroring. Forward into the foreign_cc (exec-config) actions. BAZEL_OPTS="$BAZEL_OPTS --action_env=CMAKE_POLICY_VERSION_MINIMUM=3.5 --host_action_env=CMAKE_POLICY_VERSION_MINIMUM=3.5" - # rules_rust proc-macro dylibs (thiserror_impl.so, …) failed to load into - # rustc (E0463) because our forwarded LD_LIBRARY_PATH — required so - # process_wrapper finds pkgx libgcc_s — shadows the rust libstd path. - # Link rust binaries via bazel's cc_common (our clang toolchain) instead - # of rustc's own linker: this bakes proper RPATHs into process_wrapper - # and the proc-macro dylibs, so their libstd/libgcc_s resolve by rpath - # regardless of LD_LIBRARY_PATH. - BAZEL_OPTS="$BAZEL_OPTS --@rules_rust//rust/settings:experimental_use_cc_common_link=1" + # Use the pkgx rust 1.88.0 bottle as the rust toolchain (matching envoy's + # pin) instead of the one rules_rust downloads. Its libstd is the bottle + # dir added to LD_FWD above, so proc-macros load fine; envoy builds + # WORKSPACE-style (--noenable_bzlmod), so register a rust_toolchain over + # the pkgx bottle and prefer it via --extra_toolchains. + case "$(uname -m)" in + aarch64|arm64) RTRIPLE=aarch64-unknown-linux-gnu; RCPU=aarch64 ;; + x86_64) RTRIPLE=x86_64-unknown-linux-gnu; RCPU=x86_64 ;; + esac + cat >> WORKSPACE < Date: Tue, 28 Jul 2026 19:26:25 +0200 Subject: [PATCH 34/34] envoyproxy.io: harden pkgx-rust LD_FWD (explicit rust lib dirs) Forward BOTH pkgx rust lib dirs to bazel's env-cleared actions instead of assuming the dep seeds rust/lib onto LD_LIBRARY_PATH: lib/ -> librustc_driver-*.so (rustc NEEDED, no RUNPATH) lib/rustlib//lib -> libstd-*.so (rustc + the proc-macro dylibs it loads) Validated end-to-end on real x86_64 hardware (cfarm) with rules_rust: a proc_macro crate + consumer compiles and runs under this exact rust_toolchain + --extra_toolchains + --action_env=LD_LIBRARY_PATH config, reproducing and clearing the E0463 'can't find crate for proc_macro' wall outside CI. Co-Authored-By: Claude Opus 4.8 --- projects/envoyproxy.io/package.yml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml index abd088c8cc..8d8ab9cdf2 100644 --- a/projects/envoyproxy.io/package.yml +++ b/projects/envoyproxy.io/package.yml @@ -250,15 +250,20 @@ build: # with E0463 "can't find crate". The pkgx loader still finds libc next # to itself by default, so host tools are unaffected. LD_FWD=$(printf '%s' "$LD_LIBRARY_PATH" | tr ':' '\n' | grep -v '/gnu\.org/glibc/' | paste -sd: -) - # Add the pkgx rust bottle's std dir (lib/rustlib//lib holds - # libstd-*.so). rust's plain lib/ (librustc_driver.so) is already on - # $LD_LIBRARY_PATH via the dep, but libstd lives in this sub-libdir, so - # rustc — and the proc-macro dylibs it loads — resolve it from here - # instead of an unreachable bazel-out path. This is what lets us keep - # forwarding LD_LIBRARY_PATH (for process_wrapper's libgcc_s) without - # the E0463 proc-macro failure: one path satisfies both. + # Explicitly add the pkgx rust bottle's two lib dirs so bazel's + # env-cleared (`env -`) actions can resolve rustc's own libraries: + # lib/ -> librustc_driver-*.so (rustc NEEDED, no RUNPATH) + # lib/rustlib//lib -> libstd-*.so (loaded by rustc AND by the + # proc-macro dylibs it dlopens at compile time) + # Forwarding both is what clears the E0463 "can't find crate for + # proc_macro" wall while still giving process_wrapper its libgcc_s. + # Don't rely on the dep implicitly seeding rust/lib onto + # $LD_LIBRARY_PATH — add it outright. Validated on real x86_64 HW + # (cfarm): a proc_macro crate + consumer compiles and runs under this + # exact toolchain + LD config with rules_rust. + RUST_LIB="{{deps.rust-lang.org.prefix}}/lib" RUST_STD_DIR=$(dirname "$(ls "{{deps.rust-lang.org.prefix}}"/lib/rustlib/*/lib/libstd-*.so 2>/dev/null | head -1)") - [ -n "$RUST_STD_DIR" ] && LD_FWD="$LD_FWD:$RUST_STD_DIR" + LD_FWD="$LD_FWD:$RUST_LIB${RUST_STD_DIR:+:$RUST_STD_DIR}" BAZEL_OPTS="$BAZEL_OPTS --action_env=LD_LIBRARY_PATH=$LD_FWD --host_action_env=LD_LIBRARY_PATH=$LD_FWD" # pkgx ships cmake 4.x, which dropped compatibility with projects whose # `cmake_minimum_required` is < 3.5. Envoy vendors libevent, whose