1b74898395
This change adds 2 command line parameters, --skip_set and --skip_unset that allows abuild to skip boards with particular Kconfig values either set or not set. Note that it only works on BOOL type variables. This can be set on the abuild command line, or the JENKINS_ABUILD_OPT= variable on the make command line. Signed-off-by: Martin Roth <gaumless@gmail.com> Change-Id: I43336484cf25f83065ec7facf45c123d831024b5 Reviewed-on: https://review.coreboot.org/c/coreboot/+/71730 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Fred Reitberger <reitbergerfred@gmail.com> Reviewed-by: Elyes Haouas <ehaouas@noos.fr> Reviewed-by: Felix Singer <felixsinger@posteo.net>
1020 lines
30 KiB
Bash
Executable file
1020 lines
30 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# coreboot autobuild
|
|
#
|
|
# This script builds coreboot images for all available targets.
|
|
#
|
|
# This file is subject to the terms and conditions of the GNU General
|
|
# Public License. See the file COPYING in the main directory of this
|
|
# archive for more details.
|
|
#
|
|
|
|
#set -x # Turn echo on....
|
|
|
|
ABUILD_DATE="Feb 3, 2023"
|
|
ABUILD_VERSION="0.11.01"
|
|
|
|
TOP=$PWD
|
|
|
|
# Where shall we place all the build trees?
|
|
TARGET_DEFAULT=coreboot-builds
|
|
TARGET=${COREBOOT_BUILD_DIR:-${TARGET_DEFAULT}}
|
|
XML_DEFAULT="$TOP/abuild.xml"
|
|
XMLFILE="${XML_DEFAULT}"
|
|
REAL_XMLFILE="${XML_DEFAULT}"
|
|
|
|
# Name associated with a run of abuild
|
|
TESTRUN_DEFAULT=default
|
|
TESTRUN="${TESTRUN_DEFAULT}"
|
|
|
|
export KCONFIG_OVERWRITECONFIG=1
|
|
|
|
# path to payload. Should be more generic
|
|
PAYLOAD=/dev/null
|
|
|
|
# get path to coreboot XGCC if it's not already set
|
|
if [ -z "$XGCCPATH" ]; then
|
|
XGCCPATH="${TOP}/util/crossgcc/xgcc/bin/"
|
|
fi
|
|
|
|
# Add XGCC to the path.
|
|
if [ -d "$XGCCPATH" ] && [[ ":$PATH:" != *":$XGCCPATH:"* ]]; then
|
|
PATH="$XGCCPATH:$PATH"
|
|
fi
|
|
|
|
# Lines of error context to be printed in FAILURE case
|
|
CONTEXT=12
|
|
|
|
# Configure-only mode
|
|
configureonly=0
|
|
|
|
# Did any board fail to build?
|
|
failed=0
|
|
|
|
# Exit with a non-zero errorlevel on failure
|
|
exitcode=0
|
|
|
|
# default: don't save checksums
|
|
checksum_file=""
|
|
|
|
# default: single CPU build
|
|
cpus=1
|
|
|
|
# change with -d <directory>
|
|
configdir="$TOP/configs"
|
|
|
|
# Timeless builds
|
|
TIMELESS=0
|
|
|
|
# One might want to adjust these in case of cross compiling
|
|
for i in make gmake gnumake nonexistent_make; do
|
|
$i --version 2>/dev/null |grep "GNU Make" >/dev/null && break
|
|
done
|
|
if [ "$i" = "nonexistent_make" ]; then
|
|
echo No GNU Make found.
|
|
exit 1
|
|
fi
|
|
MAKE=$i
|
|
|
|
# this can be changed to junit by -J
|
|
mode=text
|
|
|
|
# quiet mode: only print pass, failure, and 'skipped' messages
|
|
quiet=false
|
|
|
|
# clang mode enabled by -sb option.
|
|
scanbuild=false
|
|
|
|
# Mark whether abuild was called recursively
|
|
recursive=false
|
|
|
|
# Skip builds with this Kconfig value set
|
|
skipconfig_set=""
|
|
|
|
# Skip builds with this Kconfig value notset
|
|
skipconfig_unset=""
|
|
|
|
trap interrupt INT
|
|
|
|
function interrupt
|
|
{
|
|
printf "\n%s: execution interrupted manually.\n" "$0"
|
|
if [ "$mode" == "junit" ]; then
|
|
printf "%s: deleting incomplete xml output file.\n" "$0"
|
|
fi
|
|
exit 1
|
|
}
|
|
|
|
function debug
|
|
{
|
|
test "$verbose" == "true" && echo "$*"
|
|
}
|
|
|
|
function junit
|
|
{
|
|
test "$mode" == "junit" && echo "$*" >> "$XMLFILE"
|
|
return 0
|
|
}
|
|
|
|
function junitfile
|
|
{
|
|
test "$mode" == "junit" && {
|
|
printf '<![CDATA[\n'
|
|
cat "$1"
|
|
printf ']]>\n'
|
|
} >> "$XMLFILE"
|
|
}
|
|
|
|
# Return mainboard descriptors.
|
|
# By default all mainboards are listed, but when passing a two-level path
|
|
# below src/mainboard, such as emulation/qemu-i440fx, or emulation/*, it
|
|
# returns all board descriptors in that hierarchy.
|
|
function get_mainboards
|
|
{
|
|
local search_space=${1-*/*}
|
|
# shellcheck disable=SC2086
|
|
grep -h "^[[:space:]]*config\>[[:space:]]*\<BOARD_" \
|
|
${ROOT}/src/mainboard/${search_space}/Kconfig.name 2>/dev/null | \
|
|
sed "s,^.*\<BOARD_\([A-Z0-9_]*\)\>.*$,\1,"
|
|
}
|
|
|
|
# Given a mainboard descriptor, return its directory below src/mainboard
|
|
function mainboard_directory
|
|
{
|
|
local MAINBOARD=$1
|
|
|
|
# shellcheck disable=SC2086
|
|
grep -l "^[[:space:]]*config\>[[:space:]]*\<BOARD_${MAINBOARD}\>" \
|
|
${ROOT}/src/mainboard/*/*/Kconfig.name | \
|
|
sed "s:^$ROOT/src/mainboard/\(.*\)/Kconfig.name$:\1:"
|
|
}
|
|
|
|
# Given a mainboard descriptor, return its vendor (CONFIG_VENDOR_*)
|
|
function mainboard_vendor
|
|
{
|
|
local MAINBOARD=$1
|
|
local kconfig_file
|
|
|
|
# shellcheck disable=SC2086
|
|
kconfig_file=$( \
|
|
grep -l "^[[:space:]]*config\>[[:space:]]*\<BOARD_${MAINBOARD}\>" \
|
|
${ROOT}/src/mainboard/*/*/Kconfig.name | \
|
|
sed "s:^\(${ROOT}/src/mainboard/.*\)/.*/\(Kconfig.name\)$:\1/\2:" )
|
|
if [ ! -f "$kconfig_file" ]; then
|
|
exit 1
|
|
fi
|
|
grep "^[[:space:]]*config\>[[:space:]]*\<VENDOR_" "$kconfig_file" | \
|
|
sed "s,^.*\<VENDOR_\([A-Z0-9_]*\)\>.*$,\1,"
|
|
}
|
|
|
|
# Accepts directory names (eg. emulation/qemu-i440fx) and mainboard
|
|
# descriptors (eg. EMULATION_QEMU_X86_I440F} and returns the latter
|
|
# format.
|
|
# If a directory contains multiple boards, returns them all.
|
|
function normalize_target
|
|
{
|
|
# TODO: Change 'targets' variable to an array
|
|
local targets
|
|
local VARIANT_UC
|
|
|
|
VARIANT_UC=$(echo "${variant}" | tr '[:lower:]' '[:upper:]')
|
|
|
|
targets=$(get_mainboards "$1")
|
|
if [ -n "$targets" ]; then
|
|
# shellcheck disable=SC2086
|
|
targets="$(grep "${VARIANT_UC}\$" <<< ${targets})"
|
|
echo "$targets"
|
|
return
|
|
fi
|
|
|
|
targets=$(echo "$1" | tr ',' ' ')
|
|
for i in $targets; do
|
|
if [ -n "$(mainboard_directory "$i")" ]; then
|
|
echo "$i"
|
|
else
|
|
echo "$i is not a valid target" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
}
|
|
|
|
# shellcheck disable=SC2129
|
|
function create_config
|
|
{
|
|
local BUILD_NAME=$1
|
|
local build_dir=$2
|
|
local board_srcdir
|
|
|
|
local config_file="${build_dir}/config.build"
|
|
board_srcdir="$(mainboard_directory "${BUILD_NAME}")"
|
|
|
|
mkdir -p "${build_dir}"
|
|
mkdir -p "$TARGET/sharedutils"
|
|
|
|
if [ "$quiet" == "false" ]; then echo " Creating config file for $BUILD_NAME..."; fi
|
|
echo "CONFIG_VENDOR_$(mainboard_vendor "${BUILD_NAME}")=y" > "${config_file}"
|
|
echo "CONFIG_BOARD_${BUILD_NAME}=y" >> "${config_file}"
|
|
grep "select[\t ]*ARCH" "${ROOT}/src/mainboard/${board_srcdir}/Kconfig" | \
|
|
sed "s,^.*\(ARCH_.*\)[^A-Z0-9_]*,CONFIG_\1=y," >> "${config_file}"
|
|
echo "CONFIG_MAINBOARD_DIR=\"${board_srcdir}\"" >> "${config_file}"
|
|
|
|
update_config "$BUILD_NAME" "$build_dir" "$config_file"
|
|
|
|
ret=$?
|
|
if [ $ret -eq 0 ]; then
|
|
if [ "$quiet" == "false" ]; then echo " $BUILD_NAME config created."; fi
|
|
return 0
|
|
else
|
|
# Does this ever happen?
|
|
if [ "$quiet" == "false" ]; then printf "%s config creation FAILED!\nLog excerpt:\n" "$BUILD_NAME"; fi
|
|
tail -n $CONTEXT "$build_dir/config.log" 2> /dev/null || tail -$CONTEXT "$build_dir/config.log"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
function update_config
|
|
{
|
|
local BUILD_NAME=$1
|
|
local build_dir=$2
|
|
local config_file=$3
|
|
|
|
local PAYLOAD
|
|
local defconfig_file
|
|
defconfig_file=${build_dir}/config.$(echo "${BUILD_NAME}" | tr '[:upper:]' '[:lower:]').default
|
|
|
|
# get a working payload for the board if we have one.
|
|
# the --payload option expects a directory containing
|
|
# a shell script payload.sh
|
|
# Usage: payload.sh [BOARD]
|
|
# the script returns an absolute path to the payload binary.
|
|
|
|
if [ -f "$payloads/payload.sh" ]; then
|
|
PAYLOAD=$(sh "$payloads/payload.sh" "$BUILD_NAME")
|
|
local PAYLOAD_OK=$?
|
|
if [ $PAYLOAD_OK -gt 0 ]; then
|
|
echo "problem with payload"
|
|
exit 1
|
|
fi
|
|
if [ "$quiet" == "false" ]; then printf "Using payload %s\n" "$PAYLOAD"; fi
|
|
elif [ "$payloads" = "none" ]; then
|
|
PAYLOAD=none
|
|
fi
|
|
|
|
if [ "$PAYLOAD" = "none" ]; then
|
|
{
|
|
echo "CONFIG_PAYLOAD_NONE=y"
|
|
echo "# CONFIG_PAYLOAD_ELF is not set"
|
|
} >> "${config_file}"
|
|
elif [ "$PAYLOAD" != "/dev/null" ]; then
|
|
{
|
|
echo "# CONFIG_PAYLOAD_NONE is not set"
|
|
echo "CONFIG_PAYLOAD_ELF=y"
|
|
echo "CONFIG_PAYLOAD_FILE=\"$PAYLOAD\""
|
|
} >> "${config_file}"
|
|
fi
|
|
# Disable all other payload config options
|
|
{
|
|
echo "# CONFIG_PAYLOAD_SEABIOS is not set"
|
|
echo "# CONFIG_PAYLOAD_FILO is not set"
|
|
echo "# CONFIG_PAYLOAD_GRUB2 is not set"
|
|
echo "# CONFIG_PAYLOAD_DEPTHCHARGE is not set"
|
|
echo "# CONFIG_PAYLOAD_LINUXBOOT is not set"
|
|
echo "# CONFIG_PAYLOAD_UBOOT is not set"
|
|
echo "# CONFIG_PAYLOAD_EDK2 is not set"
|
|
echo "# CONFIG_PXE is not set"
|
|
echo "# CONFIG_BUILD_IPXE is not set"
|
|
echo "# CONFIG_MEMTEST_SECONDARY_PAYLOAD is not set"
|
|
echo "# CONFIG_COREINFO_SECONDARY_PAYLOAD is not set"
|
|
echo "# CONFIG_NVRAMCUI_SECONDARY_PAYLOAD is not set"
|
|
echo "# CONFIG_TINT_SECONDARY_PAYLOAD is not set"
|
|
} >> "${config_file}"
|
|
|
|
if [ "$quiet" == "false" ]; then echo " $MAINBOARD ($customizing)"; fi
|
|
# shellcheck disable=SC2059
|
|
printf "$configoptions" >> "${config_file}"
|
|
|
|
$MAKE olddefconfig "$verboseopt" "DOTCONFIG=${config_file}" "obj=${build_dir}" "objutil=$TARGET/sharedutils" &> "${build_dir}/config.log" ; \
|
|
CONFIG_OK=$?
|
|
if [ $CONFIG_OK -eq 0 ]; then
|
|
$MAKE savedefconfig "$verboseopt" DEFCONFIG="${defconfig_file}" DOTCONFIG="${config_file}" obj="${build_dir}" objutil="$TARGET/sharedutils" &>> "${build_dir}/config.log"
|
|
return $?
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# shellcheck disable=SC2129
|
|
function create_buildenv
|
|
{
|
|
local BUILD_NAME=$1
|
|
local build_dir=$2
|
|
local config_file=$3
|
|
|
|
if [ -z "$config_file" ]; then
|
|
create_config "$BUILD_NAME" "$build_dir"
|
|
else
|
|
local new_config_file="${build_dir}/config.build"
|
|
cp "$config_file" "$new_config_file"
|
|
update_config "$BUILD_NAME" "$build_dir" "$new_config_file"
|
|
fi
|
|
local ret=$?
|
|
|
|
# Allow simple "make" in the target directory
|
|
local MAKEFILE=$TARGET/${BUILD_NAME}/Makefile
|
|
echo "# autogenerated" > "$MAKEFILE"
|
|
echo "TOP=$ROOT" >> "$MAKEFILE"
|
|
echo "BUILD=$TARGET" >> "$MAKEFILE"
|
|
echo "OBJ=\$(BUILD)/${MAINBOARD}" >> "$MAKEFILE"
|
|
echo "OBJUTIL=\$(BUILD)/sharedutils" >> "$MAKEFILE"
|
|
echo "all:" >> "$MAKEFILE"
|
|
echo " @cp -a config.h config.h.bak" >> "$MAKEFILE"
|
|
echo " @cd \$(TOP); \$(MAKE) olddefconfig DOTCONFIG=\$(OBJ)/config.build objutil=\$(OBJUTIL) obj=\$(OBJ)" >> "$MAKEFILE"
|
|
echo " @tail -n+6 config.h > config.new; tail -n+6 config.h.bak > config.old" >> "$MAKEFILE"
|
|
echo " @cmp -s config.new config.old && cp -a config.h.bak config.h || echo \"Config file changed\"" >> "$MAKEFILE"
|
|
echo " @rm config.h.bak config.new config.old" >> "$MAKEFILE"
|
|
echo " @cd \$(TOP); \$(MAKE) DOTCONFIG=\$(OBJ)/config.build objutil=\$(OBJUTIL) obj=\$(OBJ)" >> "$MAKEFILE"
|
|
|
|
return $ret
|
|
}
|
|
|
|
function check_config
|
|
{
|
|
local BUILD_DIR="$1"
|
|
local TEST_TYPE="$2"
|
|
local TEST_STRING="$3"
|
|
local NEGATE="$4"
|
|
|
|
local CONFIG_FILE="$BUILD_DIR/config.build"
|
|
local CONFIG_LOG="$BUILD_DIR/config.log"
|
|
|
|
if [ -z "$NEGATE" ]; then
|
|
if ! grep -q "$TEST_STRING" "$CONFIG_FILE"; then
|
|
echo "config file: $CONFIG_FILE has incorrect $TEST_TYPE"
|
|
echo "Error: Expected '$TEST_STRING' in config file." >> "$CONFIG_LOG"
|
|
return 1
|
|
fi
|
|
else
|
|
if grep -q "$TEST_STRING" "$CONFIG_FILE"; then
|
|
echo "config file: $CONFIG_FILE has incorrect $TEST_TYPE"
|
|
echo "Error: Expected not to see '$TEST_STRING' in config file." >> "$CONFIG_LOG"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
function compile_target
|
|
{
|
|
local BUILD_NAME=$1
|
|
|
|
if [ "$quiet" == "false" ]; then echo " Compiling $MAINBOARD image$cpuconfig..."; fi
|
|
|
|
CURR=$( pwd )
|
|
#stime=`perl -e 'print time();' 2>/dev/null || date +%s`
|
|
eval "$BUILDPREFIX" "$MAKE" "$verboseopt" DOTCONFIG="${build_dir}/config.build" obj="${build_dir}" objutil="$TARGET/sharedutils" BUILD_TIMELESS=$TIMELESS \
|
|
&> "${build_dir}/make.log" ; \
|
|
MAKE_FAILED=$?
|
|
cd "${build_dir}" || return $?
|
|
|
|
etime=$(perl -e 'print time();' 2>/dev/null || date +%s)
|
|
duration=$(( etime - stime ))
|
|
junit " <testcase classname='${TESTRUN}${testclass/#/.}' name='$BUILD_NAME' time='$duration' >"
|
|
|
|
if [ $MAKE_FAILED -eq 0 ]; then
|
|
junit "<system-out>"
|
|
junitfile make.log
|
|
junit "</system-out>"
|
|
printf "ok\n" > compile.status
|
|
printf "%s built successfully. (took %ss)\n" "$BUILD_NAME" "${duration}"
|
|
echo "$BUILD_NAME" >> "$PASSED_BOARDS"
|
|
else
|
|
junit "<failure type='BuildFailed'>"
|
|
junitfile make.log
|
|
junit "</failure>"
|
|
printf "failed\n" > compile.status
|
|
printf "%s build FAILED after %ss!\nLog excerpt:\n" "$BUILD_NAME" "${duration}"
|
|
tail -n $CONTEXT make.log 2> /dev/null || tail -$CONTEXT make.log
|
|
if [ "$clean_work" = "true" ]; then
|
|
echo "$BUILD_NAME" >> "$FAILED_BOARDS"
|
|
else
|
|
echo "$BUILD_NAME - Log: ${build_dir}/make.log" >> "$FAILED_BOARDS"
|
|
fi
|
|
failed=1
|
|
fi
|
|
cd "$CURR" || return $?
|
|
if [ -n "$checksum_file" ]; then
|
|
sha256sum "${build_dir}/coreboot.rom" >> "${checksum_file}_platform"
|
|
sort "${build_dir}/config.h" | grep CONFIG_ > "${build_dir}/config.h.sorted"
|
|
sha256sum "${build_dir}/config.h.sorted" >> "${checksum_file}_config"
|
|
fi
|
|
if [ "$clean_work" = "true" ]; then
|
|
rm -rf "${build_dir}"
|
|
fi
|
|
if [ "$clean_objs" = "true" ]; then
|
|
find ${build_dir} \! \( -name coreboot.rom -o -name config.h -o -name config.build -o -name make.log \) -type f -exec rm {} +
|
|
find ${build_dir} -type d -exec rmdir -p {} + 2>/dev/null
|
|
fi
|
|
return $MAKE_FAILED
|
|
}
|
|
|
|
function build_config
|
|
{
|
|
local MAINBOARD=$1
|
|
local build_dir=$2
|
|
local BUILD_NAME=$3
|
|
local config_file=$4
|
|
local board_srcdir
|
|
local ret
|
|
|
|
board_srcdir=$(mainboard_directory "${MAINBOARD}")
|
|
|
|
if [ "$(cat "${build_dir}/compile.status" 2>/dev/null)" = "ok" ] && \
|
|
[ "$buildall" = "false" ]; then
|
|
echo "Skipping $BUILD_NAME; (already successful)"
|
|
return
|
|
fi
|
|
|
|
export HOSTCC='gcc'
|
|
|
|
if [ "$chromeos" = true ] && [ "$(grep -c "^[[:space:]]*select[[:space:]]*MAINBOARD_HAS_CHROMEOS\>" "${ROOT}/src/mainboard/${board_srcdir}/Kconfig")" -eq 0 ]; then
|
|
echo "${BUILD_NAME} doesn't support ChromeOS, skipping."
|
|
return
|
|
fi
|
|
|
|
if [ "$quiet" == "false" ]; then echo "Building $BUILD_NAME"; fi
|
|
mkdir -p "$TARGET/${BUILD_NAME}" "$TARGET/abuild"
|
|
ABSPATH="$(cd "$TARGET/abuild" && pwd)"
|
|
XMLFILE="$ABSPATH/${BUILD_NAME}.xml"
|
|
rm -f "${XMLFILE}"
|
|
|
|
stime=$(perl -e 'print time();' 2>/dev/null || date +%s)
|
|
create_buildenv "$BUILD_NAME" "$build_dir" "$config_file"
|
|
local BUILDENV_CREATED=$?
|
|
|
|
check_config "$build_dir" "mainboard" "CONFIG_BOARD_${MAINBOARD}=y"
|
|
local MAINBOARD_OK=$?
|
|
|
|
check_config "$build_dir" "vendor" "CONFIG_VENDOR_$(mainboard_vendor "${MAINBOARD}")=y"
|
|
local VENDOR_OK=$?
|
|
|
|
if [ "$chromeos" = false ]; then
|
|
# Skip this rule for configs created from templates that already
|
|
# come with CHROMEOS enabled.
|
|
grep -q "^CONFIG_CHROMEOS=y" ${config_file:-/dev/null} || \
|
|
check_config "$build_dir" "ChromeOS" "CONFIG_CHROMEOS=y" negate
|
|
local FORCE_ENABLED_CROS=$?
|
|
else
|
|
local FORCE_ENABLED_CROS=0
|
|
fi
|
|
|
|
if [ "$clang" = true ]; then
|
|
check_config "$build_dir" "clang" "CONFIG_COMPILER_LLVM_CLANG=y"
|
|
if [ $? -ne 0 ]; then
|
|
echo "${MAINBOARD} doesn't support clang, skipping."
|
|
return
|
|
fi
|
|
fi
|
|
|
|
if [ -n "${skipconfig_set}" ]; then
|
|
check_config "${build_dir}" "config value" "CONFIG_${skipconfig_set}=y" negate
|
|
if [ $? -ne 0 ]; then
|
|
echo "${MAINBOARD} has ${skipconfig_set} set. Skipping at user's request."
|
|
return
|
|
fi
|
|
fi
|
|
|
|
if [ -n "${skipconfig_unset}" ]; then
|
|
check_config "${build_dir}" "config value" "CONFIG_${skipconfig_unset}=y"
|
|
if [ $? -ne 0 ]; then
|
|
echo "${MAINBOARD} does not have ${skipconfig_unset} set. Skipping at user's request."
|
|
return
|
|
fi
|
|
fi
|
|
|
|
if [ $BUILDENV_CREATED -ne 0 ] || [ $MAINBOARD_OK -ne 0 ] || [ $VENDOR_OK -ne 0 ] || [ $FORCE_ENABLED_CROS -eq 1 ]; then
|
|
junit " <testcase classname='${TESTRUN}${testclass/#/.}' name='$BUILD_NAME' >"
|
|
|
|
junit "<failure type='BuildFailed'>"
|
|
junitfile "$build_dir/config.log"
|
|
junit "</failure>"
|
|
printf "failed\n" > compile.status
|
|
printf "%s build configuration FAILED!\nLog excerpt:\n" "$BUILD_NAME"
|
|
tail -n $CONTEXT "$build_dir/config.log" 2> /dev/null || tail -$CONTEXT "$build_dir/config.log"
|
|
|
|
junit "</testcase>"
|
|
echo "$BUILD_NAME - Log: ${TOP}/$build_dir/config.log" >> "$FAILED_BOARDS"
|
|
return
|
|
fi
|
|
|
|
local required_arches
|
|
|
|
required_arches=$(grep -E "^CONFIG_ARCH_(BOOTBLOCK|R.MSTAGE|VERSTAGE)" "$TARGET/${BUILD_NAME}/config.build" | \
|
|
sed "s,^CONFIG_ARCH_[^_]*_\([^=]*\)=.*$,\1," |sort -u |tr 'A-Z\n\r' 'a-z ')
|
|
|
|
missing_arches="$($MAKE --no-print-directory -f - \
|
|
REQUIRED_ARCHES="$required_arches" <<'EOF'
|
|
include $(xcompile)
|
|
.PHONY: missing_arches
|
|
missing_arches:
|
|
$(if $(XCOMPILE_COMPLETE),,$(error $(xcompile) is invalid.))
|
|
@echo $(foreach arch,$(REQUIRED_ARCHES),$(if $(filter $(arch),$(SUBARCH_SUPPORTED)),,$(arch)))
|
|
EOF
|
|
)"
|
|
# shellcheck disable=SC2181
|
|
if [[ $? -ne 0 ]]; then
|
|
echo "Calculating missing_arches failed" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ -n "$missing_arches" ]; then
|
|
printf "skipping %s because we're missing compilers for (%s)\n" "$BUILD_NAME" "$missing_arches"
|
|
return
|
|
fi
|
|
|
|
if [ $BUILDENV_CREATED -eq 0 ] && [ $configureonly -eq 0 ]; then
|
|
BUILDPREFIX=
|
|
if [ "$scanbuild" = "true" ]; then
|
|
scanbuild_out=$TARGET/${BUILD_NAME}-scanbuild
|
|
rm -rf "${scanbuild_out}"
|
|
BUILDPREFIX="scan-build ${SCANBUILD_ARGS} -o ${scanbuild_out}tmp"
|
|
fi
|
|
compile_target "${BUILD_NAME}"
|
|
if [ "$scanbuild" = "true" ]; then
|
|
mv "${scanbuild_out}"tmp/* "${scanbuild_out}"
|
|
rmdir "${scanbuild_out}tmp"
|
|
fi
|
|
fi
|
|
|
|
junit "</testcase>"
|
|
}
|
|
|
|
# One target may build several configs
|
|
function build_target
|
|
{
|
|
local MAINBOARD=$1
|
|
local MAINBOARD_LC
|
|
MAINBOARD_LC=$(echo "$MAINBOARD" | tr '[:upper:]' '[:lower:]')
|
|
|
|
# look for config files in the config directory that match the boardname
|
|
if [ -n "$( find "$configdir" -maxdepth 1 -name "config.${MAINBOARD_LC}*" -print -quit )" ]; then
|
|
for config in "$configdir/config.${MAINBOARD_LC}"*; do
|
|
BUILD_NAME="${config##*/}"
|
|
BUILD_NAME="${BUILD_NAME##config.}"
|
|
BUILD_NAME=$(echo "${BUILD_NAME}" | tr '[:lower:]' '[:upper:]')
|
|
echo $BUILD_NAME $MAINBOARD
|
|
# If the file in configs/ results in the same build_name as the default config
|
|
# append a '_' to differentiate. Otherwise the default configuration would
|
|
# override the results.
|
|
if [ "${MAINBOARD}" = "${BUILD_NAME}" ]; then
|
|
BUILD_NAME=${BUILD_NAME}"_"
|
|
fi
|
|
echo "Building config $BUILD_NAME"
|
|
build_dir=$TARGET/${BUILD_NAME}
|
|
build_config "$MAINBOARD" "$build_dir" "$BUILD_NAME" "$config"
|
|
remove_target "$BUILD_NAME"
|
|
done
|
|
fi
|
|
|
|
echo "Building board $MAINBOARD (using default config)"
|
|
build_dir=$TARGET/${MAINBOARD}
|
|
build_config "$MAINBOARD" "$build_dir" "$MAINBOARD"
|
|
remove_target "$MAINBOARD"
|
|
}
|
|
|
|
function remove_target
|
|
{
|
|
if [ "$remove" != "true" ]; then
|
|
return
|
|
fi
|
|
|
|
local BUILD_NAME=$1
|
|
|
|
# Save the generated coreboot.rom file of each board.
|
|
if [ -r "$TARGET/${BUILD_NAME}/coreboot.rom" ]; then
|
|
cp "$TARGET/${BUILD_NAME}/coreboot.rom" \
|
|
"${BUILD_NAME}_coreboot.rom"
|
|
fi
|
|
|
|
echo "Removing build dir for $BUILD_NAME..."
|
|
rm -rf "${TARGET:?}/${BUILD_NAME}"
|
|
|
|
return
|
|
}
|
|
|
|
function myhelp
|
|
{
|
|
cat << __END_OF_HELP
|
|
Usage: $0 [options]
|
|
$0 [-V|--version]
|
|
$0 [-h|--help]
|
|
|
|
Options:\n
|
|
[-a|--all] Build previously succeeded ports as well
|
|
[-A|--any-toolchain] Use any toolchain
|
|
[-b|--board-variant <name>] Build specific board variant under the
|
|
given target.
|
|
[-B|--blobs] Allow using binary files
|
|
[--checksum <path/basefile>] Store checksums at path/basefile
|
|
[-c|--cpus <numcpus>] Build on <numcpus> at the same time
|
|
[-C|--config] Configure-only mode
|
|
[-d|--dir <dir>] Directory containing config files
|
|
[-e|--exitcode] Exit with a non-zero errorlevel on failure
|
|
[-J|--junit] Write JUnit formatted xml log file
|
|
[-K|--kconfig <name>] Prepend file to generated Kconfig
|
|
[-l|--loglevel <num>] Set loglevel
|
|
[-L|--clang] Use clang on supported arch
|
|
[-n|--name] Set build name - also sets xmlfile if not
|
|
already set
|
|
[-o|--outdir <path>] Store build results in path
|
|
(defaults to $TARGET)
|
|
[-p|--payloads <dir>] Use payloads in <dir> to build images
|
|
[-P|--prefix <name>] File name prefix in CBFS
|
|
[-q|--quiet] Print fewer messages
|
|
[-r|--remove] Remove output dir after build
|
|
[-R|--root <path>] Absolute path to coreboot sources
|
|
(defaults to $ROOT)
|
|
[--scan-build] Use clang's static analyzer
|
|
[--skip_set <value>] Skip building boards with this Kconfig set
|
|
[--skip_unset <value>] Skip building boards with this Kconfig not set
|
|
[--timeless] Generate timeless builds
|
|
[-t|--target <vendor/board>] Attempt to build target vendor/board only
|
|
[-T|--test] Submit image(s) to automated test system
|
|
[-u|--update] Update existing image
|
|
[-v|--verbose] Print more messages
|
|
[-x|--chromeos] Build with CHROMEOS enabled
|
|
Skip boards without ChromeOS support
|
|
[-X|--xmlfile <name>] Set JUnit XML log file filename
|
|
(defaults to $XMLFILE)
|
|
[-y|--ccache] Use ccache
|
|
[-z|--clean] Remove build results when finished
|
|
[-Z|--clean-somewhat] Remove build but keep coreboot.rom + config
|
|
|
|
[-V|--version] Print version number and exit
|
|
[-h|--help] Print this help and exit
|
|
|
|
[-s|--silent] obsolete
|
|
__END_OF_HELP
|
|
}
|
|
|
|
function myversion
|
|
{
|
|
cat << EOF
|
|
|
|
coreboot autobuild v$ABUILD_VERSION ($ABUILD_DATE)
|
|
|
|
Copyright (C) 2004 by Stefan Reinauer <stepan@openbios.org>
|
|
Copyright (C) 2006-2010 by coresystems GmbH <info@coresystems.de>
|
|
|
|
This program is free software; you may redistribute it under the terms
|
|
of the GNU General Public License. This program has absolutely no
|
|
warranty.
|
|
|
|
EOF
|
|
}
|
|
|
|
# default options
|
|
target=""
|
|
buildall=false
|
|
verbose=false
|
|
|
|
test -f util/sconfig/sconfig.l && ROOT=$( pwd )
|
|
test -f ../util/sconfig/sconfig.l && ROOT=$( cd .. && pwd )
|
|
test "$ROOT" = "" && ROOT=$( cd ../.. && pwd )
|
|
|
|
# Look if we have getopt. If not, build it.
|
|
export PATH=$PATH:util/abuild
|
|
getopt - > /dev/null 2>/dev/null || gcc -o util/abuild/getopt util/abuild/getopt.c
|
|
|
|
# Save command line for xargs parallelization.
|
|
cmdline=("$@")
|
|
|
|
# parse parameters.. try to find out whether we're running GNU getopt
|
|
getoptbrand="$(getopt -V)"
|
|
|
|
# shellcheck disable=SC2086
|
|
if [ "${getoptbrand:0:6}" == "getopt" ]; then
|
|
# Detected GNU getopt that supports long options.
|
|
args=$(getopt -l version,verbose,quiet,help,all,target:,board-variant:,payloads:,cpus:,silent,junit,config,loglevel:,remove,prefix:,update,scan-build,ccache,blobs,clang,any-toolchain,clean,clean-somewhat,outdir:,chromeos,xmlfile:,kconfig:,dir:,root:,recursive,checksum:,timeless,exitcode,asserts,name:,skip_set:,skip_unset: -o Vvqhat:b:p:c:sJCl:rP:uyBLAzZo:xX:K:d:R:Ien: -- "$@") || exit 1
|
|
eval set -- $args
|
|
retval=$?
|
|
else
|
|
# Detected non-GNU getopt
|
|
args=$(getopt Vvqhat:b:p:c:sJCl:rP:uyBLAZzo:xX:K:d:R:Ien: "$@")
|
|
set -- $args
|
|
retval=$?
|
|
fi
|
|
|
|
if [ $retval != 0 ]; then
|
|
myhelp
|
|
exit 1
|
|
fi
|
|
|
|
chromeos=false
|
|
clang=false
|
|
clean_work=false
|
|
clean_objs=false
|
|
verboseopt='V=0'
|
|
customizing=""
|
|
configoptions=""
|
|
# testclass needs to be undefined if not used for variable expansion to work
|
|
unset testclass
|
|
while true ; do
|
|
case "$1" in
|
|
-J|--junit) shift; mode=junit; rm -f "$XMLFILE" ;;
|
|
-t|--target) shift; target="$1"; shift;;
|
|
-b|--board-variant) shift; variant="$1"; shift;;
|
|
-a|--all) shift; buildall=true;;
|
|
-d|--dir) shift; configdir="$1"; shift;;
|
|
-e|--exitcode) shift; exitcode=1;;
|
|
-r|--remove) shift; remove=true;;
|
|
-v|--verbose) shift; verbose=true; verboseopt='V=1';;
|
|
-q|--quiet) shift; quiet=true;;
|
|
-V|--version) shift; myversion; exit 0;;
|
|
-h|--help) shift; myversion; myhelp; exit 0;;
|
|
-p|--payloads) shift; payloads="$1"; shift;;
|
|
-R|--root) shift; ROOT="$1"; MAKE="$MAKE -C $1"; shift;;
|
|
-c|--cpus) shift
|
|
export MAKEFLAGS="-j $1"
|
|
cpus=$1
|
|
test "$MAKEFLAGS" == "-j max" && export MAKEFLAGS="-j" && cpuconfig=" in parallel"
|
|
test "$1" == "1" && cpuconfig=" on 1 cpu"
|
|
expr "$1" : '-\?[0-9]\+$' > /dev/null && test "0$1" -gt 1 && cpuconfig=" on $1 cpus in parallel"
|
|
shift;;
|
|
# obsolete option
|
|
-s|--silent) shift;;
|
|
--scan-build) shift
|
|
scanbuild=true
|
|
customizing="${customizing}, scan-build"
|
|
SCANBUILD_ARGS=${SCANBUILD_ARGS:-'-k'}
|
|
configoptions="${configoptions}CONFIG_FATAL_ASSERTS=y\n"
|
|
;;
|
|
--skip_set) shift
|
|
skipconfig_set="$1"
|
|
customizing="${customizing}, Skipping CONFIG_${skipconfig_set}=Y"
|
|
shift
|
|
;;
|
|
--skip_unset) shift
|
|
skipconfig_unset="$1"
|
|
customizing="${customizing}, Skipping CONFIG_${skipconfig_unset} not set"
|
|
shift
|
|
;;
|
|
--asserts) shift
|
|
configoptions="${configoptions}CONFIG_FATAL_ASSERTS=y\n"
|
|
;;
|
|
-y|--ccache) shift
|
|
customizing="${customizing}, ccache"
|
|
configoptions="${configoptions}CONFIG_CCACHE=y\n"
|
|
;;
|
|
-C|--config) shift; configureonly=1;;
|
|
-l|--loglevel) shift
|
|
customizing="${customizing}, loglevel $1"
|
|
configoptions="${configoptions}CONFIG_DEFAULT_CONSOLE_LOGLEVEL_$1=y\n"
|
|
configoptions="${configoptions}CONFIG_DEFAULT_CONSOLE_LOGLEVEL=$1\n"
|
|
shift;;
|
|
-u|--update) shift
|
|
customizing="${customizing}, update"
|
|
configoptions="${configoptions}CONFIG_UPDATE_IMAGE=y\n"
|
|
;;
|
|
-P|--prefix) shift
|
|
customizing="${customizing}, cbfs prefix $1"
|
|
configoptions="${configoptions}CONFIG_CBFS_PREFIX=\"$1\""
|
|
shift;;
|
|
-B|--blobs) shift
|
|
customizing="${customizing}, blobs"
|
|
configoptions="${configoptions}CONFIG_USE_AMD_BLOBS=y\nCONFIG_USE_QC_BLOBS=y\nCONFIG_FSP_USE_REPO=y\n"
|
|
;;
|
|
-A|--any-toolchain) shift
|
|
customizing="${customizing}, any-toolchain"
|
|
configoptions="${configoptions}CONFIG_ANY_TOOLCHAIN=y\n"
|
|
;;
|
|
-L|--clang) shift
|
|
clang=true
|
|
customizing="${customizing}, clang"
|
|
configoptions="${configoptions}CONFIG_COMPILER_LLVM_CLANG=y\n# CONFIG_COMPILER_GCC is not set\n"
|
|
;;
|
|
-z|--clean) shift
|
|
customizing="${customizing}, clean"
|
|
clean_work=true
|
|
;;
|
|
-Z|--clean-somewhat) shift
|
|
customizing="${customizing}, clean-somewhat"
|
|
clean_objs=true
|
|
;;
|
|
-o|--outdir) shift
|
|
TARGET=$1; shift
|
|
;;
|
|
-n|--name) shift
|
|
TESTRUN=$1
|
|
shift;;
|
|
-x|--chromeos) shift
|
|
chromeos=true
|
|
testclass=chromeos
|
|
customizing="${customizing}, chromeos"
|
|
configoptions="${configoptions}CONFIG_CHROMEOS=y\nCONFIG_VBOOT_MEASURED_BOOT=y\n"
|
|
;;
|
|
-X|--xmlfile) shift
|
|
XMLFILE=$1
|
|
REAL_XMLFILE=$1
|
|
shift;;
|
|
-I|--recursive) shift; recursive=true;;
|
|
-K|--kconfig) shift
|
|
testclass="$(basename "$1" | tr '.' '_' )"
|
|
customizing="${customizing}, $1 config"
|
|
configoptions="$(cat "$1")${configoptions}\n"
|
|
shift;;
|
|
--checksum) shift; checksum_file="$1"; shift;;
|
|
--timeless) shift; TIMELESS=1;;
|
|
--) shift; break;;
|
|
-*) printf "Invalid option '%s'\n\n" "$1"; myhelp; exit 1;;
|
|
*) break;;
|
|
esac
|
|
done
|
|
|
|
if [[ "${TESTRUN}" != "${TESTRUN_DEFAULT}" ]]; then
|
|
unset testclass
|
|
if [[ "${XML_UPDATED}" != "${XML_DEFAULT}" ]]; then
|
|
XMLFILE="abuild-${TESTRUN}.xml"
|
|
REAL_XMLFILE="${XMLFILE}"
|
|
fi
|
|
if [[ "${TARGET}" == "${TARGET_DEFAULT}" ]]; then
|
|
TARGET="${TESTRUN}"
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$1" ]; then
|
|
printf "Invalid option '%s'\n\n" "$1"; myhelp; exit 1;
|
|
fi
|
|
|
|
if [ -z "$TARGET" ] || [ "$TARGET" = "/" ]; then
|
|
echo "Please specify a valid, non-root build directory."
|
|
exit 1
|
|
fi
|
|
|
|
if ! mkdir -p "$TARGET"; then
|
|
echo "Unable to create build directory"
|
|
exit 1
|
|
fi
|
|
|
|
if echo "${skipconfig_set}${skipconfig_unset}" | grep -q "CONFIG_" >/dev/null 2>&1; then
|
|
echo "Error: Do not include CONFIG_ in the Kconfig value to skip"
|
|
exit 1
|
|
fi
|
|
|
|
customizing=$(echo "$customizing" | cut -c3-)
|
|
if [ "$customizing" = "" ]; then
|
|
customizing="default configuration"
|
|
fi
|
|
|
|
FAILED_BOARDS="$(realpath ${TARGET}/failed_boards)"
|
|
PASSED_BOARDS="$(realpath ${TARGET}/passing_boards)"
|
|
|
|
# Generate a single xcompile for all boards
|
|
export xcompile="${TARGET}/xcompile"
|
|
|
|
if [ "$recursive" = "false" ]; then
|
|
rm -f "${xcompile}"
|
|
$MAKE -C"${ROOT}" obj="$TARGET/temp" objutil="$TARGET/sharedutils" UPDATED_SUBMODULES=1 "${xcompile}" || exit 1
|
|
rm -f "$FAILED_BOARDS" "$PASSED_BOARDS"
|
|
fi
|
|
|
|
USE_XARGS=0
|
|
if [ "$cpus" != "1" ]; then
|
|
# Limit to 32 parallel builds for now.
|
|
# Thrashing all caches because we run
|
|
# 160 abuilds in parallel is no fun.
|
|
if [ "$cpus" = "max" ]; then
|
|
cpus=32
|
|
fi
|
|
# Test if xargs supports the non-standard -P flag
|
|
# FIXME: disabled until we managed to eliminate all the make(1) quirks
|
|
echo | xargs -P ${cpus:-0} -n 1 echo 2>/dev/null >/dev/null && USE_XARGS=1
|
|
fi
|
|
|
|
if [ "$USE_XARGS" = "0" ]; then
|
|
test "$MAKEFLAGS" == "" && test "$cpus" != "" && export MAKEFLAGS="-j $cpus"
|
|
export MAKEFLAGS="$MAKEFLAGS UPDATED_SUBMODULES=1" # no need to re-download
|
|
build_targets()
|
|
{
|
|
local targets=${*-$(get_mainboards)}
|
|
for MAINBOARD in $targets; do
|
|
build_target "${MAINBOARD}"
|
|
done
|
|
}
|
|
else
|
|
build_targets()
|
|
{
|
|
local ABSPATH
|
|
local stime
|
|
local etime
|
|
local num_targets
|
|
local cpus_per_target
|
|
|
|
local targets=${*-$(get_mainboards)}
|
|
# seed shared utils
|
|
TMPCFG=$(mktemp)
|
|
printf "%s" "$configoptions" > "$TMPCFG"
|
|
$MAKE -j "$cpus" DOTCONFIG="$TMPCFG" obj="$TARGET/temp" objutil="$TARGET/sharedutils" olddefconfig 2>/dev/null
|
|
BUILDPREFIX=
|
|
if [ "$scanbuild" = "true" ]; then
|
|
scanbuild_out=$TARGET/sharedutils-scanbuild
|
|
rm -rf "${scanbuild_out}"
|
|
BUILDPREFIX="scan-build -o ${scanbuild_out}tmp"
|
|
fi
|
|
mkdir -p "$TARGET/abuild"
|
|
ABSPATH="$(cd "$TARGET/abuild" && pwd)"
|
|
local XMLFILE="$ABSPATH/__util.xml"
|
|
rm -f "${XMLFILE}"
|
|
stime=$(perl -e 'print time();' 2>/dev/null || date +%s)
|
|
$BUILDPREFIX "$MAKE" -j "$cpus" DOTCONFIG="$TMPCFG" obj="$TARGET/temp" objutil="$TARGET/sharedutils" tools > "$TARGET/sharedutils/make.log" 2>&1
|
|
local ret=$?
|
|
etime=$(perl -e 'print time();' 2>/dev/null || date +%s)
|
|
local duration=$(( etime - stime ))
|
|
|
|
junit " <testcase classname='util' name='all' time='$duration' >"
|
|
if [ $ret -eq 0 ]; then
|
|
junit "<system-out>"
|
|
junitfile "$TARGET/sharedutils/make.log"
|
|
junit "</system-out>"
|
|
junit "</testcase>"
|
|
else
|
|
junit "<failure type='BuildFailed'>"
|
|
junitfile "$TARGET/sharedutils/make.log"
|
|
junit "</failure>"
|
|
junit "</testcase>"
|
|
echo "Shared Utilities - Log: $TARGET/sharedutils/make.log" >> "$FAILED_BOARDS"
|
|
rm "$TMPCFG"
|
|
return
|
|
fi
|
|
|
|
if [ "$scanbuild" = "true" ]; then
|
|
mv "${scanbuild_out}tmp/"* "${scanbuild_out}"
|
|
rmdir "${scanbuild_out}tmp"
|
|
fi
|
|
rm -rf "$TARGET/temp" "$TMPCFG"
|
|
num_targets=$(wc -w <<<"$targets")
|
|
cpus_per_target=$(((${cpus:-1} + num_targets - 1) / num_targets))
|
|
echo "$targets" | xargs -P ${cpus:-0} -n 1 "$0" "${cmdline[@]}" -I -c "$cpus_per_target" -t
|
|
}
|
|
fi
|
|
|
|
junit '<?xml version="1.0" encoding="utf-8"?>'
|
|
junit '<testsuite>'
|
|
|
|
if [ "$target" != "" ]; then
|
|
# build a single board
|
|
MAINBOARD=$(normalize_target "${target}")
|
|
if [ -z "${MAINBOARD}" ]; then
|
|
printf "No such target: %s" "${target}"
|
|
if [ -n "${variant}" ]; then
|
|
printf ", variant: %s" "${variant}"
|
|
fi
|
|
printf "\n"
|
|
exit 1
|
|
fi
|
|
build_srcdir="$(mainboard_directory "${MAINBOARD}")"
|
|
if [ "$(echo "${MAINBOARD}" | wc -w)" -gt 1 ]; then
|
|
build_targets "${MAINBOARD}"
|
|
elif [ ! -r "$ROOT/src/mainboard/${build_srcdir}" ]; then
|
|
echo "No such target: ${MAINBOARD}"
|
|
exit 1
|
|
else
|
|
build_target "${MAINBOARD}"
|
|
test "$mode" != "text" && \
|
|
test -f "$TARGET/abuild/${MAINBOARD}.xml" && \
|
|
cat "$TARGET/abuild/${MAINBOARD}.xml" >> "$REAL_XMLFILE"
|
|
XMLFILE=$REAL_XMLFILE
|
|
fi
|
|
else
|
|
build_targets
|
|
rm -f "$REAL_XMLFILE"
|
|
XMLFILE="$REAL_XMLFILE"
|
|
junit '<?xml version="1.0" encoding="utf-8"?>'
|
|
junit '<testsuite>'
|
|
if [ "$mode" != "text" ]; then
|
|
for xmlfile in $TARGET/abuild/*_*.xml; do
|
|
cat "$xmlfile" >> "$REAL_XMLFILE"
|
|
done
|
|
fi
|
|
XMLFILE=$REAL_XMLFILE
|
|
fi
|
|
junit '</testsuite>'
|
|
|
|
if [ "$recursive" = "false" ]; then
|
|
|
|
# Print the list of failed configurations
|
|
if [ -f "$FAILED_BOARDS" ]; then
|
|
printf "%s configuration(s) failed:\n" "$( wc -l < "$FAILED_BOARDS" )"
|
|
cat "$FAILED_BOARDS"
|
|
echo
|
|
if [ "$exitcode" != "0" ]; then
|
|
failed=1
|
|
fi
|
|
elif [ -f "$PASSED_BOARDS" ]; then
|
|
printf "All %s tested configurations passed.\n" "$( wc -l < "$PASSED_BOARDS" )"
|
|
else
|
|
printf "No boards tested.\n"
|
|
fi
|
|
fi
|
|
|
|
exit $failed
|