mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-07 18:43:26 -08:00
Sanitizers tell us what a run did wrong; they say nothing about what it never reached. This adds the other half: an instrumented build whose report says which of our own lines no test executes. Add an ENABLE_COVERAGE option that compiles with --coverage -O0 -g, mirroring the ENABLE_SANITIZERS opt-in including the per-source carve-out that keeps vendored trees out of the denominator. -O0 is deliberate: at higher optimisation levels GCC merges and relocates lines, so the report would describe code that no longer exists in that form. Only GCC is supported, since Clang's intermediate format needs llvm-cov; asking for coverage under Clang is a hard error rather than a build that silently produces unreadable .gcno files. Keep the filters in a committed gcovr.cfg rather than in the invocation. The tests themselves are excluded because they are compiled with the same instrumentation and are only ever executed by the run doing the measuring, so leaving them in would report a healthier project than we have. The process entry points and the client's window chrome go too: each needs a display, an interactive terminal or a live database before it does anything a headless test could observe, so they would otherwise sit at 0% forever and bury real movement. Give coverage its own matrix leg rather than a second flag on the sanitizer leg. The two answer different questions, and combining them slows both down without telling us more. The new variant key gives the leg its own Docker image and ccache entry so the two legs cannot race on the same tag. The leg publishes an HTML report with per-file pages, LCOV, Cobertura XML and a JSON summary. gcovr is a report-time dependency rather than a build dependency, so a missing one warns and skips instead of failing an otherwise green build.
194 lines
6 KiB
Bash
194 lines
6 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
|
|
# 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
|