mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
Merge topic 'devcontainer'
f1b6b3a6f0devcontainer: Run local customization hooks through the container's life3cfbea10e4devcontainer: Name the workspace path explicitlyf8f4dc7f43gitlab-ci: add a job to verify the devcontainer in CIea0667e779ci: add scripts to run and verify the devcontainer9c17332601devcontainer: Add `clang-tidy`, `clang-tools`, and `clazy`a8872df649devcontainer: Add `clang`, `gfortran`, and `valgrind`8bd053379fdevcontainer: Reword the comment on caching package downloads7ae5c3cfb2devcontainer: Add the Kitware APT repository ... Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !12423
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
# The image build sends this directory to the container engine as its build
|
||||
# context. The state hooks keep at run time is of no use to a build and,
|
||||
# having been written inside the container, may well be owned by a user the
|
||||
# build cannot even read it as. Leave it behind.
|
||||
#
|
||||
# See `Help/dev/devcontainer.rst`.
|
||||
state/
|
||||
@@ -0,0 +1 @@
|
||||
/state/
|
||||
@@ -0,0 +1,83 @@
|
||||
# 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
|
||||
# the `clang-format` version our style rules require and the Kitware APT
|
||||
# repository through which CMake itself is published.
|
||||
ARG BASE_IMAGE=ubuntu:26.04
|
||||
|
||||
FROM ${BASE_IMAGE}
|
||||
|
||||
ARG USERNAME=cmake-dev
|
||||
ARG USER_UID=1000
|
||||
ARG USER_GID=${USER_UID}
|
||||
|
||||
# The SHA-256 of the signing key `apt.kitware.com` publishes. Kitware rotates
|
||||
# that key every few years; updating this hash both approves the new key and
|
||||
# forces the step below to fetch it again rather than reuse a cached layer.
|
||||
# Read the current value with:
|
||||
# curl -fsSL https://apt.kitware.com/keys/kitware-archive-latest.asc | sha256sum
|
||||
ARG KITWARE_PUBLIC_KEY_SHA256=801bc629e356c3c96f184351272914222ce427777400fa7d1baed3ab180b3e3b
|
||||
|
||||
# Add the Kitware APT repository, which carries CMake releases newer than the
|
||||
# ones the distribution provides, before installing anything from it below.
|
||||
RUN --mount=type=bind,source=install_kitware_archive.sh,target=/root/install_kitware_archive.sh \
|
||||
--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_kitware_archive.sh ${KITWARE_PUBLIC_KEY_SHA256}
|
||||
|
||||
# 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. A rebuild then 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
|
||||
|
||||
# Install the GitLab CLI, `glab`, and `glab-axi`, an agent-ergonomic wrapper
|
||||
# around it, for interacting with our GitLab instance. Keep the packages
|
||||
# `npm` downloads in a cache, and unpack the `glab` release in a `tmpfs`, so
|
||||
# that neither leaves anything behind in the image.
|
||||
RUN --mount=type=bind,source=install_glab.sh,target=/root/install_glab.sh \
|
||||
--mount=type=cache,target=/root/.npm,sharing=locked \
|
||||
--mount=type=tmpfs,target=/tmp \
|
||||
sh /root/install_glab.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 hook for the build, if the developer
|
||||
# has written one. It is 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`.
|
||||
#
|
||||
# It runs as the container user, who may `sudo`, rather than as `root`: one
|
||||
# hook that can reach either is simpler to write against than two that each
|
||||
# reach one. Mount it beside the dispatcher that runs it, under the same
|
||||
# parent it has in the source tree, so that the dispatcher locates it here the
|
||||
# same way it does when a container lifecycle command runs it.
|
||||
#
|
||||
# `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 the hook runs and whose home it writes to, so that it may spell a path
|
||||
# relative to either.
|
||||
USER ${USERNAME}
|
||||
WORKDIR /home/${USERNAME}
|
||||
RUN --mount=type=bind,source=run-hooks.sh,target=/opt/cmake-dev/run-hooks.sh \
|
||||
--mount=type=bind,source=hooks,target=/opt/cmake-dev/hooks \
|
||||
HOME=/home/${USERNAME} sh /opt/cmake-dev/run-hooks.sh build
|
||||
Executable
+38
@@ -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"
|
||||
@@ -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`.
|
||||
@@ -0,0 +1,70 @@
|
||||
# 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. `g++` comes from `deps_packages.lst`, as CI builds with it;
|
||||
# `clang` is here for developers who prefer to build with it instead.
|
||||
#
|
||||
# `gfortran` is named only to keep the Fortran-only `gcc-16` build that
|
||||
# Ubuntu 26.04 carries out of the container: `libopenmpi-dev` lists
|
||||
# `gfortran-16` first among the alternatives it accepts, and Clang, which
|
||||
# selects the newest GCC installation it finds, cannot link against one that
|
||||
# provides no C++ standard library.
|
||||
ccache
|
||||
clang
|
||||
cmake
|
||||
gfortran
|
||||
ninja-build
|
||||
|
||||
# Debug and diagnose CMake. The sanitizer runtimes come with the compilers,
|
||||
# so building with `-fsanitize=` needs nothing installed here.
|
||||
gdb
|
||||
valgrind
|
||||
|
||||
# Analyze CMake. `clazy` is the compiler our Clazy CI job builds with,
|
||||
# `clang-tools` provides `scan-build`, and `clang-tidy` runs the checks in
|
||||
# `.clang-tidy`. Name the version of `clang-tidy` our checks are written
|
||||
# against: `Utilities/ClangTidyModule` tracks the Clang release our CI image
|
||||
# carries, and older versions report `cmake-use-cmsys-fstream` where they
|
||||
# should not. Ubuntu's unversioned `clang-tidy` package is a release behind.
|
||||
# `clazy` and `clang-tools` are whatever versions Ubuntu carries, so expect
|
||||
# their diagnostics to differ from those jobs'. CMake's own checks are not
|
||||
# among them either way: `CMake_USE_CLANG_TIDY_MODULE` needs
|
||||
# `Utilities/ClangTidyModule` built against Clang's development files, which
|
||||
# are not installed here.
|
||||
clang-tidy-22
|
||||
clang-tools
|
||||
clazy
|
||||
|
||||
# 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
|
||||
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"name": "CMake",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile"
|
||||
},
|
||||
"remoteUser": "cmake-dev",
|
||||
// Name the workspace path explicitly rather than take the default a tool
|
||||
// picks for itself, so that the path is the same under every tool and in
|
||||
// the CI job of `.gitlab/ci/devcontainer-run.sh`, which mounts the source
|
||||
// tree itself. `create_user.sh` creates the directory and gives it to the
|
||||
// container user.
|
||||
"workspaceFolder": "/home/cmake-dev/workspace",
|
||||
"workspaceMount": "source=${localWorkspaceFolder},target=/home/cmake-dev/workspace,type=bind",
|
||||
"containerEnv": {
|
||||
"GITLAB_HOST": "gitlab.kitware.com"
|
||||
},
|
||||
"remoteEnv": {
|
||||
"GITLAB_CLIENT_ID": "${localEnv:GITLAB_CLIENT_ID}",
|
||||
"GITLAB_TOKEN": "${localEnv:GITLAB_TOKEN}"
|
||||
},
|
||||
"mounts": [
|
||||
{
|
||||
"type": "volume",
|
||||
"source": "cmake-dev-ccache",
|
||||
"target": "/home/cmake-dev/.cache/ccache"
|
||||
},
|
||||
{
|
||||
"type": "volume",
|
||||
"source": "cmake-dev-glab-cli",
|
||||
"target": "/home/cmake-dev/.config/glab-cli"
|
||||
}
|
||||
],
|
||||
// Run the optional local customization hooks. Each phase is a no-op unless
|
||||
// the developer has written that hook. See `Help/dev/devcontainer.rst`.
|
||||
//
|
||||
// Each is written as a named entry, the form that runs entries concurrently
|
||||
// and says which one is speaking. Only `postAttachCommand` has two, but
|
||||
// naming the hook everywhere keeps its output labeled the same way, and
|
||||
// leaves room for a second entry beside it.
|
||||
//
|
||||
// `initializeCommand` runs on the host, so unlike the others it needs a
|
||||
// POSIX shell there; naming `sh` explicitly, in the form that starts no
|
||||
// shell of its own, is what makes that work outside a Unix host.
|
||||
"initializeCommand": {
|
||||
"hooks": [
|
||||
"sh",
|
||||
"${localWorkspaceFolder}/.devcontainer/run-hooks.sh",
|
||||
"initialize"
|
||||
]
|
||||
},
|
||||
"postCreateCommand": {
|
||||
"hooks": [
|
||||
"${containerWorkspaceFolder}/.devcontainer/run-hooks.sh",
|
||||
"post-create"
|
||||
]
|
||||
},
|
||||
"postStartCommand": {
|
||||
"hooks": [
|
||||
"${containerWorkspaceFolder}/.devcontainer/run-hooks.sh",
|
||||
"post-start"
|
||||
]
|
||||
},
|
||||
"postAttachCommand": {
|
||||
"setup-status": ["${containerWorkspaceFolder}/.devcontainer/setup-status.sh"],
|
||||
"hooks": [
|
||||
"${containerWorkspaceFolder}/.devcontainer/run-hooks.sh",
|
||||
"post-attach"
|
||||
]
|
||||
},
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/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. `CMakeLists.txt`
|
||||
# searches for `clang-tidy` only under the unversioned name, so the version
|
||||
# installed by name needs one too.
|
||||
ln -s "$(command -v clang-format-18)" /usr/local/bin/clang-format
|
||||
ln -s "$(command -v clang-tidy-22)" /usr/local/bin/clang-tidy
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Install the GitLab CLI, `glab`, and `glab-axi`, an agent-ergonomic wrapper
|
||||
# around it. Neither is provided by the distribution.
|
||||
# See `Help/dev/devcontainer.rst`.
|
||||
|
||||
set -e
|
||||
|
||||
readonly glab_version="1.114.0"
|
||||
readonly glab_axi_version="0.6.0"
|
||||
|
||||
case "$(uname -m)" in
|
||||
x86_64)
|
||||
arch="amd64"
|
||||
sha256sum="00e892a80d586a1e8b8fdc035321923db99dce0caa3b0c4fd72c5337ffdb1c48"
|
||||
;;
|
||||
aarch64)
|
||||
arch="arm64"
|
||||
sha256sum="d34d7ddb96ce5e5f3423d7e8053cb14c36bd93984e4b96320f7e20a341b83498"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported architecture: $(uname -m)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
readonly filename="glab_${glab_version}_linux_${arch}.tar.gz"
|
||||
readonly baseurl="https://gitlab.com/gitlab-org/cli/-/releases/v${glab_version}/downloads"
|
||||
|
||||
cd /tmp
|
||||
curl -L -o "$filename" "$baseurl/$filename"
|
||||
echo "$sha256sum $filename" > glab.sha256sum
|
||||
sha256sum --check glab.sha256sum
|
||||
tar -C /usr/local -xzf "$filename" bin/glab
|
||||
rm "$filename" glab.sha256sum
|
||||
|
||||
# Enable shell completion for interactive use.
|
||||
mkdir -p /etc/bash_completion.d
|
||||
/usr/local/bin/glab completion --shell bash > /etc/bash_completion.d/glab
|
||||
|
||||
# `glab-axi` is distributed only through npm. Its command surface is
|
||||
# documented at https://axi.md.
|
||||
npm install --global --no-audit --no-fund "glab-axi@${glab_axi_version}"
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Add the Kitware APT repository, which carries CMake releases newer than the
|
||||
# ones the distribution provides. See `Help/dev/devcontainer.rst`.
|
||||
|
||||
set -e
|
||||
|
||||
readonly key_sha256="$1"
|
||||
|
||||
if test -z "$key_sha256"; then
|
||||
echo "usage: $0 <sha256-of-kitware-archive-key>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install without asking questions.
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# `VERSION_CODENAME` names the suite the repository provides for the
|
||||
# distribution the container is based on.
|
||||
. /etc/os-release
|
||||
|
||||
readonly sources=/etc/apt/sources.list.d/kitware.sources
|
||||
readonly keyring=/usr/share/keyrings/kitware-archive-keyring
|
||||
readonly key_url=https://apt.kitware.com/keys/kitware-archive-latest.asc
|
||||
|
||||
# Describe the repository, verified with the keyring named as the argument.
|
||||
write_sources() {
|
||||
cat > "$sources" <<EOF
|
||||
Types: deb
|
||||
URIs: https://apt.kitware.com/ubuntu/
|
||||
Suites: ${VERSION_CODENAME}
|
||||
Components: main
|
||||
Signed-By: $1
|
||||
EOF
|
||||
}
|
||||
|
||||
apt-get update
|
||||
|
||||
# The base image carries neither `curl` nor a certificate store, and neither
|
||||
# the key nor the repository can be reached without one: both redirect HTTP
|
||||
# to HTTPS.
|
||||
apt-get install -y ca-certificates curl
|
||||
|
||||
# Trust the repository with the key it publishes, checked against the hash our
|
||||
# caller pins, just long enough to install `kitware-archive-keyring`. Once
|
||||
# that package provides the key, `apt` follows the rotations Kitware makes to
|
||||
# it each year, which a pinned hash would not. `apt` reads an armored key
|
||||
# only from a `.asc` file, and the package provides a `.gpg` one, so name the
|
||||
# file each step uses accordingly.
|
||||
curl -fsSL -o "$keyring.asc" "$key_url"
|
||||
echo "$key_sha256 $keyring.asc" | sha256sum --check
|
||||
write_sources "$keyring.asc"
|
||||
apt-get update
|
||||
apt-get install -y kitware-archive-keyring
|
||||
write_sources "$keyring.gpg"
|
||||
rm "$keyring.asc"
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Run the optional local customization hook for one phase of the development
|
||||
# container's life, named as the sole argument, if the developer has written
|
||||
# one. See `Help/dev/devcontainer.rst`.
|
||||
#
|
||||
# The hooks live beside this script, in a directory Git ignores in its
|
||||
# entirety, so customizations never appear in a commit and survive updates to
|
||||
# the tracked container definition.
|
||||
|
||||
set -eu
|
||||
|
||||
readonly phase="$1"
|
||||
readonly devcontainer_dir="$(cd -- "$(dirname -- "$0")" && pwd)"
|
||||
readonly hooks_dir="$devcontainer_dir/hooks"
|
||||
readonly hook="$hooks_dir/$phase.sh"
|
||||
|
||||
test -f "$hook" || exit 0
|
||||
|
||||
# Tell the hook where its own directory is, so that a hook needing a file it
|
||||
# brought along need not work out where it was installed.
|
||||
CMAKE_DEVCONTAINER_HOOKS_DIR="$hooks_dir"
|
||||
export CMAKE_DEVCONTAINER_HOOKS_DIR
|
||||
|
||||
# A failed `build` hook fails the image build: the image must be reproducible,
|
||||
# and a customization that did not apply would leave it quietly wrong. Every
|
||||
# other phase runs against a container that already exists, where the same
|
||||
# strictness would turn a typo in a personal hook into an environment its
|
||||
# author can no longer open in order to fix it. Report and carry on instead.
|
||||
if test "$phase" = build; then
|
||||
# The build sees this directory through a read-only bind mount, and keeps
|
||||
# nothing a later phase could read back: whatever this hook writes it
|
||||
# writes into the image. So there is no state directory to offer it.
|
||||
exec sh -e "$hook"
|
||||
fi
|
||||
|
||||
# Every other phase runs against the bind-mounted source tree, where a hook
|
||||
# may keep state that outlives the container. It sits beside the hooks rather
|
||||
# than among them: the hooks are written by hand and worth carrying to another
|
||||
# clone, while this is written by whatever they start and worth carrying
|
||||
# nowhere. `.dockerignore` also leaves it out of the build context, which a
|
||||
# hook writing here as `root` would otherwise make unreadable to the build.
|
||||
CMAKE_DEVCONTAINER_STATE_DIR="$devcontainer_dir/state"
|
||||
export CMAKE_DEVCONTAINER_STATE_DIR
|
||||
mkdir -p "$CMAKE_DEVCONTAINER_STATE_DIR"
|
||||
|
||||
sh -e "$hook" || echo "run-hooks.sh: $phase hook failed; continuing" >&2
|
||||
Executable
+58
@@ -0,0 +1,58 @@
|
||||
#!/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 <<MESSAGE
|
||||
git: this work tree is not set up for development.
|
||||
Run 'Utilities/SetupForDevelopment.sh' to configure your Git identity and
|
||||
install the project's commit hooks. The work tree is shared with the host,
|
||||
so running it here sets up both.
|
||||
MESSAGE
|
||||
fi
|
||||
|
||||
readonly host="${GITLAB_HOST:-gitlab.com}"
|
||||
|
||||
if glab auth status --hostname "$host" > /dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# The OAuth flows need the application ID of an OAuth application registered
|
||||
# on the instance. Offer them only when one is available, and prefer the
|
||||
# device flow because it needs no redirect back into the container.
|
||||
if test -n "${GITLAB_CLIENT_ID:-}"; then
|
||||
readonly login="glab auth login --hostname $host --device"
|
||||
readonly hint=""
|
||||
else
|
||||
readonly login="glab auth login --hostname $host"
|
||||
readonly hint="
|
||||
Setting GITLAB_CLIENT_ID, on the host, to the application ID of a GitLab
|
||||
OAuth application offers to sign in through that application instead.
|
||||
"
|
||||
fi
|
||||
|
||||
cat <<MESSAGE
|
||||
glab: no credential for $host yet.
|
||||
Set one up in either of two ways:
|
||||
|
||||
* Run this in the container, which stores the credential under
|
||||
~/.config/glab-cli, on a volume that persists across rebuilds:
|
||||
|
||||
$login
|
||||
|
||||
* Or set GITLAB_TOKEN, on the host, to a GitLab personal access token
|
||||
before starting the container. It is passed through automatically.
|
||||
$hint
|
||||
Run '.devcontainer/setup-status.sh' here in the container to check again.
|
||||
MESSAGE
|
||||
@@ -3,6 +3,7 @@
|
||||
.clang-format export-ignore
|
||||
.clang-tidy export-ignore
|
||||
.codespellrc export-ignore
|
||||
.devcontainer export-ignore
|
||||
.editorconfig export-ignore
|
||||
.pre-commit-config.yaml export-ignore
|
||||
.typos.toml export-ignore
|
||||
|
||||
@@ -193,6 +193,16 @@ l:clazy-fedora44:
|
||||
- .rules
|
||||
needs: []
|
||||
|
||||
t:devcontainer-fedora44:
|
||||
extends:
|
||||
- .fedora44
|
||||
- .cmake_test_devcontainer
|
||||
- .linux_x86_64_priv_tags
|
||||
- .rules
|
||||
variables:
|
||||
CMAKE_CI_JOB_NIGHTLY: "true"
|
||||
needs: []
|
||||
|
||||
# Coverage builds
|
||||
|
||||
b:fedora44-gcc-gcov:
|
||||
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
# This job runs in a container whose filesystem is `overlayfs`, over which
|
||||
# `podman` cannot stack its own `overlay` storage driver:
|
||||
#
|
||||
# 'overlay' is not supported over overlayfs, a mount_program is required
|
||||
#
|
||||
# `vfs` copies each layer rather than stacking it, which is slower but asks
|
||||
# nothing of the filesystem underneath. Name it in the environment so that
|
||||
# every `podman` command below addresses the same storage, the build included.
|
||||
export STORAGE_DRIVER=vfs
|
||||
|
||||
readonly dockerfile=".devcontainer/Dockerfile"
|
||||
readonly name="cmake-dev-container"
|
||||
|
||||
# Build the container image from the devcontainer Dockerfile
|
||||
echo "# Building devcontainer image"
|
||||
podman build -t "$name" -f "$dockerfile" .devcontainer
|
||||
|
||||
# Ensure named volumes exist for ccache and glab-cli cache persistence
|
||||
echo "# Ensuring volumes exist"
|
||||
podman volume create cmake-dev-ccache 2>/dev/null || true
|
||||
podman volume create cmake-dev-glab-cli 2>/dev/null || true
|
||||
|
||||
# Run the container with volumes mounted.
|
||||
#
|
||||
# The job's own container has no cgroup controllers delegated to it, so
|
||||
# `podman` cannot place the container it starts under one:
|
||||
#
|
||||
# crun: controller `pids` is not available under /sys/fs/cgroup/...
|
||||
#
|
||||
# Ask for no cgroup at all. Verification needs no resource limits, and there
|
||||
# is nothing to limit them with. There is no terminal either, so do not ask
|
||||
# for one.
|
||||
echo "# Starting devcontainer"
|
||||
podman run --rm \
|
||||
--cgroups=disabled \
|
||||
-v "$PWD:/home/cmake-dev/workspace:Z" \
|
||||
-v cmake-dev-ccache:/home/cmake-dev/.cache/ccache:Z \
|
||||
-v cmake-dev-glab-cli:/home/cmake-dev/.config/glab-cli:Z \
|
||||
--workdir /home/cmake-dev/workspace \
|
||||
"$name" \
|
||||
.gitlab/ci/devcontainer-verify.sh
|
||||
Executable
+75
@@ -0,0 +1,75 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
# Source CI environment scripts
|
||||
. .gitlab/ci/env.sh
|
||||
|
||||
# Verify devcontainer volumes are accessible
|
||||
echo "# Verifying devcontainer volumes"
|
||||
|
||||
# Check ccache volume
|
||||
if test -d /home/cmake-dev/.cache/ccache; then
|
||||
echo "ccache volume found at /home/cmake-dev/.cache/ccache"
|
||||
ccache -s 2>/dev/null || echo "ccache stats unavailable (may be fresh)"
|
||||
else
|
||||
echo "WARNING: ccache volume not found at expected path"
|
||||
fi
|
||||
|
||||
# Check glab-cli volume
|
||||
if test -d /home/cmake-dev/.config/glab-cli; then
|
||||
echo "glab-cli volume found at /home/cmake-dev/.config/glab-cli"
|
||||
else
|
||||
echo "WARNING: glab-cli volume not found at expected path"
|
||||
fi
|
||||
|
||||
# Verify cmake is available
|
||||
echo "# Verifying cmake"
|
||||
cmake --version
|
||||
|
||||
# Verify ccache is available
|
||||
echo "# Verifying ccache"
|
||||
ccache --version
|
||||
|
||||
# Build a simple test project to verify the devcontainer works
|
||||
echo "# Building test project"
|
||||
|
||||
# Create a temporary build directory
|
||||
mkdir -p /tmp/devcontainer-test
|
||||
cd /tmp/devcontainer-test
|
||||
|
||||
# Create a minimal C project
|
||||
cat > CMakeLists.txt <<cmake
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(devcontainer-test C)
|
||||
|
||||
add_executable(test
|
||||
main.c
|
||||
)
|
||||
cmake
|
||||
|
||||
cat > main.c <<cmake
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
printf("Devcontainer CMake build OK\n");
|
||||
return 0;
|
||||
}
|
||||
cmake
|
||||
|
||||
# Configure with cmake using ccache cache path
|
||||
echo "# Configuring with cmake"
|
||||
cmake 2>&1 \
|
||||
-GNinja \
|
||||
-S. \
|
||||
-Bbuild
|
||||
|
||||
# Build
|
||||
echo "# Building"
|
||||
ninja -C build 2>&1
|
||||
|
||||
# Verify cache was preserved
|
||||
echo "# Verifying ccache hit"
|
||||
ccache -s 2>/dev/null | head -5 || true
|
||||
|
||||
echo "# Devcontainer verification complete"
|
||||
@@ -657,6 +657,13 @@
|
||||
- docker
|
||||
- linux-x86_64
|
||||
|
||||
.linux_x86_64_priv_tags:
|
||||
tags:
|
||||
- cmake
|
||||
- docker
|
||||
- linux-x86_64
|
||||
- privileged
|
||||
|
||||
.linux_x86_64_v3_tags:
|
||||
tags:
|
||||
- cmake
|
||||
@@ -969,3 +976,11 @@
|
||||
-DCMake_SPHINX_CMAKE_ORG_OUTDATED=$CMAKE_CI_SPHINX_OUTDATED
|
||||
-DCMake_VERSION_NO_GIT=$CMAKE_CI_VERSION_NO_GIT
|
||||
- ninja
|
||||
|
||||
### Devcontainer testing
|
||||
|
||||
.cmake_test_devcontainer:
|
||||
stage: test
|
||||
script:
|
||||
- dnf install -y --setopt=install_weak_deps=False podman
|
||||
- .gitlab/ci/devcontainer-run.sh
|
||||
|
||||
@@ -26,6 +26,8 @@ To contribute patches:
|
||||
#. Fork the upstream `CMake Repository`_ into a personal account.
|
||||
#. Run `Utilities/SetupForDevelopment.sh`_ for local git configuration.
|
||||
#. See `Building CMake`_ for building CMake locally.
|
||||
Optionally, see the `CMake Dev Container Guide`_ for a ready-made
|
||||
development environment.
|
||||
#. See the `CMake Source Code Guide`_ for coding guidelines
|
||||
and the `CMake Testing Guide`_ for testing instructions.
|
||||
#. Create a topic branch named suitably for your work.
|
||||
@@ -52,6 +54,7 @@ preparing or submitting a change.
|
||||
.. _`CMake Repository`: https://gitlab.kitware.com/cmake/cmake
|
||||
.. _`Utilities/SetupForDevelopment.sh`: Utilities/SetupForDevelopment.sh
|
||||
.. _`Building CMake`: README.rst#building-cmake
|
||||
.. _`CMake Dev Container Guide`: Help/dev/devcontainer.rst
|
||||
.. _`CMake Source Code Guide`: Help/dev/source.rst
|
||||
.. _`CMake Testing Guide`: Help/dev/testing.rst
|
||||
.. _`commit messages`: Help/dev/review.rst#commit-messages
|
||||
|
||||
@@ -36,6 +36,7 @@ Developer Documentation
|
||||
|
||||
CMake developer documentation is provided by the following documents:
|
||||
|
||||
* The `CMake Dev Container Guide`_.
|
||||
* The `CMake Source Code Guide`_.
|
||||
* The `CMake Documentation Guide`_.
|
||||
* The `CMake Testing Guide`_.
|
||||
@@ -43,6 +44,7 @@ CMake developer documentation is provided by the following documents:
|
||||
* The `CMake Debugging Guide`_.
|
||||
* The `CMake Diagnostics Guide`_.
|
||||
|
||||
.. _`CMake Dev Container Guide`: devcontainer.rst
|
||||
.. _`CMake Source Code Guide`: source.rst
|
||||
.. _`CMake Documentation Guide`: documentation.rst
|
||||
.. _`CMake Testing Guide`: testing.rst
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
CMake Dev Container Guide
|
||||
*************************
|
||||
|
||||
The following is a guide to the development container provided for building,
|
||||
testing, and formatting CMake itself. See documentation on `CMake
|
||||
Development`_ for more information.
|
||||
|
||||
.. _`CMake Development`: README.rst
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
The `.devcontainer`_ directory at the top of the CMake source tree describes
|
||||
a Linux development environment following the `Dev Container Specification`_.
|
||||
Using it is entirely optional, but it offers a quick way to get a complete
|
||||
environment with all the tools needed to build CMake, run its test suite,
|
||||
build its documentation, and satisfy its style rules.
|
||||
|
||||
The container is built on Ubuntu, which offers the broadest ecosystem of
|
||||
packages and tooling for development. Its package lists mirror those of the
|
||||
Debian image our CI infrastructure uses, described under
|
||||
`.gitlab/ci/docker`_, so the dependencies available closely match the ones
|
||||
against which merge requests are tested. A few pieces of the CI environment
|
||||
are left out because a development container rarely needs them, and each is
|
||||
noted in the list that would otherwise carry it:
|
||||
|
||||
`.devcontainer/deps_packages.lst`_
|
||||
The packages needed to build CMake, run its test suite, and build its
|
||||
documentation.
|
||||
|
||||
`.devcontainer/dev_packages.lst`_
|
||||
The packages that make the container a comfortable place to work in, which
|
||||
building and testing CMake does not itself need.
|
||||
|
||||
.. _`.devcontainer`: ../../.devcontainer
|
||||
.. _`Dev Container Specification`: https://containers.dev
|
||||
.. _`.gitlab/ci/docker`: ../../.gitlab/ci/docker
|
||||
.. _`.devcontainer/deps_packages.lst`: ../../.devcontainer/deps_packages.lst
|
||||
.. _`.devcontainer/dev_packages.lst`: ../../.devcontainer/dev_packages.lst
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
* A container engine such as `Docker`_ or `Podman`_. Building the image uses
|
||||
bind and cache mounts, as the image builds under `.gitlab/ci/docker`_ do, so
|
||||
it needs `BuildKit`_, enabled by default since Docker 23.0, or Podman 4.0 or
|
||||
newer.
|
||||
|
||||
* A tool that understands the specification, such as the `Dev Containers`_
|
||||
extension for Visual Studio Code, the `Dev Container CLI`_, or another
|
||||
`supporting tool`_.
|
||||
|
||||
.. _`Docker`: https://docs.docker.com/get-started/get-docker/
|
||||
.. _`Podman`: https://podman.io
|
||||
.. _`BuildKit`: https://docs.docker.com/build/buildkit/
|
||||
.. _`Dev Containers`: https://code.visualstudio.com/docs/devcontainers/containers
|
||||
.. _`Dev Container CLI`: https://github.com/devcontainers/cli
|
||||
.. _`supporting tool`: https://containers.dev/supporting
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
In Visual Studio Code, open the CMake source tree and run the
|
||||
``Dev Containers: Reopen in Container`` command. With the
|
||||
`Dev Container CLI`_, start the container from the top of the source tree:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ devcontainer up --workspace-folder .
|
||||
$ devcontainer exec --workspace-folder . bash
|
||||
|
||||
The source tree is mounted into the container, so changes made inside it are
|
||||
made to the same working tree. Commits may be created either inside or
|
||||
outside the container. `Utilities/SetupForDevelopment.sh`_ may likewise be
|
||||
run in either place to configure your Git identity and install the project's
|
||||
commit hooks, and takes effect in both. It is interactive, so the container
|
||||
does not run it automatically, but `.devcontainer/setup-status.sh`_ reports
|
||||
whether it still needs to be run each time a tool attaches to the container.
|
||||
|
||||
.. _`Utilities/SetupForDevelopment.sh`: ../../Utilities/SetupForDevelopment.sh
|
||||
.. _`.devcontainer/setup-status.sh`: ../../.devcontainer/setup-status.sh
|
||||
|
||||
Build CMake in the container as one would on any other Linux host, as
|
||||
described in `Building CMake`_:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cmake -G Ninja -B build -S .
|
||||
$ cmake --build build
|
||||
$ ctest --test-dir build
|
||||
|
||||
.. _`Building CMake`: ../../README.rst#building-cmake
|
||||
|
||||
Provided Tools
|
||||
==============
|
||||
|
||||
In addition to the compiler and the external dependencies CMake can build
|
||||
against, the container provides:
|
||||
|
||||
* ``cmake`` and ``ninja``, to build CMake with. ``cmake`` comes from the
|
||||
`Kitware APT repository`_, which the container configures, so it is the
|
||||
latest CMake release rather than the older one Ubuntu carries, and
|
||||
``apt-get`` offers each new release as it is published:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ sudo apt-get update
|
||||
$ sudo apt-get install --only-upgrade cmake
|
||||
|
||||
The repository also carries release candidates, in a suite named after the
|
||||
Ubuntu release with ``-rc`` appended. Add it to the ``Suites`` field of
|
||||
``/etc/apt/sources.list.d/kitware.sources`` to install those as well.
|
||||
|
||||
* ``clang``, for developers who would rather build with it than with the
|
||||
default ``g++``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cmake -G Ninja -B build-clang -S . -DCMAKE_CXX_COMPILER=clang++
|
||||
|
||||
* ``ccache``, to speed up repeated builds, e.g.:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cmake -G Ninja -B build -S . -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
Its cache is stored in a named volume so that it survives rebuilds of the
|
||||
container.
|
||||
|
||||
* ``clang-format`` version 18, exactly as required by our `C++ Code Style`_,
|
||||
available as both ``clang-format`` and ``clang-format-18``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ Utilities/Scripts/clang-format.bash --modified
|
||||
|
||||
* ``pre-commit``, to run the checks configured in
|
||||
`.pre-commit-config.yaml`_:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ pre-commit install
|
||||
$ pre-commit run --all-files
|
||||
|
||||
* ``sphinx-build``, to build the documentation as described in the
|
||||
`CMake Documentation Guide`_.
|
||||
|
||||
* ``gdb``, to debug CMake as described in the `CMake Debugging Guide`_.
|
||||
|
||||
* ``valgrind``, and the sanitizer runtimes that come with ``g++`` and
|
||||
``clang``, to run CMake and its tests under a memory checker, the way the
|
||||
sanitizer and Valgrind jobs of our CI do:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cmake -G Ninja -B build-asan -S . \
|
||||
-DCMAKE_C_FLAGS=-fsanitize=address \
|
||||
-DCMAKE_CXX_FLAGS=-fsanitize=address
|
||||
$ cmake --build build-asan
|
||||
$ ctest --test-dir build-asan
|
||||
|
||||
* ``clang-tidy``, ``scan-build``, and ``clazy``, the compiler our Clazy CI
|
||||
job builds with, to analyze CMake rather than only compile it:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cmake -G Ninja -B build-tidy -S . -DCMake_RUN_CLANG_TIDY=ON
|
||||
$ cmake -G Ninja -B build-clazy -S . -DCMAKE_CXX_COMPILER=clazy
|
||||
|
||||
``clang-tidy`` is the version our checks are written against, which is
|
||||
not the one Ubuntu's unversioned package provides. ``scan-build`` and
|
||||
``clazy`` are whatever versions Ubuntu carries rather than the ones our
|
||||
CI image does, so expect their diagnostics to differ from those jobs'.
|
||||
CMake's own checks are not available either way:
|
||||
``CMake_USE_CLANG_TIDY_MODULE`` needs `Utilities/ClangTidyModule`_ built
|
||||
against Clang's development files, which the container does not install.
|
||||
|
||||
* ``glab``, the `GitLab CLI`_, to work with merge requests, issues, and
|
||||
pipelines on our GitLab instance, and `glab-axi`_, a wrapper around it
|
||||
whose output follows the `AXI`_ conventions:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ glab mr list
|
||||
$ glab-axi mr view 1234
|
||||
|
||||
See `GitLab Authentication`_ below for the one-time setup they need.
|
||||
|
||||
.. _`Kitware APT repository`: https://apt.kitware.com
|
||||
.. _`C++ Code Style`: source.rst#c-code-style
|
||||
.. _`.pre-commit-config.yaml`: ../../.pre-commit-config.yaml
|
||||
.. _`Utilities/ClangTidyModule`: ../../Utilities/ClangTidyModule
|
||||
.. _`CMake Documentation Guide`: documentation.rst
|
||||
.. _`CMake Debugging Guide`: debug.rst
|
||||
.. _`GitLab CLI`: https://docs.gitlab.com/editor_extensions/gitlab_cli/
|
||||
.. _`glab-axi`: https://github.com/karotkriss/glab-axi
|
||||
.. _`AXI`: https://axi.md
|
||||
|
||||
The base image ships without documentation, but the container keeps the man
|
||||
pages and other documentation of every package installed on top of it. Run
|
||||
``sudo unminimize`` to restore the documentation of the packages the base
|
||||
image itself provides.
|
||||
|
||||
GitLab Authentication
|
||||
=====================
|
||||
|
||||
The container sets ``GITLAB_HOST`` to ``gitlab.kitware.com`` so that ``glab``
|
||||
and ``glab-axi`` address our GitLab instance by default. Both still need a
|
||||
credential for it. `.devcontainer/setup-status.sh`_ reports whether a
|
||||
working credential has been configured and provides instructions to do so if
|
||||
not. It is run automatically when attaching to the container.
|
||||
|
||||
A ``GITLAB_TOKEN`` or ``GITLAB_CLIENT_ID`` set on the host is passed through
|
||||
to the container, so a credential configured outside it is used as-is.
|
||||
|
||||
Local Customization
|
||||
===================
|
||||
|
||||
The container is meant to be an unconstrained space that each developer may
|
||||
adapt. `.devcontainer/run-hooks.sh`_ runs an optional script, if one is
|
||||
present, at each of five points in the container's life:
|
||||
|
||||
``.devcontainer/hooks/initialize.sh``
|
||||
Runs on the host, before the container is created or started, e.g. to
|
||||
prepare something the container goes on to use.
|
||||
|
||||
``.devcontainer/hooks/build.sh``
|
||||
Runs while the image is built, e.g. to install additional packages.
|
||||
|
||||
``.devcontainer/hooks/post-create.sh``
|
||||
Runs once, when the container is created, and unlike ``build.sh`` runs with
|
||||
the source tree mounted, e.g. to prepare something in the work tree itself.
|
||||
|
||||
``.devcontainer/hooks/post-start.sh``
|
||||
Runs each time the container starts, e.g. to start a background service.
|
||||
Note that a container may be started by a tool that never attaches to it.
|
||||
|
||||
``.devcontainer/hooks/post-attach.sh``
|
||||
Runs each time a tool attaches to the container, concurrently with the
|
||||
report described under `GitLab Authentication`_ above rather than before or
|
||||
after it, so expect whatever it prints to interleave with that report.
|
||||
|
||||
``build.sh`` runs as the container user, in that user's home directory, rather
|
||||
than as ``root``; reach for ``sudo`` for whatever needs privilege. One hook
|
||||
that can be either user is simpler to write against than two that each can be
|
||||
one. Bear in mind that ``sudo`` resets ``HOME`` to ``root``'s, so pass ``-H``
|
||||
or ``-E`` where a command cares which home it writes to. The three
|
||||
container hooks that follow it likewise run as the container user, in the
|
||||
workspace directory; ``post-start.sh`` and ``post-attach.sh`` run again on
|
||||
every start and attach, so write those two to be repeatable.
|
||||
|
||||
Each hook is given ``CMAKE_DEVCONTAINER_HOOKS_DIR``, naming the ``hooks``
|
||||
directory itself, so that a hook needing a file it brought along need not work
|
||||
out where it was installed. Every hook but ``build.sh`` is given
|
||||
``CMAKE_DEVCONTAINER_STATE_DIR`` as well, a directory to keep runtime state
|
||||
in: it is part of the source tree, bind-mounted from the host, so what a hook
|
||||
leaves there outlives the container. It sits beside the ``hooks`` directory
|
||||
rather than inside it, because the two are worth different things: hooks are
|
||||
written by hand and worth carrying to another clone, while state is written by
|
||||
whatever they start and worth carrying nowhere.
|
||||
`.devcontainer/.dockerignore`_ also keeps it out of the image build context,
|
||||
which state written as ``root`` would otherwise make unreadable. ``build.sh``
|
||||
is given neither a state directory nor a writable ``hooks`` directory, because
|
||||
a build keeps nothing a later phase could read back: whatever it writes, it
|
||||
writes into the image.
|
||||
|
||||
A failing ``build.sh`` fails the image build, because an image whose
|
||||
customizations did not apply is quietly wrong. The other three are reported
|
||||
and otherwise ignored: they run against a container that already exists, where
|
||||
the same strictness would turn a typo into an environment its author can no
|
||||
longer open in order to fix it.
|
||||
|
||||
The whole ``hooks`` directory is ignored by Git, apart from its
|
||||
``.gitignore``, so customizations never appear in a commit, may bring along
|
||||
whatever other files they need, and are preserved across updates to the
|
||||
tracked container definition. For example, to add a package, a shell alias,
|
||||
and a service that runs for as long as the container does:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cat > .devcontainer/hooks/build.sh <<'EOF'
|
||||
sudo apt-get update && sudo apt-get install -y tmux
|
||||
echo "alias b='cmake --build build'" >> ~/.bashrc
|
||||
EOF
|
||||
$ cat > .devcontainer/hooks/post-start.sh <<'EOF'
|
||||
pidof my-service > /dev/null ||
|
||||
my-service --daemon --state "$CMAKE_DEVCONTAINER_STATE_DIR/my-service"
|
||||
EOF
|
||||
|
||||
Rebuild the container to apply a new or changed ``build.sh``, e.g. with the
|
||||
``Dev Containers: Rebuild Container`` command in Visual Studio Code. The
|
||||
other three hooks are read afresh each time they run.
|
||||
|
||||
``initialize.sh`` is the one hook that runs outside the container, so it is
|
||||
also the one that depends on the host: it needs ``sh`` on the ``PATH`` there.
|
||||
That is a given on a Unix host and, on Windows, comes with Git for Windows.
|
||||
|
||||
Some things a container needs must be settled before it exists, and so cannot
|
||||
come from a hook: added capabilities, extra mounts, `Dev Container Features`_,
|
||||
and arguments to the container engine all belong to
|
||||
`.devcontainer/devcontainer.json`_. Those, and any larger or longer-lived
|
||||
change, may of course be made by editing that file or
|
||||
`.devcontainer/Dockerfile`_ directly, but take care not to commit them
|
||||
accidentally.
|
||||
|
||||
.. _`.devcontainer/run-hooks.sh`: ../../.devcontainer/run-hooks.sh
|
||||
.. _`.devcontainer/.dockerignore`: ../../.devcontainer/.dockerignore
|
||||
.. _`Dev Container Features`: https://containers.dev/features
|
||||
.. _`.devcontainer/Dockerfile`: ../../.devcontainer/Dockerfile
|
||||
.. _`.devcontainer/devcontainer.json`: ../../.devcontainer/devcontainer.json
|
||||
Reference in New Issue
Block a user