mirror of
https://codeberg.org/canoeboot/cbmk.git
synced 2025-01-10 00:59:56 +00:00
1b1dae36d2
we already check the python version, and set a variable for it, so that we can reliably use python3, even if python in PATH doesn't correspond to python3. for example if a system has python as python2 and python3 as python3 well, we use that when running deguard for example, but various upstream projects that we use may need python, and all of them use python3, not 2 so, re-use the python variable set up by lbmk, and set it up in PATH accordingly. this now makes the note about python3 obsolete, on docs/build.md in lbwww.git Signed-off-by: Leah Rowe <leah@libreboot.org>
358 lines
11 KiB
Bash
Executable file
358 lines
11 KiB
Bash
Executable file
#!/usr/bin/env sh
|
|
# 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>
|
|
|
|
set -u -e
|
|
|
|
. "include/lib.sh"
|
|
. "include/git.sh"
|
|
|
|
XBMKPATH="$PATH"
|
|
|
|
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 tree rev tree_depend build_depend \
|
|
defconfig postmake mkhelpercfg dry dest_dir mdir cleanargs gccver gccfull \
|
|
gnatver gnatfull gccdir cmakedir`"; badhash="n"
|
|
|
|
main()
|
|
{
|
|
while getopts f:b:m:u:c:x:s:l:n:d: option; do
|
|
[ -n "$_f" ] && $err "only one flag is permitted"
|
|
_f="$1" && [ "$_f" = "-d" ] && dry=":"
|
|
case "$1" in
|
|
-d) mode="" ;;
|
|
-b) mode="" ;;
|
|
-u) mode="oldconfig" ;;
|
|
-m) mode="menuconfig" ;;
|
|
-c) mode="distclean" ;;
|
|
-x) mode="crossgcc-clean" ;;
|
|
-f) mode="fetch" ;;
|
|
-s) mode="savedefconfig" ;;
|
|
-l) mode="olddefconfig" ;;
|
|
-n) mode="nconfig" ;;
|
|
*) $err "invalid option '-$option'" ;;
|
|
esac
|
|
[ -z "${OPTARG+x}" ] && shift 1 && break
|
|
project="${OPTARG#src/}"; shift 2
|
|
done
|
|
[ -z "$_f" ] && $err "missing flag (-m/-u/-b/-c/-x/-f/-s/-l/-n)"
|
|
[ -z "$project" ] && mk $_f $(ls -1 config/git) && return 1
|
|
|
|
[ -f "config/git/$project/pkg.cfg" ] || $err "'$project' not defined"
|
|
|
|
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"
|
|
e "$mkhelpercfg" f missing && mkhelpercfg="$TMPDIR/mkhelper.cfg" && \
|
|
x_ touch "$mkhelpercfg"
|
|
|
|
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 || return 0
|
|
|
|
[ -n "$mode" ] || $dry copy_elf; return 0
|
|
}
|
|
|
|
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
|
|
[ -n "$mode" ] || [ -z "$postmake" ] || $postmake || \
|
|
$err "$project/$target: !postmake: $postmake"; continue
|
|
done; return 0
|
|
}
|
|
|
|
handle_defconfig()
|
|
{
|
|
target_dir="$configdir/$target"
|
|
|
|
[ -f "CHANGELOG" ] || fetch_project "$project"
|
|
configure_project "$target_dir" || return 0
|
|
x_ mkdir -p "$elfdir/$target"
|
|
|
|
chkvars tree; srcdir="src/$project/$tree"
|
|
|
|
if [ "$mode" = "distclean" ] || [ "$mode" = "crossgcc-clean" ]; then
|
|
[ -d "$srcdir" ] || return 0
|
|
fi
|
|
[ -z "$mode" ] && $dry check_cross_compiler
|
|
|
|
for y in "$target_dir/config"/*; do
|
|
[ "$_f" = "-d" ] || [ -f "$y" ] || continue
|
|
[ "$_f" = "-d" ] || defconfig="$y"
|
|
|
|
[ -n "$mode" ] || check_defconfig || continue
|
|
handle_makefile
|
|
[ -n "$mode" ] || $dry copy_elf
|
|
done; return 0
|
|
}
|
|
|
|
configure_project()
|
|
{
|
|
eval "`setvars "" cleanargs build_depend autoconfargs xtree postmake \
|
|
tree_depend makeargs btype mkhelper bootstrapargs premake release \
|
|
xarch xlang`"
|
|
_tcfg="$1/target.cfg"; badhash="n"; [ -f "$_tcfg" ] || btype="auto"
|
|
[ -f "$datadir/mkhelper.cfg" ] && \
|
|
eval "`setcfg "$datadir/mkhelper.cfg"`"
|
|
|
|
while [ -f "$_tcfg" ] || [ "$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
|
|
[ "$mode" = "fetch" ] || 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" ] && build_dependencies
|
|
|
|
mdir="$PWD/config/submodule/$project"
|
|
[ -n "$tree" ] && mdir="$mdir/$tree"
|
|
[ -f "CHANGELOG" ] || check_project_hashes
|
|
|
|
[ "$mode" = "fetch" ] || x_ ./mk -f "$project" "$target"
|
|
[ "$mode" = "fetch" ] || return 0
|
|
[ -f "CHANGELOG" ] && return 1; fetch_${cmd#build_}; return 1
|
|
}
|
|
|
|
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 ./mk -b $bd_p $bd_t \
|
|
|| $err "!mk $project/$tree $bd_p/$bd_t"; continue
|
|
done; return 0
|
|
}
|
|
|
|
check_project_hashes()
|
|
{
|
|
mkdir -p "$XBMK_CACHE/hash" || $err "!mkdir '$XBMK_CACHE/hash'"
|
|
old_pjhash=""; [ ! -f "$XBMK_CACHE/hash/$project$tree" ] || \
|
|
read -r old_pjhash < "$XBMK_CACHE/hash/$project$tree"
|
|
|
|
x_ rm -f "$TMPDIR/project.list" "$TMPDIR/project.hash" \
|
|
"$TMPDIR/project.tmp"; x_ touch "$TMPDIR/project.tmp"
|
|
x_ touch "$TMPDIR/project.hash"
|
|
|
|
for rmchk in "$datadir" "$configdir/$tree" "$mdir"; do
|
|
[ -d "$rmchk" ] || continue
|
|
find "$rmchk" -type f -not -path "*/.git*/*" >> \
|
|
"$TMPDIR/project.tmp" || $err "!find $rmchk > project.tmp"
|
|
done; sort "$TMPDIR/project.tmp" > "$TMPDIR/project.list" || \
|
|
$err "!sort project tmp/list"
|
|
|
|
while read -r rmchk; do
|
|
[ ! -f "$rmchk" ] || sha512sum "$rmchk" | awk \
|
|
'{print $1}' >> "$TMPDIR/project.hash" || $err "!h $rmchk"
|
|
done < "$TMPDIR/project.list"
|
|
|
|
pjhash="$(sha512sum "$TMPDIR/project.hash" | awk '{print $1}')" || :
|
|
badhash="y" && [ "$pjhash" = "$old_pjhash" ] && badhash="n"
|
|
[ -f "$XBMK_CACHE/hash/$project$tree" ] || badhash="y"
|
|
|
|
printf "%s\n" "$pjhash" > "$XBMK_CACHE/hash/$project$tree" || \
|
|
$err "!mk $XBMK_CACHE/hash/$project$tree"
|
|
|
|
[ "$badhash" = "n" ] || rm -Rf "src/$project/$tree" \
|
|
"elf/$project/$tree" "elf/$project/$target" || \
|
|
$err "!rm $project $tree"; :
|
|
}
|
|
|
|
check_cross_compiler()
|
|
{
|
|
xgccargs="UPDATED_SUBMODULES=1 CPUS=$XBMK_THREADS"
|
|
for _xarch in $xarch; do
|
|
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="$PWD/$cbdir/util/crossgcc/xgcc/bin:$PATH"
|
|
export CROSS_COMPILE="${xarch% *}-"
|
|
[ -n "$xlang" ] && export BUILD_LANGUAGES="$xlang"
|
|
|
|
xfix="${_xarch%-*}" && [ "$xfix" = "x86_64" ] && xfix="x64"
|
|
|
|
# match gnat-X to gcc
|
|
check_gnu_path gcc gnat || check_gnu_path gnat gcc || \
|
|
$err "Cannot match host GCC/GNAT versions"
|
|
|
|
# sometimes buildgcc fails for like no reason. try twice.
|
|
make -C "$cbdir" crossgcc-$xfix $xgccargs || \
|
|
make -C "$cbdir" crossgcc-$xfix $xgccargs || \
|
|
$err "!mkxgcc $project/$xtree '$xfix' '$xgccargs'"
|
|
|
|
# we only want to mess with hostcc to build xgcc
|
|
rm -f "$XBMK_CACHE/gnupath/"* || \
|
|
$err "Cannot clear gnupath/"; :
|
|
done; return 0
|
|
}
|
|
|
|
# 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()
|
|
{
|
|
[ $# -lt 2 ] && $err "check_gnu_path: Too few arguments"
|
|
[ "$1" = "$2" ] && $err "check_gnu_path: Both arguments identical"
|
|
for _gnuarg in 1 2; do
|
|
eval "[ \"\$$_gnuarg\" = \"gcc\" ] && continue"
|
|
eval "[ \"\$$_gnuarg\" = \"gnat\" ] && continue"
|
|
$err "check_gnu_path: Invalid argument \"$_gnuarg\""
|
|
done
|
|
command -v "$1" 1>/dev/null || $err "Host '$1' unavailable"
|
|
|
|
eval "`setvars "" gccver gccfull gnatver gnatfull gccdir gnatdir`"
|
|
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 _gnubin in "$_gnudir/$2-"*; do
|
|
[ -f "$_gnubin" ] || continue
|
|
[ "${_gnubin#"$_gnudir/$2-"}" = "$_gnuver" ] || continue
|
|
_gnuver="${_gnubin#"$_gnudir/$2-"}"; break
|
|
done
|
|
gnu_setver "$2" "$_gnudir/$2-$_gnuver" || return 1
|
|
[ "$gnatfull" = "$gccfull" ] || return 1
|
|
|
|
(
|
|
rm -f "$XBMK_CACHE/gnupath/"* || $err "Cannot clear gnupath/"
|
|
cd "$XBMK_CACHE/gnupath" || $err "Can't cd to gnupath/"
|
|
for _gnubin in "$_gnudir/$2"*"-$_gnuver"; do
|
|
[ -e "$_gnubin" ] || continue; _gnuutil="${_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
|
|
$dry x_ mkdir -p "$dest_dir"
|
|
}
|
|
|
|
elfcheck()
|
|
{
|
|
# TODO: very hacky check. do it properly (based on build.list)
|
|
for elftest in "$dest_dir"/*; do
|
|
[ -e "$elftest" ] && e "$elftest" f && return 1
|
|
done; return 0
|
|
}
|
|
|
|
handle_makefile()
|
|
{
|
|
$dry check_makefile "$srcdir" && x_ make -C "$srcdir" $cleanargs clean
|
|
[ -f "$defconfig" ] && x_ cp "$defconfig" "$srcdir/.config"
|
|
[ -n "$mode" ] || [ -n "$btype" ] || $dry make -C \
|
|
"$srcdir" silentoldconfig || make -C "$srcdir" oldconfig || :
|
|
|
|
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()
|
|
{
|
|
[ -z "$premake" ] || [ -n "$mode" ] || $premake || $err "!$premake"
|
|
$dry check_cmake "$srcdir" && [ -z "$mode" ] && $dry check_autoconf \
|
|
"$srcdir"; $dry check_makefile "$srcdir" || return 1
|
|
|
|
$dry make -C "$srcdir" $mode -j$XBMK_THREADS $makeargs || $err "!$mode"
|
|
[ -z "$mkhelper" ] || [ -n "$mode" ] || $mkhelper || $err "!$mkhelper"
|
|
|
|
[ "$mode" != "clean" ] || \
|
|
$dry make -C "$srcdir" $cleanargs distclean || :; :
|
|
}
|
|
|
|
check_cmake()
|
|
{
|
|
[ -z "$cmakedir" ] || $dry check_makefile "$1" || cmake -B "$1" \
|
|
"$1/$cmakedir" || $dry check_makefile "$1" || $err \
|
|
"$1: !cmk $cmakedir"
|
|
[ -z "$cmakedir" ] || $dry check_makefile "$1" || \
|
|
$err "check_cmake $1: can't generate Makefile"; return 0
|
|
}
|
|
|
|
check_autoconf()
|
|
{
|
|
(
|
|
cd "$1" || $err "!cd $1"
|
|
[ -f "bootstrap" ] && x_ ./bootstrap $bootstrapargs
|
|
[ -f "autogen.sh" ] && x_ ./autogen.sh $autogenargs
|
|
[ -f "configure" ] && x_ ./configure $autoconfargs; return 0
|
|
) || $err "can't bootstrap project: $1"
|
|
}
|
|
|
|
check_makefile()
|
|
{
|
|
[ -f "$1/Makefile" ] || [ -f "$1/makefile" ] || \
|
|
[ -f "$1/GNUmakefile" ] || return 1; return 0
|
|
}
|
|
|
|
copy_elf()
|
|
{
|
|
[ -f "$listfile" ] && x_ mkdir -p "$dest_dir" && while read -r f; do
|
|
[ -f "$srcdir/$f" ] && x_ cp "$srcdir/$f" "$dest_dir"
|
|
done < "$listfile"; x_ make clean -C "$srcdir" $cleanargs
|
|
}
|
|
|
|
main "$@" || exit 0
|
|
. "$mkhelpercfg"
|
|
$cmd
|