cbmk/include/tree.sh
Leah Rowe 3830f3aa54 tree.sh: add sha512 error for check_project_hashes
handle errors on sha512sum - also handle awk errors inside
the mini subshell, and provide overall error handling.

we know that the project.hash file should always exist, and
always be read no matter what; technically, the find command
that proceeds it might not yield any results, but an empty
file would then be produced.

the edge case of an empty file would have lead to an error
beforehand, when configuring the project in function,
configure_project(), so we've already got that covered.

Signed-off-by: Leah Rowe <leah@libreboot.org>
2025-05-28 22:47:15 +01:00

330 lines
9.5 KiB
Bash

# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (c) 2022-2023 Alper Nebi Yasak <alpernebiyasak@gmail.com>
# Copyright (c) 2022 Ferass El Hafidi <vitali64pmemail@protonmail.com>
# Copyright (c) 2023-2025 Leah Rowe <leah@libreboot.org>
eval "`setvars "" xarch srcdir premake gnatdir xlang mode makeargs elfdir cmd \
project target target_dir targets xtree _f release bootstrapargs mkhelper \
autoconfargs listfile autogenargs btype rev build_depend gccdir cmakedir \
defconfig postmake mkhelpercfg dry dest_dir mdir cleanargs gccver gccfull \
gnatver gnatfull do_make badhash tree`"
trees()
{
flags="f:b:m:u:c:x:s:l:n:d:"
while getopts $flags option; do
[ -n "$_f" ] && err "only one flag is permitted"
_f="$1"
case "$_f" in
-d) dry=":" ;;
-b) : ;;
-u) mode="oldconfig" ;;
-m) mode="menuconfig" ;;
-c) mode="distclean" ;;
-x) mode="crossgcc-clean" ;;
-f)
do_make="n"
dry=":" ;;
-s) mode="savedefconfig" ;;
-l) mode="olddefconfig" ;;
-n) mode="nconfig" ;;
*) err "invalid option '-$option'" ;;
esac
if [ -z "${OPTARG+x}" ]; then
shift 1
break
fi
project="${OPTARG#src/}"
project="${project#config/git/}"
shift 2
done
[ -z "$_f" ] && err "missing flag ($flags)"
[ -z "$project" ] && fx_ "x_ ./mk $_f" x_ ls -1 config/git && return 1
e "config/git/$project/pkg.cfg" f missing && err "$project: no pkg.cfg"
for d in "elf" "config/data" "config" "src"; do
eval "${d#*/}dir=\"$d/$project\""
done
dest_dir="$elfdir"
listfile="$datadir/build.list"
[ -f "$listfile" ] || listfile="" # optional on all projects
mkhelpercfg="$datadir/mkhelper.cfg"
if e "$mkhelpercfg" f missing; then
mkhelpercfg="$xbtmp/mkhelper.cfg"
x_ touch "$mkhelpercfg"
fi
targets="$*"
cmd="build_targets $targets"
singletree "$project" && cmd="build_project"
remkdir "${tmpgit%/*}"
}
build_project()
{
configure_project "$configdir" || return 0
[ ! -f "$listfile" ] || $dry elfcheck || return 0
[ "$mode" = "distclean" ] && mode="clean"
run_make_command && [ -z "$mode" ] && $dry copy_elf; :
}
build_targets()
{
[ -d "$configdir" ] || err "directory, $configdir, does not exist"
[ $# -gt 0 ] || targets="$(ls -1 "$configdir")" || err "!o $configdir"
for x in $targets; do
unset CROSS_COMPILE
export PATH="$xbmkpath"
[ "$x" = "list" ] && x_ ls -1 "config/$project" && \
listfile="" && break; target="$x"
printf "'make %s', '%s', '%s'\n" "$mode" "$project" "$target"
x_ handle_defconfig && [ -z "$mode" ] && x_ $postmake; :
done; :
}
handle_defconfig()
{
target_dir="$configdir/$target"
[ -f "CHANGELOG" ] || fetch_project "$project"
configure_project "$target_dir" || return 0
chkvars tree
srcdir="src/$project/$tree"
[ "$mode" = "${mode%clean}" ] && [ ! -d "$srcdir" ] && return 0
[ -z "$mode" ] && for _xarch in $xarch; do
$dry check_cross_compiler "$_xarch"
done; :
for y in "$target_dir/config"/*; do
[ "$_f" = "-d" ] || [ -f "$y" ] || continue
[ "$_f" = "-d" ] || defconfig="$y"
[ -n "$mode" ] || check_defconfig || continue
handle_makefile && [ -z "$mode" ] && $dry copy_elf; :
done; :
}
configure_project()
{
eval "`setvars "" cleanargs build_depend autoconfargs xtree postmake \
makeargs btype mkhelper bootstrapargs premake release xlang xarch \
badhash`"
_tcfg="$1/target.cfg" && [ ! -f "$_tcfg" ] && btype="auto"
e "$datadir/mkhelper.cfg" f && eval "`setcfg "$datadir/mkhelper.cfg"`"
while e "$_tcfg" f || [ "$cmd" != "build_project" ]; do
eval "`setvars "" rev tree`"
eval "`setcfg "$_tcfg"`"
printf "Loading %s config: %s\n" "$project" "$_tcfg"
[ "$_f" = "-d" ] && build_depend="" # dry run
[ "$cmd" = "build_project" ] && break
[ "$do_make" != "n" ] && break
[ "${_tcfg%/*/target.cfg}" = "${_tcfg%"/$tree/target.cfg"}" ] \
&& break; _tcfg="${_tcfg%/*/target.cfg}/$tree/target.cfg"
done
[ "$XBMK_RELEASE" = "y" ] && [ "$release" = "n" ] && return 1
[ -z "$btype" ] || [ "${mode%config}" = "$mode" ] || return 1
[ -z "$mode" ] && $dry build_dependencies
mdir="$xbmkpwd/config/submodule/$project"
[ -n "$tree" ] && mdir="$mdir/$tree"
[ -f "CHANGELOG" ] || check_project_hashes
if [ "$do_make" = "n" ]; then
[ -f "CHANGELOG" ] || fetch_${cmd#build_}
return 1
fi
x_ ./mk -f "$project" "$target"
}
build_dependencies()
{
for bd in $build_depend; do
bd_p="${bd%%/*}"
bd_t="${bd##*/}"
[ -z "$bd_p" ] && $dry err "$project/$tree: !bd '$bd'"
[ "${bd##*/}" = "$bd" ] && bd_t=""
[ -z "$bd_p" ] || $dry x_ ./mk -b $bd_p $bd_t; :
done; :
}
check_project_hashes()
{
old_pjhash="" && x_ mkdir -p "$XBMK_CACHE/hash"
[ ! -f "$XBMK_CACHE/hash/$project$tree" ] || \
read -r old_pjhash < "$XBMK_CACHE/hash/$project$tree" || \
err "old_pjhash: Can't read '$XBMK_CACHE/hash/$project$tree'"
fx_ "x_ sha512sum" find "$datadir" "$configdir/$tree" "$mdir" \
-type f -not -path "*/.git*/*" | awk '{print $1}' > \
"$xbtmp/project.hash" || err "!h $project $tree"
pjhash="$(x_ sha512sum "$xbtmp/project.hash" | awk '{print $1}' || \
err)" || err "pjhash: Can't read sha512 of '$xbtmp/project.hash'"
[ "$pjhash" != "$old_pjhash" ] && badhash="y"
[ -f "$XBMK_CACHE/hash/$project$tree" ] || badhash="y"
printf "%s\n" "$pjhash" > "$XBMK_CACHE/hash/$project$tree" || err
[ "$badhash" != "y" ] || x_ rm -Rf "src/$project/$tree" \
"elf/$project/$tree" "elf/$project/$target"; :
}
check_cross_compiler()
{
cbdir="src/coreboot/$tree"
[ "$project" != "coreboot" ] && cbdir="src/coreboot/default"
[ -n "$xtree" ] && cbdir="src/coreboot/$xtree"
x_ ./mk -f coreboot "${cbdir#src/coreboot/}"
export PATH="$xbmkpwd/$cbdir/util/crossgcc/xgcc/bin:$PATH"
export CROSS_COMPILE="${xarch% *}-"
[ -n "$xlang" ] && export BUILD_LANGUAGES="$xlang"
# match gnat-X to gcc
check_gnu_path gcc gnat || x_ check_gnu_path gnat gcc
xfix="${1%-*}" && [ "$xfix" = "x86_64" ] && xfix="x64"
xgccargs="crossgcc-$xfix UPDATED_SUBMODULES=1 CPUS=$XBMK_THREADS"
make -C "$cbdir" $xgccargs || x_ make -C "$cbdir" $xgccargs
# we only want to mess with hostcc to build xgcc
remkdir "$XBMK_CACHE/gnupath"
}
# fix mismatching gcc/gnat versions on debian trixie/sid. as of december 2024,
# trixie/sid had gnat-13 as gnat and gcc-14 as gcc, but has gnat-14 in apt. in
# some cases, gcc 13+14 and gnat-13 are present; or gnat-14 and gcc-14, but
# gnat in PATH never resolves to gnat-14, because gnat-14 was "experimental"
check_gnu_path()
{
command -v "$1" 1>/dev/null || err "Host '$1' unavailable"
eval "`setvars "" gccver gccfull gnatver gnatfull gccdir gnatdir`"
x_ gnu_setver "$1" "$1" || err "Command '$1' unavailable."
gnu_setver "$2" "$2" || :
eval "[ -z \"\$$1ver\" ] && err \"Cannot detect host '$1' version\""
[ "$gnatfull" = "$gccfull" ] && return 0
eval "$1dir=\"$(dirname "$(command -v "$1")")\""
eval "_gnudir=\"\$$1dir\"; _gnuver=\"\$$1ver\""
for _bin in "$_gnudir/$2-"*; do
[ "${_bin#"$_gnudir/$2-"}" = "$_gnuver" ] && [ -x "$_bin" ] \
&& _gnuver="${_bin#"$_gnudir/$2-"}" && break; :
done
gnu_setver "$2" "$_gnudir/$2-$_gnuver" || return 1
[ "$gnatfull" = "$gccfull" ] || return 1
(
remkdir "$XBMK_CACHE/gnupath" && x_ cd "$XBMK_CACHE/gnupath"
for _gnubin in "$_gnudir/$2"*"-$_gnuver"; do
_gnuutil="${_gnubin##*/}" && [ -e "$_gnubin" ] && \
x_ ln -s "$_gnubin" "${_gnuutil%"-$_gnuver"}"
done
) || err "Cannot create $2-$_gnuver link in $_gnudir"; :
}
gnu_setver()
{
eval "$2 --version 1>/dev/null 2>/dev/null || return 1"
eval "$1ver=\"`"$2" --version 2>/dev/null | head -n1`\""
eval "$1ver=\"\${$1ver##* }\""
eval "$1full=\"\$$1ver\""
eval "$1ver=\"\${$1ver%%.*}\""; :
}
check_defconfig()
{
[ -f "$defconfig" ] || $dry err "$project/$target: missing defconfig"
dest_dir="$elfdir/$target/${defconfig#"$target_dir/config/"}"
$dry elfcheck || return 1; : # skip build if a previous one exists
}
elfcheck()
{
# TODO: *STILL* very hacky check. do it properly (based on build.list)
( fx_ "eval exit 1 && err" find "$dest_dir" -type f ) || return 1; :
}
handle_makefile()
{
$dry check_makefile "$srcdir" && \
$dry x_ make -C "$srcdir" $cleanargs clean
[ -f "$defconfig" ] && x_ cp "$defconfig" "$srcdir/.config"
run_make_command || err "handle_makefile $srcdir: no makefile!"
_copy=".config" && [ "$mode" = "savedefconfig" ] && _copy="defconfig"
[ "${mode%config}" = "$mode" ] || \
$dry x_ cp "$srcdir/$_copy" "$defconfig"
[ -e "$srcdir/.git" ] && [ "$project" = "u-boot" ] && \
[ "$mode" = "distclean" ] && \
$dry x_ git -C "$srcdir" $cleanargs clean -fdx; :
}
run_make_command()
{
[ -n "$mode" ] || x_ $premake
$dry check_cmake "$srcdir"
[ -n "$mode" ] || ( $dry check_autoconf "$srcdir" ) || err
$dry check_makefile "$srcdir" || return 1
$dry x_ make -C "$srcdir" $mode -j$XBMK_THREADS $makeargs
[ -n "$mode" ] || x_ $mkhelper
check_makefile "$srcdir" || return 0
[ "$mode" != "clean" ] || \
$dry make -C "$srcdir" $cleanargs distclean || \
$dry x_ make -C "$srcdir" $cleanargs clean; :
}
check_cmake()
{
[ -z "$cmakedir" ] || $dry check_makefile "$1" || cmake -B "$1" \
"$1/$cmakedir" || $dry x_ check_makefile "$1"
[ -z "$cmakedir" ] || $dry x_ check_makefile "$1"; :
}
check_autoconf()
{
x_ cd "$1"
[ -f "bootstrap" ] && x_ ./bootstrap $bootstrapargs
[ -f "autogen.sh" ] && x_ ./autogen.sh $autogenargs
[ -f "configure" ] && x_ ./configure $autoconfargs; :
}
check_makefile()
{
[ -f "$1/Makefile" ] || [ -f "$1/makefile" ] || \
[ -f "$1/GNUmakefile" ] || return 1; :
}
copy_elf()
{
[ -f "$listfile" ] && x_ mkdir -p "$dest_dir"
[ ! -f "$listfile" ] || while read -r f; do
[ -f "$srcdir/$f" ] && x_ cp "$srcdir/$f" "$dest_dir"; :
done < "$listfile" || err "copy_elf $*: cannot read '$listfile'"; :
x_ make clean -C "$srcdir" $cleanargs
}