238 lines
4.8 KiB
Bash
Executable file
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
|