diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..bfe09d9 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +.git +.cache +.mypy_cache +__pycache__ +build +localizer/src/build +localizer/src/install +localizer/cpp +.VSCodeCounter +.vscode +*archive* diff --git a/docker-compose.core.yml b/docker-compose.core.yml new file mode 100644 index 0000000..0713f47 --- /dev/null +++ b/docker-compose.core.yml @@ -0,0 +1,20 @@ +services: + dccc-core: + build: + context: . + dockerfile: docker/Dockerfile.core + args: + USER_UID: "${UID:-1000}" + USER_GID: "${GID:-1000}" + image: dccc-core-dev:latest + working_dir: /workspace/localizer/src + volumes: + - .:/workspace + - dccc-conan-cache:/home/dev/.conan2 + - dccc-cmake-build:/workspace/localizer/src/build + tty: true + stdin_open: true + +volumes: + dccc-conan-cache: + dccc-cmake-build: diff --git a/docker/Dockerfile.core b/docker/Dockerfile.core new file mode 100644 index 0000000..01aaf4a --- /dev/null +++ b/docker/Dockerfile.core @@ -0,0 +1,43 @@ +FROM ubuntu:22.04 + +ARG DEBIAN_FRONTEND=noninteractive +ARG USERNAME=dev +ARG USER_UID=1000 +ARG USER_GID=1000 + +ENV TZ=Etc/UTC \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + CONAN_HOME=/home/${USERNAME}/.conan2 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + cmake \ + curl \ + git \ + ninja-build \ + pkg-config \ + python3 \ + python3-pip \ + python3-venv \ + sudo \ + unzip \ + zlib1g-dev \ + && rm -rf /var/lib/apt/lists/* + +RUN python3 -m pip install --no-cache-dir --default-timeout=120 --retries=10 \ + "conan>=2.0,<3" \ + pandas \ + pytest + +RUN groupadd --gid ${USER_GID} ${USERNAME} \ + && useradd --uid ${USER_UID} --gid ${USER_GID} -m ${USERNAME} -s /bin/bash \ + && echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${USERNAME} \ + && chmod 0440 /etc/sudoers.d/${USERNAME} + +USER ${USERNAME} +WORKDIR /workspace/localizer/src + +RUN conan profile detect --force + +CMD ["/bin/bash"] diff --git a/localizer/src/README.md b/localizer/src/README.md index 9b3ab3f..baf68e8 100644 --- a/localizer/src/README.md +++ b/localizer/src/README.md @@ -143,3 +143,42 @@ Run the ADAD decoupling-based metric: ## Developers To add your own brain PET metric, please refer to the [developer guide](../../docs/developer_guide.md). + +## Docker Development Environment + +The C++ core can be built in a Linux Docker container with Conan and CMake. This is the recommended path for reproducible development across machines. + +Build the development image: + +```bash +docker compose -f docker-compose.core.yml build +``` + +Build and install the C++ core: + +```bash +docker compose -f docker-compose.core.yml run --rm dccc-core /workspace/scripts/docker-build-core.sh +``` + +The installed executable will be written to `localizer/src/install/bin/DCCCcore`. Conan packages and the CMake build tree are kept in Docker volumes so later builds can reuse downloaded and compiled dependencies. The script defaults to `CONAN_CPPSTD=gnu17` on Linux to reuse more Conan Center binaries; use `CONAN_CPPSTD=17` if you need a strict non-GNU C++17 profile. + +The first run can still take a long time because Conan Center may not provide matching Linux binaries for heavy packages such as ITK, ONNX Runtime, Boost, HDF5, GDCM, or TBB. Let the first build finish once on a machine, then keep the Docker volumes for normal incremental development. + +Run the CLI from an interactive shell: + +```bash +docker compose -f docker-compose.core.yml run --rm dccc-core +./install/bin/DCCCcore --help +``` + +Run Python CLI tests after building: + +```bash +docker compose -f docker-compose.core.yml run --rm dccc-core pytest tests +``` + +To force a clean dependency/build cache, remove the Docker volumes: + +```bash +docker compose -f docker-compose.core.yml down -v +``` diff --git a/scripts/docker-build-core.sh b/scripts/docker-build-core.sh new file mode 100755 index 0000000..6301095 --- /dev/null +++ b/scripts/docker-build-core.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "${BASH_SOURCE[0]}")/../localizer/src" + +BUILD_TYPE="${BUILD_TYPE:-Release}" +INSTALL_PREFIX="${INSTALL_PREFIX:-./install}" +CONAN_CPPSTD="${CONAN_CPPSTD:-gnu17}" + +sudo mkdir -p build "${CONAN_HOME:-/home/dev/.conan2}" +sudo chown -R "$(id -u):$(id -g)" build "${CONAN_HOME:-/home/dev/.conan2}" + +conan profile detect --force +conan install . \ + --output-folder=build \ + --build=missing \ + -s build_type="${BUILD_TYPE}" \ + -s compiler.cppstd="${CONAN_CPPSTD}" \ + -c tools.cmake.cmaketoolchain:generator=Ninja \ + -o onetbb/*:tbbmalloc=False \ + -o onetbb/*:tbbproxy=False + +cmake -S . \ + -B build \ + -G Ninja \ + -DCMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake \ + -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ + -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" + +cmake --build build --config "${BUILD_TYPE}" +cmake --install build --config "${BUILD_TYPE}"