mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 09:33:25 -08:00
The bounds in #7667 were set below what one build configuration writes, so ccache filled to its limit and evicted the entries the next build was about to ask for. The probe measured what that costs: a 6G cache on one leg served 1059 of 1059 calls and the 700M bound on the same leg served 14%. The quota also went to legs that finish under the Windows leg, which has no compiler cache. Debug information is most of an object's size. A measured Arch Debug configuration is 1.2 GB with the built-in flags and 660 MB at -g1; Debian 13 Release is 1.1 GB and 614 MB. CI therefore builds every non-tag leg at level 1 through a new COCKATRICE_DEBUG_INFO_LEVEL option, which stays empty by default so local, distro and release builds are unchanged. Level 1 keeps file and line, which is what a failing test or a sanitizer backtrace reports. Tags and the nightly legs keep full DWARF for the Sentry symbols. With one configuration at half the size, the quota goes to fewer legs at a size that holds one whole configuration, and the legs that duplicate another leg's coverage give their cache up: Servatrice, Ubuntu 26.04 behind the sanitize leg of the same distro, macOS 15 and macOS 26 Debug behind macOS 14. Their keys leave LIVE_CCACHE_KEYS, so prune-caches reclaims the existing entries. Fedora 44 built Debug for the suite and Release for the package in one job, so the package compile queued behind the debug one and the leg took 30.8 min - the only leg above Windows - while one 1.6 GB cache held two configurations. It is split into two legs with one cache each, running in parallel. The option reaches compile.sh inside the container through docker.sh, and compile.sh turns it into the CMake flag. Bounds: Arch 850M, Debian 13 800M, Fedora 44 debug 850M and release 800M, Ubuntu 26.04 sanitize 1200M, macOS 14 800M. That is 5.3 GB of compiler cache on top of roughly 4.5 GB of buildkit, vcpkg, Qt and CodeQL entries. Windows still sets the pull-request wall clock: 29.5 min, 23.7 of it compiling with no compiler cache, because ccache and sccache cannot store the 711 of 1054 translation units that use PCH under MSVC (#7381). That is a separate trial.
205 lines
6.7 KiB
Bash
205 lines
6.7 KiB
Bash
#!/bin/bash
|
|
|
|
# This script is to be used by the ci environment from the project root directory, do not use it from somewhere else.
|
|
|
|
# Creates or loads docker images to use in compilation, creates RUN function to start compilation on the docker image.
|
|
#
|
|
# usage: source <script> <name> [--get] [--build] [--save] [--interactive] [--set-cache <location>]
|
|
# <name> sets the name of the docker image, these correspond to directories in .ci
|
|
# --get loads the image from a previously saved image cache, will build if no image is found
|
|
# --build builds the image from the Dockerfile in .ci/$NAME
|
|
# --save stores the image, if an image was loaded it will not be stored
|
|
# --interactive immediately starts the image interactively for debugging
|
|
# --set-cache <location> sets the location to cache the image or for ccache
|
|
#
|
|
# requires: docker
|
|
# uses env: NAME IMAGE_SUFFIX CACHE BUILD GET SAVE INTERACTIVE
|
|
# (correspond to args: <name> --set-cache <cache> --build --get --save --interactive)
|
|
# sets env: RUN CCACHE_DIR IMAGE_NAME RUN_ARGS RUN_OPTS BUILD_SCRIPT
|
|
# exitcode: 1 for failure, 2 for missing dockerfile, 3 for invalid arguments
|
|
#
|
|
# IMAGE_SUFFIX is optional and only appended to the image name, never to NAME, so that a matrix
|
|
# leg can have its own image and image cache without needing its own directory under .ci. Two legs
|
|
# building and tagging the same image concurrently would race on `docker build --tag`.
|
|
# It is deliberately not called SUFFIX: compile.sh already uses SUFFIX for output file names, and
|
|
# a job-level SUFFIX would be silently overridden by those per-step env blocks.
|
|
#
|
|
# exported RUN function will run the BUILD_SCRIPT inside of the docker container.
|
|
# note that the docker container will not inherit any environment variables!
|
|
#
|
|
# usage: RUN [arguments for build script]
|
|
# roughly equivalent to `docker run $IMAGE_NAME bash $BUILD_SCRIPT $@`
|
|
# uses env: CCACHE_DIR IMAGE_NAME RUN_ARGS RUN_OPTS BUILD_SCRIPT
|
|
# exitcode: 3 for invalid arguments, returns the returncode of docker run
|
|
export BUILD_SCRIPT=".ci/compile.sh"
|
|
|
|
project_name="cockatrice"
|
|
save_extension=".tar.gz"
|
|
image_cache="image"
|
|
ccache_cache=".ccache"
|
|
|
|
# Read arguments
|
|
while [[ $# != 0 ]]; do
|
|
case "$1" in
|
|
'--build')
|
|
BUILD=1
|
|
shift
|
|
;;
|
|
'--get')
|
|
GET=1
|
|
shift
|
|
;;
|
|
'--interactive')
|
|
INTERACTIVE=1
|
|
shift
|
|
;;
|
|
'--save')
|
|
SAVE=1
|
|
shift
|
|
;;
|
|
'--set-cache')
|
|
shift
|
|
if [[ $# == 0 ]]; then
|
|
echo "--set-cache expects an argument" >&2
|
|
exit 3
|
|
fi
|
|
CACHE=$1
|
|
shift
|
|
if ! [[ -d $CACHE ]]; then
|
|
echo "could not find cache path: $CACHE" >&2
|
|
return 3
|
|
fi
|
|
;;
|
|
*)
|
|
if [[ ${1:0:1} == - ]]; then
|
|
echo "unrecognized option: $1"
|
|
return 3
|
|
fi
|
|
NAME="$1"
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Setup
|
|
if ! [[ $NAME ]]; then
|
|
echo "no build name given" >&2
|
|
return 3
|
|
fi
|
|
|
|
export IMAGE_NAME="${project_name,,}_${NAME,,}${IMAGE_SUFFIX:+_${IMAGE_SUFFIX,,}}" # lower case
|
|
|
|
docker_dir=".ci/$NAME"
|
|
if ! [[ -r $docker_dir/Dockerfile ]]; then
|
|
echo "could not find Dockerfile in $docker_dir" >&2
|
|
return 2 # even if the image is cached, we do not want to run if there is no way to build this image
|
|
fi
|
|
|
|
if ! [[ $CACHE ]]; then
|
|
echo "cache dir is not set!" >&2
|
|
CACHE="$(mktemp -d)"
|
|
echo "set cache dir to $CACHE" >&2
|
|
fi
|
|
if ! [[ -d $CACHE ]]; then
|
|
echo "could not find cache dir: $CACHE" >&2
|
|
mkdir -p "$CACHE"
|
|
unset GET # the dir is empty
|
|
fi
|
|
if [[ $GET || $SAVE ]]; then
|
|
img_dir="$CACHE/$image_cache"
|
|
img_save="$img_dir/$IMAGE_NAME$save_extension"
|
|
if ! [[ -d $img_dir ]]; then
|
|
echo "could not find image dir: $img_dir" >&2
|
|
mkdir -p "$img_dir"
|
|
fi
|
|
fi
|
|
export CCACHE_DIR="$CACHE/$ccache_cache"
|
|
if ! [[ -d $CCACHE_DIR ]]; then
|
|
echo "could not find ccache dir: $CCACHE_DIR" >&2
|
|
mkdir -p "$CCACHE_DIR"
|
|
fi
|
|
|
|
# Get the docker image from previously stored save
|
|
if [[ $GET ]]; then
|
|
if [[ $img_save ]] && docker load --input "$img_save"; then
|
|
echo "loaded image"
|
|
docker images
|
|
unset BUILD # do not overwrite the loaded image with build
|
|
unset SAVE # do not overwrite the stored image with the same image
|
|
if [[ $(find "$CCACHE_DIR" -type f -print -quit) ]]; then # check contents of ccache
|
|
echo "setting ccache to readonly"
|
|
export RUN_ARGS="$RUN_ARGS -e CCACHE_READONLY=1 -e CCACHE_NOSTATS=1" # do not overwrite ccache
|
|
else
|
|
echo "ccache is empty: $(find "$CCACHE_DIR")" >&2
|
|
fi
|
|
else
|
|
echo "could not load cached image, building instead" >&2
|
|
BUILD=1
|
|
fi
|
|
fi
|
|
|
|
# Build the docker image from dockerfile
|
|
if [[ $BUILD ]]; then
|
|
if docker build --tag "$IMAGE_NAME" "$docker_dir"; then
|
|
echo "built image"
|
|
docker images
|
|
else
|
|
echo "could not build image $IMAGE_NAME" >&2
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Save docker image to cache (compressed)
|
|
if [[ $SAVE ]]; then
|
|
if [[ $img_save ]] && docker save --output "$img_save" "$IMAGE_NAME"; then
|
|
echo "saved image to: $img_save"
|
|
else
|
|
echo "could not save image $IMAGE_NAME" >&2
|
|
fi
|
|
fi
|
|
|
|
# Set compile function, runs the compile script on the image, passes arguments to the script
|
|
# shellcheck disable=2120
|
|
function RUN ()
|
|
{
|
|
echo "running image:"
|
|
if [[ $(docker images) =~ $IMAGE_NAME ]]; then
|
|
local args=(--mount "type=bind,source=$PWD,target=/src")
|
|
args+=(--workdir "/src")
|
|
args+=(--user "$(id -u):$(id -g)")
|
|
if [[ $CCACHE_DIR ]]; then
|
|
args+=(--mount "type=bind,source=$CCACHE_DIR,target=/.ccache")
|
|
args+=(--env "CCACHE_DIR=/.ccache")
|
|
fi
|
|
if [[ $GITHUB_OUTPUT ]]; then
|
|
args+=(--mount "type=bind,source=$GITHUB_OUTPUT,target=/gh_output")
|
|
args+=(--env "GITHUB_OUTPUT=/gh_output")
|
|
fi
|
|
# The container does not inherit host env vars: without this, WITH_CRASH_REPORTING=ON set in
|
|
# the workflow never reaches compile.sh inside the container, so the Linux packages silently
|
|
# build with crash reporting OFF while the libcurl packages in the Dockerfiles go unused.
|
|
if [[ ${WITH_CRASH_REPORTING:-} ]]; then
|
|
args+=(--env "WITH_CRASH_REPORTING=${WITH_CRASH_REPORTING}")
|
|
fi
|
|
# Same reason: the DWARF level reaches compile.sh only if it is handed to the container,
|
|
# and without it CI would cache objects twice the size its bounds were measured for.
|
|
if [[ ${COCKATRICE_DEBUG_INFO_LEVEL:-} ]]; then
|
|
args+=(--env "COCKATRICE_DEBUG_INFO_LEVEL=${COCKATRICE_DEBUG_INFO_LEVEL}")
|
|
fi
|
|
# shellcheck disable=2086
|
|
docker run "${args[@]}" $RUN_ARGS "$IMAGE_NAME" bash "$BUILD_SCRIPT" $RUN_OPTS "$@"
|
|
return $?
|
|
else
|
|
echo "could not find docker image: $IMAGE_NAME" >&2
|
|
return 3
|
|
fi
|
|
}
|
|
|
|
# for debugging, start the docker image interactively instead of building
|
|
# starts immediately, does not require sourcing or RUN
|
|
if [[ $INTERACTIVE ]]; then
|
|
export BUILD_SCRIPT="-i"
|
|
export RUN_ARGS="$RUN_ARGS -it"
|
|
# shellcheck disable=2119
|
|
RUN
|
|
fi
|