Skip to content

Commit 94d4f85

Browse files
committed
Update Codex 0.148 Solaris build and runtime fixes
Build a native protoc for the code-mode protocol, honor PROTOC in its build script, and install the 0.148 sources. Refresh the exec-server patch, keep Mio event-port fallback polling active, and make the vendored Mio patch series repeatable.
1 parent 58f09b0 commit 94d4f85

8 files changed

Lines changed: 285 additions & 74 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ Installed artifacts end up in:
5151
- `build/install/codex/bin/codex`
5252
- `build/install/codex/bin/codex-code-mode-host`
5353

54-
Codex 0.147 enables the separate code-mode host by default. Keep
54+
Codex 0.148 enables the separate code-mode host by default. Keep
5555
`codex-code-mode-host` beside `codex` when copying or packaging this build.
5656

5757
Quick verification:
@@ -69,7 +69,7 @@ build/install/codex/bin/codex-code-mode-host --help
6969
- The wrapper uses the official Solaris Rust standalone installer target
7070
`x86_64-pc-solaris`.
7171
- The pinned Codex source is the upstream `openai/codex` release tag
72-
`rust-v0.147.0`, built from its `codex-rs/` workspace.
72+
`rust-v0.148.0`, built from its `codex-rs/` workspace.
7373
- Set `SOLARIS_CODEX_PROXY_SETUP=/path/to/proxy.sh` if your host needs an
7474
environment hook before downloads.
7575
- The codex build clears inherited Solaris `LD_*` hardening variables because

build-codex.sh

Lines changed: 58 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -350,48 +350,75 @@ patch_vendored_mio_event_ports() {
350350
local event_ports_rs=${mio_root}/src/sys/unix/selector/event_ports.rs
351351
local base_patch=${TOP}/patches/mio/0001-selector-use-solaris-event-ports.patch
352352
local cache_stamp=${CODEX_SRC_DIR}/target/.solaris-mio-event-ports.sha256
353+
local patch_stamp=${mio_root}/.solaris-mio-patch-series.sha256
353354
local event_ports_hash
354355
local cached_hash=
356+
local patch_series_hash
357+
local cached_patch_series_hash=
355358
local patch
356359

357360
[[ -d "${mio_root}" ]] || die "missing vendored mio source: ${mio_root}"
358361
[[ -f "${checksum_json}" ]] || die "missing vendored mio checksum: ${checksum_json}"
359362

360-
if [[ ! -f "${event_ports_rs}" ]]; then
361-
[[ -f "${base_patch}" ]] || die "missing mio Solaris event ports patch: ${base_patch}"
362-
log "Applying vendored mio $(basename "${base_patch}")"
363-
(
364-
cd "${mio_root}"
365-
"${PATCH_TOOL}" --batch --forward --fuzz=0 -p1 < "${base_patch}"
366-
) || die "failed to apply vendored mio patch: ${base_patch}"
367-
fi
363+
patch_series_hash=$(python3 - "${TOP}/patches/mio" <<'PY2'
364+
from pathlib import Path
365+
import hashlib
366+
import sys
368367
369-
[[ -f "${event_ports_rs}" ]] || die "vendored mio Solaris event ports source was not created"
368+
digest = hashlib.sha256()
369+
for path in sorted(Path(sys.argv[1]).glob("*.patch")):
370+
digest.update(path.name.encode())
371+
digest.update(b"\0")
372+
digest.update(path.read_bytes())
373+
print(digest.hexdigest())
374+
PY2
375+
)
376+
if [[ -f "${patch_stamp}" ]]; then
377+
cached_patch_series_hash=$(<"${patch_stamp}")
378+
fi
370379

371-
# Newer Mio revisions may already contain the base event-ports
372-
# implementation. Apply Solaris correctness fixes independently so they are
373-
# also used when the base patch is unnecessary.
374-
for patch in "${TOP}/patches/mio"/*.patch; do
375-
[[ -e "${patch}" ]] || continue
376-
[[ "${patch}" == "${base_patch}" ]] && continue
377-
if (
378-
cd "${mio_root}"
379-
"${PATCH_TOOL}" --dry-run --batch --forward --fuzz=0 -p1 < "${patch}" >/dev/null 2>&1
380-
); then
381-
log "Applying vendored mio $(basename "${patch}")"
380+
if [[ "${cached_patch_series_hash}" == "${patch_series_hash}" ]]; then
381+
log "Already applied vendored mio Solaris patch series"
382+
else
383+
if [[ ! -f "${event_ports_rs}" ]]; then
384+
[[ -f "${base_patch}" ]] || die "missing mio Solaris event ports patch: ${base_patch}"
385+
log "Applying vendored mio $(basename "${base_patch}")"
382386
(
383387
cd "${mio_root}"
384-
"${PATCH_TOOL}" --batch --forward --fuzz=0 -p1 < "${patch}"
385-
)
386-
elif (
387-
cd "${mio_root}"
388-
"${PATCH_TOOL}" --dry-run --batch --forward --fuzz=0 -R -p1 < "${patch}" >/dev/null 2>&1
389-
); then
390-
log "Already applied vendored mio $(basename "${patch}")"
391-
else
392-
die "failed to apply vendored mio patch: ${patch}"
388+
"${PATCH_TOOL}" --batch --forward --fuzz=0 -p1 < "${base_patch}"
389+
) || die "failed to apply vendored mio patch: ${base_patch}"
393390
fi
394-
done
391+
392+
[[ -f "${event_ports_rs}" ]] || die "vendored mio Solaris event ports source was not created"
393+
394+
# Newer Mio revisions may already contain the base event-ports
395+
# implementation. Apply Solaris correctness fixes independently so they are
396+
# also used when the base patch is unnecessary.
397+
for patch in "${TOP}/patches/mio"/*.patch; do
398+
[[ -e "${patch}" ]] || continue
399+
[[ "${patch}" == "${base_patch}" ]] && continue
400+
if (
401+
cd "${mio_root}"
402+
"${PATCH_TOOL}" --dry-run --batch --forward --fuzz=0 -p1 < "${patch}" >/dev/null 2>&1
403+
); then
404+
log "Applying vendored mio $(basename "${patch}")"
405+
(
406+
cd "${mio_root}"
407+
"${PATCH_TOOL}" --batch --forward --fuzz=0 -p1 < "${patch}"
408+
)
409+
elif (
410+
cd "${mio_root}"
411+
"${PATCH_TOOL}" --dry-run --batch --forward --fuzz=0 -R -p1 < "${patch}" >/dev/null 2>&1
412+
); then
413+
log "Already applied vendored mio $(basename "${patch}")"
414+
else
415+
die "failed to apply vendored mio patch: ${patch}"
416+
fi
417+
done
418+
printf '%s\n' "${patch_series_hash}" > "${patch_stamp}"
419+
fi
420+
421+
[[ -f "${event_ports_rs}" ]] || die "vendored mio Solaris event ports source was not created"
395422

396423
python3 - "${mio_root}" "${checksum_json}" <<'PY2'
397424
from pathlib import Path
@@ -1066,6 +1093,7 @@ refresh_cached_rusty_v8_archive() {
10661093

10671094

10681095
prepare_rust
1096+
prepare_protoc
10691097
build_env_common
10701098

10711099
v8_install_matches_pin || bash "${TOP}/build-v8.sh"

common.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ CODEX_SRC_DIR=${CODEX_REPO_DIR}/codex-rs
2626
CODEX_VENDOR_DIR=${SRC_DIR}/codex-rust-v${SOLARIS_CODEX_VERSION}-vendored-sources
2727
BINDGEN_PREFIX=${TOOLCHAIN_DIR}/bindgen-cli-${BINDGEN_CLI_VERSION}
2828
BINDGEN_ROOT_DIR=${TOOLCHAIN_DIR}/rust-bindgen-root
29+
PROTOBUF_PREFIX=${TOOLCHAIN_DIR}/protobuf-${PROTOBUF_VERSION}
30+
PROTOBUF_ARCHIVE=protobuf-${PROTOBUF_VERSION}.tar.gz
31+
PROTOBUF_ARCHIVE_URL=https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOBUF_VERSION}/${PROTOBUF_ARCHIVE}
2932

3033
GN_INSTALL_DIR=${INSTALL_DIR}/gn
3134
V8_INSTALL_DIR=${INSTALL_DIR}/v8
@@ -212,6 +215,50 @@ prepare_rust() {
212215
)
213216
}
214217

218+
prepare_protoc() {
219+
ensure_dirs
220+
need_cmd /usr/bin/curl
221+
need_cmd gtar
222+
need_cmd cmake
223+
need_cmd gcc
224+
need_cmd g++
225+
226+
if [[ -x "${PROTOBUF_PREFIX}/bin/protoc" ]]; then
227+
return 0
228+
fi
229+
230+
maybe_enable_proxy
231+
232+
local archive_path=${DOWNLOAD_DIR}/${PROTOBUF_ARCHIVE}
233+
local source_dir=${SRC_DIR}/protobuf-${PROTOBUF_VERSION}
234+
local build_dir=${BUILD_DIR}/protobuf-build-${PROTOBUF_VERSION}
235+
236+
if [[ ! -f "${archive_path}" ]]; then
237+
log "Downloading protobuf ${PROTOBUF_VERSION} for native protoc"
238+
/usr/bin/curl -L -f -o "${archive_path}" "${PROTOBUF_ARCHIVE_URL}"
239+
fi
240+
241+
rm -rf "${source_dir}" "${build_dir}"
242+
gtar xf "${archive_path}" -C "${SRC_DIR}"
243+
244+
log "Building native protoc ${PROTOBUF_VERSION}"
245+
cmake -S "${source_dir}" -B "${build_dir}" \
246+
-DCMAKE_BUILD_TYPE=Release \
247+
-DCMAKE_C_COMPILER="$(command -v gcc)" \
248+
-DCMAKE_CXX_COMPILER="$(command -v g++)" \
249+
-DCMAKE_INSTALL_PREFIX="${PROTOBUF_PREFIX}" \
250+
-Dprotobuf_BUILD_TESTS=OFF \
251+
-Dprotobuf_BUILD_EXAMPLES=OFF \
252+
-Dprotobuf_BUILD_SHARED_LIBS=OFF \
253+
-Dprotobuf_WITH_ZLIB=OFF
254+
cmake --build "${build_dir}" --target protoc -j "${SOLARIS_CODEX_JOBS:-4}"
255+
mkdir -p "${PROTOBUF_PREFIX}/bin"
256+
cp -p "${build_dir}/protoc" "${PROTOBUF_PREFIX}/bin/protoc"
257+
258+
[[ -x "${PROTOBUF_PREFIX}/bin/protoc" ]] || \
259+
die "native protoc build did not produce ${PROTOBUF_PREFIX}/bin/protoc"
260+
}
261+
215262
ensure_gn_source() {
216263
fetch_git_source "${GN_GIT_URL}" "${GN_GIT_REF}" "${GN_SRC_DIR}"
217264
}
@@ -337,6 +384,7 @@ build_env_common() {
337384
export NINJA=/usr/bin/ninja
338385
export LIBCLANG_PATH="$(detect_libclang_path)"
339386
export CARGO_NET_GIT_FETCH_WITH_CLI=true
387+
export PROTOC="${PROTOBUF_PREFIX}/bin/protoc"
340388
}
341389

342390
verify_file() {

patches/codex/0010-exec-server-drain-fs-helper-output-concurrently.patch

Lines changed: 24 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,23 @@ before it can exit. Add a regression test that returns a helper-shaped response
1010
larger than a typical pipe buffer.
1111

1212
diff --git a/codex-rs/exec-server/src/fs_sandbox.rs b/codex-rs/exec-server/src/fs_sandbox.rs
13-
index 812d767..1286e8f 100644
1413
--- a/codex-rs/exec-server/src/fs_sandbox.rs
1514
+++ b/codex-rs/exec-server/src/fs_sandbox.rs
16-
@@ -17,6 +17,7 @@ use codex_sandboxing::SandboxablePreference;
17-
use codex_utils_absolute_path::AbsolutePathBuf;
15+
@@ -19,5 +19,6 @@ use codex_utils_absolute_path::AbsolutePathBuf;
1816
use codex_utils_absolute_path::canonicalize_preserving_symlinks;
1917
use codex_utils_path_uri::PathUri;
2018
+use tokio::io::AsyncReadExt;
2119
use tokio::io::AsyncWriteExt;
2220
use tokio::process::Command;
2321

24-
@@ -300,6 +301,25 @@ async fn run_command(
25-
request_json: Vec<u8>,
26-
) -> Result<FsHelperPayload, JSONRPCErrorError> {
27-
let mut child = spawn_command(command)?;
22+
@@ -322,9 +323,35 @@ async fn run_command(
23+
}
24+
25+
pub(crate) async fn wait_for_helper_output(
26+
- child: tokio::process::Child,
27+
+ mut child: tokio::process::Child,
28+
) -> Result<std::process::Output, JSONRPCErrorError> {
29+
- let output = child.wait_with_output().await.map_err(io_error)?;
2830
+ let mut stdout = child
2931
+ .stdout
3032
+ .take()
@@ -34,43 +36,28 @@ index 812d767..1286e8f 100644
3436
+ .take()
3537
+ .ok_or_else(|| internal_error("failed to open fs sandbox helper stderr".to_string()))?;
3638
+ let stdout_task = tokio::spawn(async move {
37-
+ let mut stdout_buf = Vec::new();
38-
+ stdout.read_to_end(&mut stdout_buf).await?;
39-
+ Ok::<_, std::io::Error>(stdout_buf)
39+
+ let mut bytes = Vec::new();
40+
+ stdout.read_to_end(&mut bytes).await?;
41+
+ Ok::<_, std::io::Error>(bytes)
4042
+ });
4143
+ let stderr_task = tokio::spawn(async move {
42-
+ let mut stderr_buf = Vec::new();
43-
+ stderr.read_to_end(&mut stderr_buf).await?;
44-
+ Ok::<_, std::io::Error>(stderr_buf)
44+
+ let mut bytes = Vec::new();
45+
+ stderr.read_to_end(&mut bytes).await?;
46+
+ Ok::<_, std::io::Error>(bytes)
4547
+ });
4648
+
47-
let mut stdin = child
48-
.stdin
49-
.take()
50-
@@ -308,15 +328,17 @@ async fn run_command(
51-
stdin.shutdown().await.map_err(io_error)?;
52-
drop(stdin);
53-
54-
- let output = child.wait_with_output().await.map_err(io_error)?;
55-
- if !output.status.success() {
5649
+ let status = child.wait().await.map_err(io_error)?;
5750
+ let stdout = stdout_task.await.map_err(join_error)?.map_err(io_error)?;
5851
+ let stderr = stderr_task.await.map_err(join_error)?.map_err(io_error)?;
59-
+
60-
+ if !status.success() {
52+
+ let output = std::process::Output {
53+
+ status,
54+
+ stdout,
55+
+ stderr,
56+
+ };
57+
if !output.status.success() {
6158
return Err(internal_error(format!(
6259
"fs sandbox helper failed with status {status}: {stderr}",
63-
- status = output.status,
64-
- stderr = String::from_utf8_lossy(&output.stderr).trim()
65-
+ stderr = String::from_utf8_lossy(&stderr).trim()
66-
)));
67-
}
68-
- let response: FsHelperResponse = serde_json::from_slice(&output.stdout).map_err(json_error)?;
69-
+ let response: FsHelperResponse = serde_json::from_slice(&stdout).map_err(json_error)?;
70-
match response {
71-
FsHelperResponse::Ok(payload) => Ok(payload),
72-
FsHelperResponse::Error(error) => Err(error),
73-
@@ -359,6 +381,10 @@ fn io_error(err: std::io::Error) -> JSONRPCErrorError {
60+
@@ -381,6 +408,10 @@ pub(crate) fn io_error(err: std::io::Error) -> JSONRPCErrorError {
7461
internal_error(err.to_string())
7562
}
7663

@@ -81,8 +68,8 @@ index 812d767..1286e8f 100644
8168
fn json_error(err: serde_json::Error) -> JSONRPCErrorError {
8269
internal_error(format!(
8370
"failed to encode or decode fs sandbox helper message: {err}"
84-
@@ -682,6 +708,58 @@ mod tests {
85-
assert!(policy.can_read_path_with_cwd(alias_parent.as_path(), cwd.as_path()));
71+
@@ -727,6 +758,58 @@ mod tests {
72+
assert!(!policy.can_read_path_with_cwd(alias_parent.as_path(), cwd.as_path()));
8673
}
8774

8875
+ #[cfg(unix)]
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Allow builders to provide a native protoc executable. The
2+
protoc-bin-vendored crate has no Solaris platform package, and its statically
3+
linked Linux executable cannot run on Solaris.
4+
5+
diff --git a/codex-rs/code-mode-protocol/build.rs b/codex-rs/code-mode-protocol/build.rs
6+
--- a/codex-rs/code-mode-protocol/build.rs
7+
+++ b/codex-rs/code-mode-protocol/build.rs
8+
@@ -5,7 +5,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
9+
println!("cargo:rerun-if-changed=src/grpc");
10+
11+
let mut config = tonic_prost_build::Config::new();
12+
- config.protoc_executable(protoc_bin_vendored::protoc_bin_path()?);
13+
+ let protoc = match std::env::var_os("PROTOC") {
14+
+ Some(path) => PathBuf::from(path),
15+
+ None => protoc_bin_vendored::protoc_bin_path()?,
16+
+ };
17+
+ config.protoc_executable(protoc);
18+
let proto_files = glob::glob("src/grpc/*.proto")?.collect::<Result<Vec<_>, _>>()?;
19+
20+
tonic_prost_build::configure()

patches/mio/0002-event-ports-rearm-fallback-interests.patch

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,23 +255,29 @@ index aacf254..ee1d0ff 100644
255255
}
256256

257257
cfg_io_source! {
258-
@@ -454,8 +473,7 @@ impl SelectorState {
258+
@@ -454,8 +473,11 @@ impl SelectorState {
259259
interests,
260260
generation: 1,
261261
associated: true,
262262
- needs_fallback: true,
263263
- fallback_writable: true,
264-
+ fallback_interests: None,
264+
+ // Poll immediately after registration as a safety net for
265+
+ // readiness that changes while the event-port association is
266+
+ // being installed. Without this, a missed initial event can
267+
+ // leave the descriptor outside fallback polling forever.
268+
+ fallback_interests: Some(interests),
265269
},
266270
);
267271
Ok(record)
268-
@@ -480,8 +498,7 @@ impl SelectorState {
272+
@@ -480,8 +502,9 @@ impl SelectorState {
269273
data.interests = interests;
270274
data.generation = generation;
271275
data.associated = true;
272276
- data.needs_fallback = true;
273277
- data.fallback_writable = true;
274-
+ data.fallback_interests = None;
278+
+ // As with initial registration, cover readiness changes racing with
279+
+ // the new event-port association.
280+
+ data.fallback_interests = Some(interests);
275281
Ok(())
276282
}
277283

0 commit comments

Comments
 (0)