dosbox-staging/scripts/compile_commits.sh
2023-11-16 12:13:48 -08:00

238 lines
4.8 KiB
Bash
Executable file

#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-3.0-or-later
#
# Copyright (C) 2023-2023 The DOSBox Staging Team
#
# Compiles all branch commits from the current commit backward.
# It Automatically uses the latest supported version of GCC and Clang,
# building with both compilers if available.
#
# When a failure occurs, the log is printed and the problematic commit
# will be checked out, so you can inspect and amend the commit.
#
# CLI tools used:
# - Bash 3.x or Zsh, for enhanced error handling versus SH
# - GNU ls, for natural-sorting the native files by version
# - GNU sed, to parse the default branch
# - Git, (any version should be fine)
# - All the project's build depenencies (Meson, Ninja, etc)
#
set -euo pipefail
# Common variables that are setup and used below
declare temp_file
declare head_commit
declare current_branch
declare -a commits
declare -a compilers
declare meson_setup
# GNU tools
declare LS
declare SED
populate_gnu_ls() {
# shellcheck disable=2010
if \ls --version 2> /dev/null | grep -q GNU; then
LS=$(command -v ls)
elif command -v gdir > /dev/null; then
LS=$(command -v gdir)
else
echo "Please install corutils with your package manager and then:"
echo " - Adjust your PATH to find GNU 'ls' first, or"
echo " - Symlink 'ls' to your GNU 'ls' binary, or"
echo " - If on macOS, simply 'brew install coreutils' and you're done!"
exit 1
fi
}
populate_gnu_sed() {
if \sed --version 2> /dev/null | grep -q GNU; then
SED=$(command -v sed)
elif command -v gsed > /dev/null; then
SED=$(command -v gsed)
else
echo "Please install GNU sed with your package manager."
echo " - If on macOS, simply 'brew install gnu-sed' and you're done!"
exit 1
fi
}
extract_cpp_from_native_file() {
grep cpp "$1" | cut -d "'" -f 4
}
get_native_file_for_compiler() {
local compiler
compiler="$1"
populate_gnu_ls
local native_file
for native_file in $("$LS" \
--reverse \
--sort=version \
.github/meson/native-"$compiler"*.ini); do
local cpp
cpp=$(extract_cpp_from_native_file "$native_file")
if command -v "$cpp" &> /dev/null; then
echo "$native_file"
return
fi
done
}
allocate_temp_file() {
temp_file=$(mktemp 2> /dev/null || mktemp -t temp)
echo "Realtime log: $temp_file"
# Cleanup the log on termination
trap 'rm -f "$temp_file"' EXIT
}
run() {
# Split the command into an array
local -a cmds
IFS=" " read -r -a cmds <<< "$1"
# Execute the command with arguments
if ! "${cmds[@]}" &> "$temp_file"; then
echo "failed"
echo ""
echo "---"
cat "$temp_file"
echo "---"
echo ""
return 1
fi
return 0
}
populate_commits() {
populate_gnu_sed
current_branch=$(git rev-parse --abbrev-ref HEAD)
local default_branch
default_branch=$(git symbolic-ref refs/remotes/origin/HEAD 2> /dev/null \
| "$SED" 's@^refs/remotes/origin/@@')
if [[ -z $default_branch ]]; then
echo "Failed to determine the default branch."
exit 1
fi
commits=()
while IFS= read -r line; do
commits+=("$line")
done < <(git log --format="%h" HEAD --not "$default_branch" --reverse)
if ((${#commits[@]} <= 1)); then
echo "Branch does not have multiple commits; skipping"
exit 0
fi
head_commit="${commits[0]}"
}
meson_setup_for_compiler() {
local compiler
compiler="$1"
local native_file
native_file=$(get_native_file_for_compiler "$compiler")
if [[ -z $native_file ]]; then
echo "No supported $compiler versions found, skipping"
return 1
fi
echo "Using $compiler native file: $native_file"
local options
options="-Dbuildtype=debug -Dunit_tests=disabled -Dwarning_level=0 -Dc_args=-w -Dcpp_args=-w"
meson_setup="meson setup $options --native-file=$native_file build/$compiler"
if ! run "$meson_setup"; then
echo "Reproduce with:"
echo "$meson_setup"
echo ""
return 1
fi
return 0
}
populate_compilers() {
compilers=()
if meson_setup_for_compiler gcc; then
compilers+=(gcc)
fi
if meson_setup_for_compiler clang; then
compilers+=(clang)
fi
if ((${#compilers[@]} == 0)); then
echo "No supported compilers found, quitting"
exit 1
fi
}
compile_commit() {
local compiler
compiler="$1"
local commit
commit="$2"
local title
local meson_compile
meson_compile="meson compile -C build/$compiler"
if ! run "$meson_compile"; then
echo "Reproduce with:"
echo "git checkout $head_commit"
echo "$meson_setup"
echo "git checkout $commit"
echo "$meson_compile"
exit 1
fi
echo -n " $compiler"
}
main() {
allocate_temp_file
populate_commits
populate_compilers
for commit in "${commits[@]}"; do
git checkout "$commit" &> /dev/null
title=$(git show \
--no-patch \
--format="%s" "$commit")
printf "Compiling %s %-40.40s.." "$commit" "$title"
for compiler in "${compilers[@]}"; do
compile_commit "$compiler" "$commit"
done
echo ""
done
git checkout "$current_branch" &> /dev/null
}
main