# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 # NOTE: This package uses a pre-built crate tarball instead of listing individual # crates in CRATES="". This approach is required because: # 1. Codex has 800+ crate dependencies, which triggers Portage QA warnings: # "QA Notice: This package uses a very large number of CRATES. Please provide # a crate tarball instead and fetch it via SRC_URI." # 2. Stricter ebuild QA checks (e.g., in CI pipelines) fail with too many crates. # # The crate tarball is auto-generated by GitHub Actions at: # https://github.com/gentoo-zh-drafts/codex/ # Workflow source code for auditing: # https://github.com/gentoo-zh-drafts/codex/blob/crate-dist/.github/workflows/crates.yml # This allows anyone to review and verify the crate packaging process. # bentoo: openssl-sys below 0.9.115 aborts its build script against # dev-libs/openssl-4 ("only compatible with OpenSSL ... 3"), and both 0.166.0 # and 0.167.0 lock 0.9.111. Raising openssl-sys alone is not enough: the # openssl crate the lock pairs with it (0.10.75) then fails E0308 on the # const pointers OpenSSL 4's X509 getters return, so both move, to the pair # released together. 0.9.118 also fixes SSL_OP_ALL and SSL_OP_TLSEXT_PADDING # on OpenSSL 4.1, and openssl 0.10.82 fixes out-of-bounds writes and a # use-after-free. src_prepare points the lock at them; drop both entries # once upstream's lock reaches them (the guard there stops acting then). CRATES=" openssl-sys@0.9.118 openssl@0.10.82 " # Mirrors every "source = git+..." entry in codex-rs/Cargo.lock. Diff this block # against that file on every bump; an entry the lock no longer references only # costs a pointless fetch, but a stale rev is silent here. src_prepare rewrites # [patch.crates-io] as "path = ", and a path carries no revision, # so cargo never reports "can't checkout #" -- it just compiles the # previous release's source. That surfaces much later as unrelated-looking # errors in a consumer crate: 0.148.0 with the 0.147.0 crossterm failed with # E0425/E0433 in codex-tui for discard_buffered_input, buffer_input and # InputDiscardStatus, all added by the fork after f69a4a04. # Upstream moved crossterm from nornagon to openai-oss-forks in 0.147.0 and # dropped its ratatui fork back to crates.io. # # A git repository holding several workspace members gets one entry per member # the lock references, all pointing at the same commit. That is not redundancy: # the eclass emits one " = { path = ... }" line per key under a single # [patch.''] block, and a member with no key is left resolving to the git # source, which dies under --offline with "can't checkout ". SRC_URI and # DISTDIR see the same tarball several times and deduplicate it. That covers # nucleo/nucleo-matcher, hyperium/h3 (h3 + h3-quinn, since 0.156.0) and # modelcontextprotocol/rust-sdk (rmcp + rmcp-macros, since 0.161.0). # # 0.166.0 moved v8 under [patch.crates-io] to a fork (seanh-oai/rusty_v8) # whose only change is src/cppgc.rs, so the prebuilt RUSTY_V8_TAG archive and # its binding still match. The GitHub tarball lacks the V8 submodules; that is # fine because build.rs never reads them when RUSTY_V8_ARCHIVE is set. Without # this entry cargo looks for v8 in the crates tarball and dies with "no # matching package named `v8` found". # # microsoft/mxc held eight such members up to 0.163; 0.164.0 replaced all of # them with a single mxc-sdk crate (src/mxc-sdk), reached by the new # codex-rs/mxc-sandbox workspace member, and moved the commit with it. Keeping # the eight old keys failed exactly as above, because mxc-sdk had none. # # Windows-only git dependencies are vendored anyway, because cargo resolves the # whole dependency graph before it ever looks at the target, so a # Windows-gated git dependency still has to be fetchable. Deleting them from # Cargo.toml instead would work and would be a divergence from upstream's lock # to re-apply, and re-verify, on every bump. declare -A GIT_CRATES=( [mxc-sdk]='https://github.com/microsoft/mxc;b21120168d70befe12356411423266bfef168446;mxc-%commit%/src/mxc-sdk' [crossterm]='https://github.com/openai-oss-forks/crossterm;ed1cdab335221515706178d68495bba2aed1924f;crossterm-%commit%' [h3-quinn]='https://github.com/hyperium/h3;e07e69412876f7e26f026bd75a48b2704d8c8283;h3-%commit%/h3-quinn' [h3]='https://github.com/hyperium/h3;e07e69412876f7e26f026bd75a48b2704d8c8283;h3-%commit%/h3' [nucleo-matcher]='https://github.com/helix-editor/nucleo;4253de9faabb4e5c6d81d946a5e35a90f87347ee;nucleo-%commit%/matcher' [nucleo]='https://github.com/helix-editor/nucleo;4253de9faabb4e5c6d81d946a5e35a90f87347ee;nucleo-%commit%' [rmcp-macros]='https://github.com/modelcontextprotocol/rust-sdk;98a54789b80110ad210495c4ad845c89e1980471;rust-sdk-%commit%/crates/rmcp-macros' [rmcp]='https://github.com/modelcontextprotocol/rust-sdk;98a54789b80110ad210495c4ad845c89e1980471;rust-sdk-%commit%/crates/rmcp' [runfiles]='https://github.com/dzbarsky/rules_rust;b56cbaa8465e74127f1ea216f813cd377295ad81;rules_rust-%commit%/rust/runfiles' [tokio-tungstenite]='https://github.com/openai-oss-forks/tokio-tungstenite;0e5b2d73aa18dd9f0a50ee9ff199d5aef7594186;tokio-tungstenite-%commit%' [tungstenite]='https://github.com/openai-oss-forks/tungstenite-rs;4fffad30fe373adbdcffab9545e9e9bf4f2fc19f;tungstenite-rs-%commit%' [v8]='https://github.com/seanh-oai/rusty_v8;350c5a859a9b3c84ab8701b3ad51e3b7699b30c0;rusty_v8-%commit%' ) RUST_MIN_VER="1.95.0" # Tag of the crate tarball published by the crate-dist fork # (gentoo-zh-drafts/codex). It is derived from PV rather than written out, # because the failure mode of the two choices is not symmetric. Deriving it and # guessing wrong means the fetch 404s -- loud, before a single crate compiles. # Writing it out and forgetting to raise it vendors the PREVIOUS release's # crates, and cargo then dies deep in the build on whichever dependency moved # ("failed to select a version for the requirement"), reading like an upstream # bug rather than a stale pin. # # That is not hypothetical: it stayed at a literal rust-v0.150.0 across the # 0.151, 0.152 and 0.153 bumps, and that vendor directory is missing six crates # the 0.153.0 lock needs -- cidr, flatbuffers, tracelogging, tracelogging_macros, # unicode-general-category, winreg. # # Replace it with a literal ONLY when the fork published no tarball for this # release AND the two codex-rs/Cargo.lock files are byte-identical; say which # tag and why, as the 0.150.1 case did (both locks hashed # beb9a924bf01f03ecfa9fedbf50a602cbaa5399df5cbe57e2d6bd40529e055a6). MY_CRATES_TAG="rust-v${PV}" # Version of the prebuilt V8 static library, which MUST equal the version of # the "v8" crate the lock resolves -- the archive ships its own FFI bindings # (RUSTY_V8_SRC_BINDING_PATH), so a mismatched pair compiles against one ABI # and links against another. Upstream documents this as # ".github/scripts/rusty_v8_bazel.py resolved-v8-crate-version", but that # script is not runnable outside their Bazel checkout (it imports # run_bazel_with_buildbuddy). Read it off the lock instead: # # awk '/^name = "v8"$/{f=1} f&&/^version = /{print $3; exit}' codex-rs/Cargo.lock # # It was stale at 147.4.0 while the lock had already moved to 150.4.0. RUSTY_V8_TAG="152.2.0" inherit cargo DESCRIPTION="Codex CLI - OpenAI's AI-powered coding agent" HOMEPAGE="https://github.com/openai/codex" # The crate tarball URL uses ${PV} so it auto-updates when bumping versions. # This tarball is generated by: https://github.com/gentoo-zh-drafts/codex/ # See .github/workflows/crates.yml for the generation process. SRC_URI=" https://github.com/openai/${PN}/archive/rust-v${PV}.tar.gz -> ${P}.tar.gz https://github.com/gentoo-zh-drafts/codex/releases/download/${MY_CRATES_TAG}/codex-${MY_CRATES_TAG}-crates.tar.xz amd64? ( https://github.com/openai/codex/releases/download/rusty-v8-v${RUSTY_V8_TAG}/librusty_v8_release_x86_64-unknown-linux-musl.a.gz -> rusty_v8_${RUSTY_V8_TAG}_librusty_v8_release_x86_64-unknown-linux-musl.a.gz https://github.com/openai/codex/releases/download/rusty-v8-v${RUSTY_V8_TAG}/src_binding_release_x86_64-unknown-linux-musl.rs -> rusty_v8_${RUSTY_V8_TAG}_src_binding_release_x86_64-unknown-linux-musl.rs ) ${CARGO_CRATE_URIS} " S="${WORKDIR}/${PN}-rust-v${PV}/codex-rs" LICENSE="Apache-2.0" # Dependent crate licenses LICENSE+=" Apache-2.0 Apache-2.0-with-LLVM-exceptions BSD-2 BSD Boost-1.0 CC0-1.0 CDLA-Permissive-2.0 ISC MIT MPL-2.0 Unicode-3.0 ZLIB " SLOT="0" KEYWORDS="-* ~amd64" # Tests fail due to ring crate conflicts with system OpenSSL RESTRICT="test" DEPEND=" dev-libs/openssl:= sys-apps/dbus " RDEPEND="${DEPEND}" BDEPEND="virtual/pkgconfig" # rust does not use *FLAGS from make.conf, silence portage warning QA_FLAGS_IGNORED="usr/bin/${PN}" gen_git_crate_dir() { # https://github.com/gentoo/gentoo/blob/b09dd88412fe2d5eee5a8891e08bfa2d67848da3/eclass/cargo.eclass#L442 IFS=';' read -r crate_uri commit crate_dir <<<"${GIT_CRATES[$1]}" echo "${WORKDIR}/${crate_dir//%commit%/${commit}}" } src_prepare() { default # Fix tokio-tungstenite's git dependency on tungstenite sed -i '/^\[dependencies\.tungstenite\]/,/^$/{ s|git = "https://github.com/openai-oss-forks/tungstenite-rs"|path = "'"$(gen_git_crate_dir tungstenite)"'"| /^rev = /d }' "$(gen_git_crate_dir tokio-tungstenite)/Cargo.toml" || die # rustc 1.98 counts the layout query of an async fn body against # recursion_limit, and codex-chatgpt's connectors::list_connectors() adds # 130 to the depth on its own -- past the default of 128, which aborts the # crate with "queries overflow the depth limit!". Upstream never sees it # (their toolchain accounts the query differently), so the crate root # carried no attribute of its own; the guard exists because inserting a # second #![recursion_limit] once upstream adds one is a hard error. # By 0.161.0 the crate mentions recursion_limit itself, so only insert ours # when no crate-level attribute is present. if ! grep -q '^[[:space:]]*#!\[recursion_limit' chatgpt/src/lib.rs; then sed -i '1i #![recursion_limit = "512"]' chatgpt/src/lib.rs || die fi # bentoo: see the CRATES note. Only ever raise a version: once the lock # is at or past the wanted one on its own, its entry here does nothing. _codex_raise_locked_crate() { local name=${1} want=${2} sum=${3} have have=$(awk -v n="name = \"${name}\"" '$0==n{f=1} f&&/^version = /{gsub(/"/,"",$3); print $3; exit}' Cargo.lock) || die "Cannot read ${name} from Cargo.lock" [[ -n ${have} ]] || die "Cargo.lock no longer locks ${name}; drop its CRATES entry" ver_test "${have}" -ge "${want}" && return 0 sed -i '/^name = "'"${name}"'"$/,/^$/{ s|^version = ".*"|version = "'"${want}"'"| s|^checksum = ".*"|checksum = "'"${sum}"'"| }' Cargo.lock || die grep -A1 "^name = \"${name}\"$" Cargo.lock | grep -qxF "version = \"${want}\"" || die "${name} was not raised to ${want} in Cargo.lock" } _codex_raise_locked_crate openssl-sys 0.9.118 \ 19c413d6ec9ebaf5e1c829d355cb3e9f783778d700f135be0921d52fce3c60ae _codex_raise_locked_crate openssl 0.10.82 \ 4de8111e94a56adca7ebfe6c365b6d42f99deb69b74b6becacd4204e2f193aa2 # openssl 0.10.81 dropped its once_cell dependency; a stale entry in its # lock block makes cargo want to rewrite the lock, which --offline refuses. sed -i '/^name = "openssl"$/,/^$/{/^ "once_cell",$/d}' Cargo.lock || die unset -f _codex_raise_locked_crate # Remove the [patch.crates-io] section and add path-based patches sed -i '/^\[patch\.crates-io\]/,/^$/d' "${S}/Cargo.toml" || die sed -i '/^\[patch\."ssh:\/\/git@github\.com/,/^$/d' "${S}/Cargo.toml" || die # Add new patch section with local paths cat >> "${S}/Cargo.toml" <<-EOF || die [patch.crates-io] crossterm = { path = "$(gen_git_crate_dir crossterm)" } tokio-tungstenite = { path = "$(gen_git_crate_dir tokio-tungstenite)" } tungstenite = { path = "$(gen_git_crate_dir tungstenite)" } v8 = { path = "$(gen_git_crate_dir v8)" } EOF } src_compile() { local rusty_v8_triple use amd64 && rusty_v8_triple=x86_64-unknown-linux-musl RUSTY_V8_ARCHIVE="${DISTDIR}/rusty_v8_${RUSTY_V8_TAG}_librusty_v8_release_${rusty_v8_triple}.a.gz" \ RUSTY_V8_SRC_BINDING_PATH="${DISTDIR}/rusty_v8_${RUSTY_V8_TAG}_src_binding_release_${rusty_v8_triple}.rs" \ cargo_src_compile --package codex-cli } src_install() { dobin "$(cargo_target_dir)/codex" einstalldocs }