diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000000..aa7968152e --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,59 @@ +# syntax=docker/dockerfile:1 + +# Base image for the CMake development container. +# +# The images our CI infrastructure uses, described under `.gitlab/ci/docker/`, +# are built on the distributions we test CMake against and are minimized for +# CI use. Prepare the environment from scratch on Ubuntu instead: it offers +# the broadest ecosystem of packages and tooling for development, including a +# recent `cmake` and the `clang-format` version our style rules require. +ARG BASE_IMAGE=ubuntu:26.04 + +FROM ${BASE_IMAGE} + +ARG USERNAME=cmake-dev +ARG USER_UID=1000 +ARG USER_GID=${USER_UID} + +# Install the packages needed to build CMake, run its test suite, build its +# documentation, and satisfy its style rules, along with a few more that make +# the container a comfortable place to work. +# +# Cache the package lists and the downloaded archives, and hide the +# `docker-clean` configuration the base image provides so that it does not +# discard them, so that rebuilding the container downloads only what has +# changed since the last build. +RUN --mount=type=bind,source=install_deps.sh,target=/root/install_deps.sh \ + --mount=type=bind,source=deps_packages.lst,target=/root/deps_packages.lst \ + --mount=type=bind,source=dev_packages.lst,target=/root/dev_packages.lst \ + --mount=type=bind,source=docker-clean,target=/etc/apt/apt.conf.d/docker-clean \ + --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \ + --mount=type=cache,target=/var/cache/apt,sharing=locked \ + sh /root/install_deps.sh + +# Create an unprivileged user matching the typical host account so that files +# created in the mounted source tree are not owned by `root`. +RUN --mount=type=bind,source=create_user.sh,target=/root/create_user.sh \ + sh /root/create_user.sh ${USERNAME} ${USER_UID} ${USER_GID} + +# Run the optional local customization scripts. They are ignored by Git so +# that developers may customize the container without modifying tracked files +# or risking that the customizations end up in a commit. See +# `Help/dev/devcontainer.rst`. +# +# `USER` sets neither the working directory nor `HOME`, and BuildKit passes a +# `RUN` only the environment the image records, which names just `PATH`. Say +# where each hook runs and whose home it writes to, so that a hook may spell a +# path relative to either. +WORKDIR /root +RUN --mount=type=bind,source=hooks,target=/opt/cmake-dev-hooks \ + if test -f /opt/cmake-dev-hooks/root.sh; then \ + HOME=/root sh -e /opt/cmake-dev-hooks/root.sh; \ + fi + +USER ${USERNAME} +WORKDIR /home/${USERNAME} +RUN --mount=type=bind,source=hooks,target=/opt/cmake-dev-hooks \ + if test -f /opt/cmake-dev-hooks/user.sh; then \ + HOME=/home/${USERNAME} sh -e /opt/cmake-dev-hooks/user.sh; \ + fi diff --git a/.devcontainer/create_user.sh b/.devcontainer/create_user.sh new file mode 100755 index 0000000000..fad12bcdd7 --- /dev/null +++ b/.devcontainer/create_user.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +# Create the unprivileged user that the container runs as, given its name, +# uid, and gid. See `Help/dev/devcontainer.rst`. + +set -e + +readonly username="$1" +readonly uid="$2" +readonly gid="$3" + +# The base image already ships an unprivileged user, which usually occupies +# the uid we want. Remove whoever holds it, and the group holding our gid, +# before creating ours. +if getent passwd "$uid" > /dev/null; then + userdel --remove "$(getent passwd "$uid" | cut -d: -f1)" +fi +if getent group "$gid" > /dev/null; then + groupdel "$(getent group "$gid" | cut -d: -f1)" +fi + +groupadd --gid "$gid" "$username" +useradd --uid "$uid" --gid "$gid" --create-home --shell /bin/bash "$username" + +# Let the user administer the container, e.g. to install more packages. +echo "$username ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/$username" +chmod 0440 "/etc/sudoers.d/$username" + +# Pre-create the directories that `devcontainer.json` mounts volumes over so +# that the volumes inherit the ownership recorded here. Name the parents too: +# `install -d` records the ownership only of the directories it is given, and +# tools that write elsewhere under them need to own them as well. +install -d -o "$username" -g "$username" \ + "/home/$username/.cache" \ + "/home/$username/.cache/ccache" \ + "/home/$username/.config" \ + "/home/$username/.config/glab-cli" \ + "/home/$username/workspace" diff --git a/.devcontainer/deps_packages.lst b/.devcontainer/deps_packages.lst new file mode 100644 index 0000000000..f3b4654b11 --- /dev/null +++ b/.devcontainer/deps_packages.lst @@ -0,0 +1,126 @@ +# Packages needed to build CMake, run its test suite, and build its +# documentation. +# +# This list mirrors `.gitlab/ci/docker/debian13-x86_64/deps_packages.lst`, +# section by section, so that the two are easy to compare as our +# dependencies evolve. Package names differ where Ubuntu names them +# differently, and entries needed only by our CI infrastructure are left out +# with a note saying so. +# +# Additions for personal use belong in `.devcontainer/hooks/root.sh`. +# See `Help/dev/devcontainer.rst`. + +locales + +# Install build requirements. +libssl-dev + +# Install development tools. +g++ +curl +git + +# The base image carries no certificate store, which `curl` and `apt` need +# to reach anything over HTTPS. +ca-certificates + +# Install optional external build dependencies. +libarchive-dev +libbz2-dev +libcurl4-gnutls-dev +libexpat1-dev +libjsoncpp-dev +liblzma-dev +libncurses-dev +librhash-dev +libuv1-dev +libzstd-dev +zlib1g-dev + +# NOTE `include-what-you-use` is not provided here. CI builds it from +# source, which is more than a development container needs. + +# Tools needed for the test suite. +jq + +# Packages needed to test CTest. +brz +cvs +subversion +mercurial + +# Install ASM_NASM language toolchain. +nasm + +# NOTE The HIP language toolchain, `hipcc`, is not provided here. It is +# large and rarely needed while working on CMake itself. + +# NOTE The IAR compiler is not provided here, so neither are the packages it +# depends on. + +# Packages needed to test find modules. +alsa-utils +aspell +aspell-en +doxygen graphviz +freeglut3-dev +libgnutls28-dev +libarchive-dev +libaspell-dev +libblas-dev +libboost-dev +libboost-filesystem-dev +libboost-program-options-dev +libboost-python-dev +libboost-thread-dev +libbz2-dev +libcups2-dev +libcurl4-gnutls-dev +libdevil-dev +libfontconfig1-dev +libfreetype-dev +libgdal-dev +libgif-dev +libgl1-mesa-dev +libglew-dev +libgmock-dev +libgrpc++-dev libgrpc-dev +libgsl-dev +libgtest-dev +libgtk2.0-dev +libhdf5-dev +libhdf5-mpich-dev +libhdf5-openmpi-dev +libicu-dev +libinput-dev +libjpeg-dev +libjsoncpp-dev +liblapack-dev +liblzma-dev +libmagick++-dev +libopenal-dev +libopenmpi-dev openmpi-bin +libosp-dev +libpng-dev +libpq-dev postgresql-server-dev-18 +libprotobuf-dev libprotobuf-c-dev libprotoc-dev protobuf-compiler protobuf-compiler-grpc +libsdl1.2-dev +libsqlite3-dev +libtiff-dev +libuv1-dev +libwxgtk3.2-dev +libx11-dev +libxalan-c-dev +libxerces-c-dev +libxml2-dev libxml2-utils +libxslt1-dev xsltproc +openjdk-25-jdk +python3 python3-dev python3-numpy pypy3 pypy3-dev python3-venv +qtbase5-dev qtbase5-dev-tools +rbenv ruby-build +ruby ruby-dev +swig +unixodbc-dev + +# NOTE The packages needed to test ironpython are not provided here. Ubuntu +# no longer carries `libmono-system-windows-forms4.0-cil`. diff --git a/.devcontainer/dev_packages.lst b/.devcontainer/dev_packages.lst new file mode 100644 index 0000000000..5f3286d6f1 --- /dev/null +++ b/.devcontainer/dev_packages.lst @@ -0,0 +1,44 @@ +# Packages that make the container a comfortable place to work in, but that +# building CMake, running its test suite, and building its documentation do +# not need, and so are not part of `deps_packages.lst`. +# +# Additions for personal use belong in `.devcontainer/hooks/root.sh`. +# See `Help/dev/devcontainer.rst`. + +# Make the shell pleasant to work in interactively. `unminimize` restores +# the documentation of the packages the base image provides, which it ships +# without; see `Help/dev/devcontainer.rst`. +bash-completion +less +man-db +manpages +manpages-dev +nano +unminimize +vim + +# Build CMake. +ccache +cmake +ninja-build + +# Debug CMake. +gdb + +# Build the documentation. +python3-sphinx + +# Satisfy our style rules. Our `.clang-format` requires `clang-format` +# version 18, exactly, so install that version by name. Do not install the +# unversioned `clang-format` package: it provides a much newer version. +clang-format-18 +pre-commit + +# Run `glab` and `glab-axi`, and reach GitLab over SSH. `nodejs` and `npm` +# are needed both to install `glab-axi` and to run it. +nodejs +npm +openssh-client + +# Let the unprivileged container user install more packages. +sudo diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..c16cea876a --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,30 @@ +{ + "name": "CMake", + "build": { + "dockerfile": "Dockerfile" + }, + "remoteUser": "cmake-dev", + "mounts": [ + { + "type": "volume", + "source": "cmake-dev-ccache", + "target": "/home/cmake-dev/.cache/ccache" + } + ], + "postAttachCommand": "${containerWorkspaceFolder}/.devcontainer/setup-status.sh", + "customizations": { + "vscode": { + "extensions": [ + "EditorConfig.EditorConfig", + "ms-vscode.cmake-tools", + "ms-vscode.cpptools", + "lextudio.restructuredtext", + "llvm-vs-code-extensions.vscode-clangd" + ], + "settings": { + "C_Cpp.clang_format_path": "/usr/bin/clang-format-18", + "cmake.configureOnOpen": false + } + } + } +} diff --git a/.devcontainer/docker-clean b/.devcontainer/docker-clean new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.devcontainer/hooks/.gitignore b/.devcontainer/hooks/.gitignore new file mode 100644 index 0000000000..347b930096 --- /dev/null +++ b/.devcontainer/hooks/.gitignore @@ -0,0 +1,5 @@ +# Local customizations applied while building the development container. +# Everything here is ignored, except this file, so that customizations may +# bring along whatever files they need. See `Help/dev/devcontainer.rst`. +/* +!/.gitignore diff --git a/.devcontainer/install_deps.sh b/.devcontainer/install_deps.sh new file mode 100755 index 0000000000..3b80f4ff77 --- /dev/null +++ b/.devcontainer/install_deps.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# Install the packages listed in `deps_packages.lst` and `dev_packages.lst`. +# See `Help/dev/devcontainer.rst`. + +set -e + +# Install without asking questions, e.g. the time zone `tzdata` wants. +export DEBIAN_FRONTEND=noninteractive + +# Unlike our CI images, keep the documentation that packages carry: the base +# image excludes man pages and the like, which is not what one wants in a +# development environment. Packages already installed in the base image +# remain without their documentation; `unminimize` restores that. +rm -f /etc/dpkg/dpkg.cfg.d/excludes + +apt-get update +apt-get install -y $(grep -h '^[^#]\+$' /root/deps_packages.lst /root/dev_packages.lst) + +# Add locales, the way our CI images do, for the tests that need them. +sed -i -E '/^# en_US[ .](ISO-8859-1|UTF-8)( |$)/ s/^# //' /etc/locale.gen +dpkg-reconfigure --frontend=noninteractive locales + +# `Utilities/Scripts/clang-format.bash` finds `clang-format-18` by name, but +# make the unversioned name resolve to version 18 as well so that tools +# looking for it get the version our style rules require. +ln -s "$(command -v clang-format-18)" /usr/local/bin/clang-format diff --git a/.devcontainer/setup-status.sh b/.devcontainer/setup-status.sh new file mode 100755 index 0000000000..92ae3ad8b6 --- /dev/null +++ b/.devcontainer/setup-status.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +# Report whether this clone and its development container are ready to use +# and, if they are not, print what remains to be set up. Run each time a +# tool attaches to the container. See `Help/dev/devcontainer.rst`. + +set -u + +cd "$(dirname "$0")/.." + +# Check that development setup is up-to-date, the way our `pre-commit` hook +# does. `Utilities/SetupForDevelopment.sh` is interactive, so the container +# cannot run it on the developer's behalf. +eval "$(grep '^SetupForDevelopment_VERSION=' Utilities/SetupForDevelopment.sh)" +setup_done=$(git config --get hooks.SetupForDevelopment || echo 0) +if test "$setup_done" -lt "${SetupForDevelopment_VERSION:-0}"; then + cat < .devcontainer/hooks/root.sh <<'EOF' + apt-get update && apt-get install -y tmux + EOF + $ cat > .devcontainer/hooks/user.sh <<'EOF' + echo "alias b='cmake --build build'" >> ~/.bashrc + EOF + +Rebuild the container to apply them, e.g. with the +``Dev Containers: Rebuild Container`` command in Visual Studio Code. + +Larger or longer-lived changes may of course be made by editing +`.devcontainer/Dockerfile`_ or `.devcontainer/devcontainer.json`_ directly, +but take care not to commit them accidentally. + +.. _`.devcontainer/Dockerfile`: ../../.devcontainer/Dockerfile +.. _`.devcontainer/devcontainer.json`: ../../.devcontainer/devcontainer.json