new(envoyproxy.io): Envoy — high-performance edge/service proxy (Bazel build from source) - #13046
new(envoyproxy.io): Envoy — high-performance edge/service proxy (Bazel build from source)#13046tannevaled wants to merge 35 commits into
Conversation
63141f0 to
a8d9ca4
Compare
|
Blocker: bazel can't fetch Latest run fails at the fetch phase, before any actual compilation: Envoy's bazel rules pull yq from Options to unblock, in increasing complexity:
I'll move this PR to draft until we have one of those in place. The from-source pattern is correct but envoy's bazel chain has too many fetch-from-network steps for the current runner setup. Open to suggestions from anyone who's wrangled envoy CI elsewhere. |
|
Took option 1 — patched The http_archive download was technically working but bazel placed the artifact at Added |
|
Yq patch + workspace_status stub both worked — bazel got 5211 compile actions deep before failing. Current blocker: envoy uses bazel's
This needs a bigger patch than #13046 currently has:
Both options are non-trivial. Re-marking as draft until I have a cleaner approach. Open to suggestions if anyone has wrangled this. |
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 (pkgxdev#13046's prior commit) demonstrated: where envoy supports it, use an env-var hook rather than patching the bazel files.
|
Update — envoy's `bazel/repo.bzl` reads `BAZEL_LLVM_PATH` and uses a local LLVM install when set (I found it on re-reading the file): llvm_path = repository_ctx.os.environ.get("BAZEL_LLVM_PATH", "")
local_llvm = "True" if llvm_path else "False"Added `llvm.org` as a build dep and `export BAZEL_LLVM_PATH={{deps.llvm.org.prefix}}` before invoking bazelisk. No file patching needed — envoy provides this env-var hook on purpose for exactly this case. This keeps the from-source promise intact: every binary tool in the toolchain now comes from pantry (bazelisk, llvm.org clang, yq), no http_archive fetches of opaque binaries from random GitHub releases. Marking ready for review. |
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.
…'t a config)
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.
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.
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.
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 (pkgxdev#13046's prior commit) demonstrated: where envoy supports it, use an env-var hook rather than patching the bazel files.
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.
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 <noreply@anthropic.com>
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 <string> 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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
eb4027c to
eec90e2
Compare
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
…rors Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
…river
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
|
Status update — got the from-source build to ~34% (6382/18507 actions) on linux by clearing several toolchain walls (all in the latest commits). The root difficulty: pkgx ships clang ( Walls cleared so far:
Current wall: |
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
…/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) <noreply@anthropic.com>
…bcurl 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) <noreply@anthropic.com>
|
Progress update — got much further (≈6350/18501 actions; the foreign_cc deps nghttp2 and maxmind now build). Additional walls cleared beyond the previous comment:
Current wall (infrastructure-level, pausing here): |
… bottle 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=<bottle ld-linux>` + 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) <noreply@anthropic.com>
--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=<bottle ld-linux>`, `-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) <noreply@anthropic.com>
…step) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…vent 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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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/<triple>/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 <noreply@anthropic.com>
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/<t>/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 <noreply@anthropic.com>
…roc-macro E0463) Root cause of the remaining rules_rust E0463 (confirmed from the nix64 CI log): a glibc-version SKEW. envoy's clang.cfg links every exec-config output — including the proc-macro dylibs rustc dlopens at compile time — against the pkgx glibc 2.44 bottle (needed so foreign_cc host tools get GLIBC_2.29+ on the old CI base). But the pkgx rustc ran on its standard PT_INTERP = the old base glibc, so dlopen'ing a proc-macro that references GLIBC_2.29+ symbols failed against the loaded old libc -> E0463 'can't find crate for <proc-macro>'. Fix: drive rustc through a wrapper that re-execs the real rustc via the pkgx glibc 2.44 loader (ld-linux --library-path). glibc is backward-compatible, so rustc (needs only old symbols) runs fine on 2.44, and it now shares one modern libc with the proc-macros it loads -> dlopen succeeds. rustc comes from a tiny second new_local_repository holding the wrapper (pkgx_rust stays read-only over the bottle; the wrapper filegroup is named distinctly from its rustc.sh source to avoid a self-edge cycle). Validated end-to-end on real x86_64 hardware (cfarm13): rules_rust drives a wrapper rustc that re-execs via an explicit loader, and a proc_macro crate + consumer then compile and run. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
From-source Bazel build of envoy. Heavy CI (1-3h, ~8GB RAM), with a vendored-binary fallback commented at the bottom if CI can't sustain it.
Closes a mainstream coverage gap (was missing vs brew + nixpkgs).