Skip to content

Commit 9db53b3

Browse files
authored
fix: support arm64 build for Linux (openai#1225)
Users were running into issues with glibc mismatches on arm64 linux. In the past, we did not provide a musl build for arm64 Linux because we had trouble getting the openssl dependency to build correctly. Though today I just tried the same trick in `Cargo.toml` that we were doing for `x86_64-unknown-linux-musl` (using `openssl-sys` with `features = ["vendored"]`), so I'm not sure what problem we had in the past the builds "just worked" today! Though one tweak that did have to be made is that the integration tests for Seccomp/Landlock empirically require longer timeouts on arm64 linux, or at least on the `ubuntu-24.04-arm` GitHub Runner. As such, we change the timeouts for arm64 in `codex-rs/linux-sandbox/tests/landlock.rs`. Though in solving this problem, I decided I needed a turnkey solution for testing the Linux build(s) from my Mac laptop, so this PR introduces `.devcontainer/Dockerfile` and `.devcontainer/devcontainer.json` to facilitate this. Detailed instructions are in `.devcontainer/README.md`. We will update `dotslash-config.json` and other release-related scripts in a follow-up PR.
1 parent 515b633 commit 9db53b3

File tree

8 files changed

+128
-7
lines changed

8 files changed

+128
-7
lines changed

.devcontainer/Dockerfile

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
FROM ubuntu:22.04
2+
3+
ARG DEBIAN_FRONTEND=noninteractive
4+
# enable 'universe' because musl-tools & clang live there
5+
RUN apt-get update && \
6+
apt-get install -y --no-install-recommends \
7+
software-properties-common && \
8+
add-apt-repository --yes universe
9+
10+
# now install build deps
11+
RUN apt-get update && \
12+
apt-get install -y --no-install-recommends \
13+
build-essential curl git ca-certificates \
14+
pkg-config clang musl-tools libssl-dev && \
15+
rm -rf /var/lib/apt/lists/*
16+
17+
# non-root dev user
18+
ARG USER=dev
19+
ARG UID=1000
20+
RUN useradd -m -u $UID $USER
21+
USER $USER
22+
23+
# install Rust + musl target as dev user
24+
RUN curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal && \
25+
~/.cargo/bin/rustup target add aarch64-unknown-linux-musl
26+
27+
ENV PATH="/home/${USER}/.cargo/bin:${PATH}"
28+
29+
WORKDIR /workspace

.devcontainer/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Containerized Development
2+
3+
We provide the following options to facilitate Codex development in a container. This is particularly useful for verifying the Linux build when working on a macOS host.
4+
5+
## Docker
6+
7+
To build the Docker image locally for x64 and then run it with the repo mounted under `/workspace`:
8+
9+
```shell
10+
CODEX_DOCKER_IMAGE_NAME=codex-linux-dev
11+
docker build --platform=linux/amd64 -t "$CODEX_DOCKER_IMAGE_NAME" ./.devcontainer
12+
docker run --platform=linux/amd64 --rm -it -e CARGO_TARGET_DIR=/workspace/codex-rs/target-amd64 -v "$PWD":/workspace -w /workspace/codex-rs "$CODEX_DOCKER_IMAGE_NAME"
13+
```
14+
15+
Note that `/workspace/target` will contain the binaries built for your host platform, so we include `-e CARGO_TARGET_DIR=/workspace/codex-rs/target-amd64` in the `docker run` command so that the binaries built inside your container are written to a separate directory.
16+
17+
For arm64, specify `--platform=linux/amd64` instead for both `docker build` and `docker run`.
18+
19+
Currently, the `Dockerfile` works for both x64 and arm64 Linux, though you need to run `rustup target add x86_64-unknown-linux-musl` yourself to install the musl toolchain for x64.
20+
21+
## VS Code
22+
23+
VS Code recognizes the `devcontainer.json` file and gives you the option to develop Codex in a container. Currently, `devcontainer.json` builds and runs the `arm64` flavor of the container.
24+
25+
From the integrated terminal in VS Code, you can build either flavor of the `arm64` build (GNU or musl):
26+
27+
```shell
28+
cargo build --target aarch64-unknown-linux-musl
29+
cargo build --target aarch64-unknown-linux-gnu
30+
```

.devcontainer/devcontainer.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "Codex",
3+
"build": {
4+
"dockerfile": "Dockerfile",
5+
"context": "..",
6+
"platform": "linux/arm64"
7+
},
8+
9+
/* Force VS Code to run the container as arm64 in
10+
case your host is x86 (or vice-versa). */
11+
"runArgs": ["--platform=linux/arm64"],
12+
13+
"containerEnv": {
14+
"RUST_BACKTRACE": "1",
15+
"CARGO_TARGET_DIR": "${containerWorkspaceFolder}/codex-rs/target-arm64"
16+
},
17+
18+
"remoteUser": "dev",
19+
"customizations": {
20+
"vscode": {
21+
"settings": {
22+
"terminal.integrated.defaultProfile.linux": "bash"
23+
},
24+
"extensions": [
25+
"rust-lang.rust-analyzer"
26+
],
27+
}
28+
}
29+
}

.github/workflows/rust-ci.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ jobs:
5555
target: x86_64-unknown-linux-musl
5656
- runner: ubuntu-24.04
5757
target: x86_64-unknown-linux-gnu
58+
- runner: ubuntu-24.04-arm
59+
target: aarch64-unknown-linux-musl
60+
- runner: ubuntu-24.04-arm
61+
target: aarch64-unknown-linux-gnu
5862
- runner: windows-latest
5963
target: x86_64-pc-windows-msvc
6064

@@ -75,7 +79,7 @@ jobs:
7579
${{ github.workspace }}/codex-rs/target/
7680
key: cargo-${{ matrix.runner }}-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }}
7781

78-
- if: ${{ matrix.target == 'x86_64-unknown-linux-musl' }}
82+
- if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl'}}
7983
name: Install musl build tools
8084
run: |
8185
sudo apt install -y musl-tools pkg-config

.github/workflows/rust-release.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ jobs:
6969
target: x86_64-unknown-linux-musl
7070
- runner: ubuntu-24.04
7171
target: x86_64-unknown-linux-gnu
72+
- runner: ubuntu-24.04-arm
73+
target: aarch64-unknown-linux-musl
7274
- runner: ubuntu-24.04-arm
7375
target: aarch64-unknown-linux-gnu
7476

@@ -88,7 +90,7 @@ jobs:
8890
${{ github.workspace }}/codex-rs/target/
8991
key: cargo-release-${{ matrix.runner }}-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }}
9092

91-
- if: ${{ matrix.target == 'x86_64-unknown-linux-musl' }}
93+
- if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl'}}
9294
name: Install musl build tools
9395
run: |
9496
sudo apt install -y musl-tools pkg-config

codex-rs/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
11
/target/
2+
3+
# Recommended value of CARGO_TARGET_DIR when using Docker as explained in .devcontainer/README.md.
4+
/target-amd64/
5+
6+
# Value of CARGO_TARGET_DIR when using .devcontainer/devcontainer.json.
7+
/target-arm64/

codex-rs/core/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ seccompiler = "0.5.0"
5959
[target.x86_64-unknown-linux-musl.dependencies]
6060
openssl-sys = { version = "*", features = ["vendored"] }
6161

62+
# Build OpenSSL from source for musl builds.
63+
[target.aarch64-unknown-linux-musl.dependencies]
64+
openssl-sys = { version = "*", features = ["vendored"] }
65+
6266
[dev-dependencies]
6367
assert_cmd = "2"
6468
maplit = "1.0.2"

codex-rs/linux-sandbox/tests/landlock.rs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,23 @@ use std::sync::Arc;
1515
use tempfile::NamedTempFile;
1616
use tokio::sync::Notify;
1717

18+
// At least on GitHub CI, the arm64 tests appear to need longer timeouts.
19+
20+
#[cfg(not(target_arch = "aarch64"))]
21+
const SHORT_TIMEOUT_MS: u64 = 200;
22+
#[cfg(target_arch = "aarch64")]
23+
const SHORT_TIMEOUT_MS: u64 = 5_000;
24+
25+
#[cfg(not(target_arch = "aarch64"))]
26+
const LONG_TIMEOUT_MS: u64 = 1_000;
27+
#[cfg(target_arch = "aarch64")]
28+
const LONG_TIMEOUT_MS: u64 = 5_000;
29+
30+
#[cfg(not(target_arch = "aarch64"))]
31+
const NETWORK_TIMEOUT_MS: u64 = 2_000;
32+
#[cfg(target_arch = "aarch64")]
33+
const NETWORK_TIMEOUT_MS: u64 = 10_000;
34+
1835
fn create_env_from_core_vars() -> HashMap<String, String> {
1936
let policy = ShellEnvironmentPolicy::default();
2037
create_env(&policy)
@@ -52,7 +69,7 @@ async fn run_cmd(cmd: &[&str], writable_roots: &[PathBuf], timeout_ms: u64) {
5269

5370
#[tokio::test]
5471
async fn test_root_read() {
55-
run_cmd(&["ls", "-l", "/bin"], &[], 200).await;
72+
run_cmd(&["ls", "-l", "/bin"], &[], SHORT_TIMEOUT_MS).await;
5673
}
5774

5875
#[tokio::test]
@@ -63,7 +80,7 @@ async fn test_root_write() {
6380
run_cmd(
6481
&["bash", "-lc", &format!("echo blah > {}", tmpfile_path)],
6582
&[],
66-
200,
83+
SHORT_TIMEOUT_MS,
6784
)
6885
.await;
6986
}
@@ -75,7 +92,7 @@ async fn test_dev_null_write() {
7592
&[],
7693
// We have seen timeouts when running this test in CI on GitHub,
7794
// so we are using a generous timeout until we can diagnose further.
78-
1_000,
95+
LONG_TIMEOUT_MS,
7996
)
8097
.await;
8198
}
@@ -93,7 +110,7 @@ async fn test_writable_root() {
93110
&[tmpdir.path().to_path_buf()],
94111
// We have seen timeouts when running this test in CI on GitHub,
95112
// so we are using a generous timeout until we can diagnose further.
96-
1_000,
113+
LONG_TIMEOUT_MS,
97114
)
98115
.await;
99116
}
@@ -115,7 +132,7 @@ async fn assert_network_blocked(cmd: &[&str]) {
115132
cwd,
116133
// Give the tool a generous 2-second timeout so even slow DNS timeouts
117134
// do not stall the suite.
118-
timeout_ms: Some(2_000),
135+
timeout_ms: Some(NETWORK_TIMEOUT_MS),
119136
env: create_env_from_core_vars(),
120137
};
121138

0 commit comments

Comments
 (0)