Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 45 additions & 37 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
FROM ubuntu:22.04
# Development container for the IPC Toolkit.
#
# One file serves both containers. BASE_IMAGE selects which:
#
# ubuntu:22.04 -> .devcontainer/devcontainer.json
# nvidia/cuda:*-devel-ubuntu22.04 -> .devcontainer/cuda/devcontainer.json
#
# The CUDA "devel" images are Ubuntu 22.04 with nvcc and a host gcc already
# installed, so everything below applies unchanged to both. Compiling CUDA does
# not require a GPU; only running it does. On a Linux host with the NVIDIA
# Container Toolkit you can expose the GPU by adding "--gpus=all" to runArgs in
# the CUDA devcontainer.json.
#
# This does NOT copy the source: the workspace is bind-mounted at runtime by
# the Dev Containers tooling, and .devcontainer/cuda/build-cuda.sh rsyncs it
# into a named volume. All of the project's own dependencies are fetched by
# CMake/CPM, so no development libraries are installed here.

ARG BASE_IMAGE=ubuntu:22.04
FROM ${BASE_IMAGE}

# Set environment variables
ENV DEBIAN_FRONTEND=noninteractive
ENV CCACHE_DIR=/home/devuser/.ccache
ENV CCACHE_MAXSIZE=1G
ENV CXX_STANDARD=17

# Update package lists
RUN apt-get update

# Install essential packages
RUN apt-get install -y --no-install-recommends \
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
git \
wget \
Expand All @@ -19,23 +35,17 @@ RUN apt-get install -y --no-install-recommends \
zsh \
ninja-build \
ccache \
libeigen3-dev \
libtbb-dev \
libspdlog-dev \
rsync \
python3 \
python3-pip \
python3-dev \
libgmp-dev \
libssl-dev \
libncurses5-dev \
libncursesw5-dev \
libxml2-dev \
libjsoncpp-dev \
libz3-dev \
sudo \
software-properties-common \
lsb-release \
gnupg \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*

# Create a new user with sudo privileges
Expand All @@ -47,36 +57,34 @@ RUN useradd -m devuser \
&& chown devuser:devuser $CCACHE_DIR

# Set up Python tools
RUN pip3 install --upgrade pip setuptools wheel pre-commit
RUN pip3 install --no-cache-dir --upgrade pip setuptools wheel pre-commit

# Add Kitware APT repository for CMake
# Add Kitware APT repository for a recent CMake (project requires >= 3.24)
RUN wget -qO- https://apt.kitware.com/keys/kitware-archive-latest.asc | \
gpg --dearmor -o /usr/share/keyrings/kitware-archive-keyring.gpg

RUN echo "deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ $(lsb_release -cs) main" | \
tee /etc/apt/sources.list.d/kitware.list > /dev/null

# Update package lists and install CMake
RUN apt-get update
RUN apt-get install -y cmake
gpg --dearmor -o /usr/share/keyrings/kitware-archive-keyring.gpg \
&& echo "deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ $(lsb_release -cs) main" | \
tee /etc/apt/sources.list.d/kitware.list > /dev/null \
&& apt-get update && apt-get install -y --no-install-recommends cmake \
&& rm -rf /var/lib/apt/lists/* \
&& cmake --version \
&& if command -v nvcc > /dev/null; then nvcc --version; fi

# Verify CMake installation
RUN cmake --version

# Install LLVM/Clang and Clang-Format version 18
RUN wget -q https://apt.llvm.org/llvm.sh -O /tmp/llvm.sh
RUN chmod +x /tmp/llvm.sh
# Install LLVM/Clang and clang-format 18 so pre-commit's clang-format hook
# matches CI.
RUN wget -q https://apt.llvm.org/llvm.sh -O /tmp/llvm.sh \
&& chmod +x /tmp/llvm.sh
# NOTE: llvm.sh exits non-zero in cases where it has still registered the apt
# source, so its status alone is not a failure. Keep it on its own line: in a
# `&&` chain the trailing `|| true` would also swallow a failed download and
# defer the error to the confusing "Unable to locate package clang-18" below.
RUN /tmp/llvm.sh 18 || true
RUN apt-get update \
&& apt-get install -y --no-install-recommends clang-18 clang-tools-18 clang-format-18 \
&& rm -rf /var/lib/apt/lists/* \
&& clang-format-18 --version

RUN apt-get update && apt-get install -y clang-18 clang-tools-18 clang-format-18

RUN clang-18 --version
RUN clang++-18 --version
RUN clang-format-18 --version

# 12. Set the default user and working directory
# Set the default user and working directory
USER devuser

WORKDIR /home/devuser/workspace

CMD ["bash"]
76 changes: 0 additions & 76 deletions .devcontainer/cuda/Dockerfile

This file was deleted.

21 changes: 14 additions & 7 deletions .devcontainer/cuda/build-cuda.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
# hermetic and never writes into your host working tree.
#
# Usage:
# .devcontainer/cuda/build-cuda.sh # cuda-release, arch 75;80;86;89
# .devcontainer/cuda/build-cuda.sh # cuda-release, arch 75
# PRESET=test .devcontainer/cuda/build-cuda.sh # test preset (CUDA + tests)
# CUDA_ARCH="86" .devcontainer/cuda/build-cuda.sh # single architecture
# CUDA_ARCH="75;80;86;89" .devcontainer/cuda/build-cuda.sh # several archs
# JOBS=4 .devcontainer/cuda/build-cuda.sh # limit parallelism (memory)
#
set -euo pipefail
Expand All @@ -23,17 +23,21 @@ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"

IMAGE_NAME="${IMAGE_NAME:-ipc-toolkit-cuda-dev}"
PRESET="${PRESET:-cuda-release}"
CUDA_ARCH="${CUDA_ARCH:-75;80;86;89}"
# One architecture is enough to answer "does it compile": nvcc runs the whole
# device front-end per architecture in the list, so the extra ones only repeat
# codegen. 75 is the oldest we support, hence the strictest. Override to build
# a list when you want to check architecture-specific codegen.
CUDA_ARCH="${CUDA_ARCH:-75}"
CUDA_IMAGE="${CUDA_IMAGE:-nvidia/cuda:12.6.2-devel-ubuntu22.04}"
# Heavy TUs (headers textually include implementations under CUDA) can OOM the
# VM at full parallelism; default below nproc.
JOBS="${JOBS:-4}"

echo ">> Building CUDA dev image '${IMAGE_NAME}'"
docker build \
-f "${REPO_ROOT}/.devcontainer/cuda/Dockerfile" \
-f "${REPO_ROOT}/.devcontainer/Dockerfile" \
-t "${IMAGE_NAME}" \
--build-arg "CUDA_IMAGE=${CUDA_IMAGE}" \
--build-arg "BASE_IMAGE=${CUDA_IMAGE}" \
"${REPO_ROOT}"

echo ">> Compiling (preset=${PRESET}, arch=${CUDA_ARCH})"
Expand All @@ -50,21 +54,24 @@ docker run --rm --user root \
"${IMAGE_NAME}" \
bash -euo pipefail -c '
export CPM_SOURCE_CACHE=/cpm-cache CCACHE_DIR=/root/.ccache
mkdir -p /workspace
# /workspace is a persistent named volume: rsync copies only files
# that changed since the last run (the macOS<->VM file-share is slow,
# so minimizing reads matters) and ninja can then build incrementally.
# The excludes also shield the persistent build/ dir from --delete.
# tests/data is excluded for the same reason: it is cloned by an
# ExternalProject whose stamp lives under build/, so deleting the data
# while keeping the stamp makes the next build fail in gitupdate.cmake
# instead of re-cloning.
echo ">> [1/3] Syncing source into the container (delta copy)..."
time rsync -a --delete \
--exclude=/build \
--exclude=/.git \
--exclude=/.ccache \
--exclude=/tests/data \
--exclude=/docs \
--exclude=/notebooks \
--exclude=/IPCToolkitOptions.cmake \
/src/ /workspace/
rm -f /workspace/IPCToolkitOptions.cmake
cd /workspace
echo ">> [2/3] Configuring (preset=${PRESET})..."
cmake --preset="${PRESET}" -G Ninja \
Expand Down
36 changes: 29 additions & 7 deletions .devcontainer/cuda/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
// CUDA variant of ../devcontainer.json. It shares ../Dockerfile and differs
// only in the base image it selects, the GPU pass-through note, and the
// CMake-on-open setting; keep everything else in step with the default.
{
"$schema": "https://raw.githubusercontent.com/devcontainers/spec/main/schemas/devContainer.schema.json",
"name": "IPCToolkit CUDA Development Container",
"build": {
"dockerfile": "Dockerfile",
// Context is the repository root (two levels up from this file) so the
// build sees the whole project, matching the default dev container.
"context": "../.."
// Shared with the default dev container; BASE_IMAGE is what makes this
// one CUDA-enabled. Context is the repository root (two levels up from
// this file), matching the default.
"dockerfile": "../Dockerfile",
"context": "../..",
"args": {
"BASE_IMAGE": "nvidia/cuda:12.6.2-devel-ubuntu22.04"
}
},
// On a Linux host with the NVIDIA Container Toolkit, uncomment the next line
// to pass the GPU into the container so the CUDA code can actually run.
Expand All @@ -14,15 +21,22 @@
"customizations": {
"vscode": {
"settings": {
"terminal.integrated.shell.linux": "/bin/zsh",
"terminal.integrated.shell.linux": "/bin/fish",
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"C_Cpp.default.intelliSenseMode": "gcc-x64",
"C_Cpp.default.compilerPath": "/usr/bin/clang++-18",
"C_Cpp.clang_format_path": "/usr/bin/clang-format-18",
"C_Cpp.clang_format_style": "file",
// Unlike the default container: opening this one would kick off
// a CUDA configure, which fetches and builds the GPU
// dependencies. Configure when you mean to.
"cmake.configureOnOpen": false,
"cmake.buildDirectory": "${workspaceFolder}/build",
"python.pythonPath": "/usr/bin/python3"
"python.pythonPath": "/usr/bin/python3",
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.formatting.provider": "black",
"prettier.requireConfig": true
},
"extensions": [
"ms-vscode.cpptools",
Expand All @@ -31,10 +45,18 @@
"ms-python.python",
"ms-azuretools.vscode-docker",
"eamodio.gitlens",
"esbenp.prettier-vscode",
"mhutchie.git-graph",
"twxs.cmake",
"jeff-hykin.better-cpp-syntax",
"vadimcn.vscode-lldb",
"ms-python.vscode-pylance"
"cschlosser.doxdocgen",
"ms-python.vscode-pylance",
"mutantdino.resourcemonitor",
"randomfractalsinc.vscode-data-preview",
"oderwat.indent-rainbow",
"formulahendry.code-runner",
"donjayamanne.git-extension-pack"
]
}
},
Expand Down
12 changes: 8 additions & 4 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
# Shrinks the build context uploaded to the Docker daemon. Both dev containers
# (.devcontainer/Dockerfile and .devcontainer/cuda/Dockerfile) use the repo
# root as their build context but never COPY it in (the workspace is mounted at
# runtime), so this only speeds up context transfer — it does not affect image
# contents.
# build .devcontainer/Dockerfile with the repo root as their context but never
# COPY it in (the workspace is mounted at runtime), so this only speeds up
# context transfer — it does not affect image contents.
build/
docs/_build/
# The test data is ~120 MB and carries its own nested .git. It is fetched
# inside the container when it is needed, never from the build context.
tests/data/
.ccache/
*.o
*.so
*.a
__pycache__/
*.pyc
.git/
**/.git/
.venv/
venv/
.DS_Store
7 changes: 6 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,17 @@ exits with the build's status:
./.devcontainer/cuda/build-cuda.sh
```

`PRESET` (default `cuda-release`), `CUDA_ARCH`, and `JOBS` override the
`PRESET` (default `cuda-release`), `CUDA_ARCH` (default `75`, the oldest
architecture we support and so the strictest; a list such as `"75;80;86;89"`
costs one full device codegen pass per entry), and `JOBS` override the
defaults, e.g. `PRESET=test ./.devcontainer/cuda/build-cuda.sh` to build the
CUDA tests too. The source is mounted read-only and rsynced into a named
volume, so a run never writes into your working tree, and later runs are
incremental.

The script builds `.devcontainer/Dockerfile`, the same file the non-CUDA dev
container uses, passing `BASE_IMAGE` to select an nvcc-equipped base.

**This needs a running Docker daemon.** On macOS that means Docker Desktop or
a colima VM (`colima start`); the failure mode otherwise is a confusing
`/var/run/docker.sock` connection error rather than a clear diagnostic. A VM
Expand Down
Loading
Loading