diff --git a/projects/envoyproxy.io/package.yml b/projects/envoyproxy.io/package.yml new file mode 100644 index 0000000000..2778da28ac --- /dev/null +++ b/projects/envoyproxy.io/package.yml @@ -0,0 +1,387 @@ +# 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 + +# 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: '*' + # 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: + 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) + 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++) + # 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: '*' + gnu.org/automake: '*' + gnu.org/autoconf: '*' + gnu.org/libtool: '*' + gnu.org/patch: '*' + freedesktop.org/pkg-config: '*' + 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. 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: | + 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 + # 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 || true + + # 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 + + # 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). + # + # 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" "$COMBINED/bin" + for d in "$LLVM"/*; do + b=$(basename "$d") + 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") + 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" < 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)") + 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 + # 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" + # 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 + # ROOT FIX for the proc-macro E0463 wall: clang.cfg (above) links every + # exec-config output — including the proc-macro dylibs rustc dlopens at + # compile time — against the pkgx glibc 2.44 bottle. But the pkgx rustc, + # left alone, runs on its standard PT_INTERP = the OLD CI-base glibc, so + # when it dlopens a proc-macro that references GLIBC_2.29+ symbols the + # already-loaded old libc can't satisfy them → dlopen fails → E0463 + # "can't find crate for ". Run rustc THROUGH the same pkgx + # glibc 2.44 loader (glibc is backward-compatible, so rustc — which needs + # only old symbols — runs fine on it), so rustc and the proc-macros it + # loads share one modern libc and the dlopen succeeds. Proven on real HW + # (cfarm13): rules_rust happily drives a wrapper `rustc` that re-execs the + # real rustc via `ld-linux --library-path …`, and a proc_macro crate + + # consumer then compile and run. GLIBCLIB/GLIBCLD were computed above for + # clang.cfg; reuse them. LD_FWD carries librustc_driver/libstd/libgcc_s. + # Keep this OUTSIDE the bazel workspace — new_local_repository rejects a + # path that overlaps the main repo. + RUSTWRAP="${TMPDIR:-/tmp}/pkgxrust-envoy" + mkdir -p "$RUSTWRAP" + cat > "$RUSTWRAP/rustc.sh" <> WORKSPACE <////<...>". + # 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