/ util / abuild / abuild
abuild
   1  #!/usr/bin/env bash
   2  #
   3  #  coreboot autobuild
   4  #
   5  #  This script builds coreboot images for all available targets.
   6  #
   7  #  This file is subject to the terms and conditions of the GNU General
   8  #  Public License. See the file COPYING in the main directory of this
   9  #  archive for more details.
  10  #
  11  
  12  #set -x # Turn echo on....
  13  
  14  ABUILD_DATE="Nov 18, 2023"
  15  ABUILD_VERSION="0.11.03"
  16  
  17  TOP=$PWD
  18  
  19  # Where shall we place all the build trees?
  20  TARGET_DEFAULT=coreboot-builds
  21  TARGET=${COREBOOT_BUILD_DIR:-${TARGET_DEFAULT}}
  22  XML_DEFAULT="$TOP/abuild.xml"
  23  XMLFILE="${XML_DEFAULT}"
  24  REAL_XMLFILE="${XML_DEFAULT}"
  25  
  26  # Name associated with a run of abuild
  27  TESTRUN_DEFAULT=default
  28  TESTRUN="${TESTRUN_DEFAULT}"
  29  
  30  export KCONFIG_OVERWRITECONFIG=1
  31  
  32  # path to payload. Should be more generic
  33  PAYLOAD=/dev/null
  34  
  35  # get path to coreboot XGCC if it's not already set
  36  if [ -z "$XGCCPATH" ]; then
  37  	XGCCPATH="${TOP}/util/crossgcc/xgcc/bin/"
  38  fi
  39  
  40  # Add XGCC to the path.
  41  if [ -d "$XGCCPATH" ] && [[ ":$PATH:" != *":$XGCCPATH:"* ]]; then
  42  	PATH="$XGCCPATH:$PATH"
  43  fi
  44  
  45  # Lines of error context to be printed in FAILURE case
  46  CONTEXT=12
  47  
  48  # Configure-only mode
  49  configureonly=0
  50  
  51  # Did any board fail to build?
  52  failed=0
  53  
  54  # Exit with a non-zero errorlevel on failure
  55  exitcode=0
  56  
  57  # default: don't save checksums
  58  checksum_file=""
  59  
  60  # default: single CPU build
  61  cpus=1
  62  
  63  # change with -d <directory>
  64  configdir="$TOP/configs"
  65  
  66  # Timeless builds
  67  TIMELESS=0
  68  
  69  # One might want to adjust these in case of cross compiling
  70  for i in make gmake gnumake nonexistent_make; do
  71  	$i --version 2>/dev/null |grep "GNU Make" >/dev/null && break
  72  done
  73  if [ "$i" = "nonexistent_make" ]; then
  74  	echo No GNU Make found.
  75  	exit 1
  76  fi
  77  MAKE=$i
  78  
  79  # this can be changed to junit by -J
  80  mode=text
  81  
  82  # quiet mode: only print pass, failure, and 'skipped' messages
  83  quiet=false
  84  
  85  # clang mode enabled by -sb option.
  86  scanbuild=false
  87  
  88  # Mark whether abuild was called recursively
  89  recursive=false
  90  
  91  # Skip builds with this Kconfig value set
  92  skipconfig_set=""
  93  
  94  # Skip builds with this Kconfig value notset
  95  skipconfig_unset=""
  96  
  97  # EPOCHTIME
  98  ts_exec_shell=$(printf "%(%s)T" -2)
  99  ts_basetime_str=$(date -u --date=@${ts_exec_shell})
 100  
 101  trap interrupt INT
 102  
 103  function interrupt
 104  {
 105  	printf "\n%s: execution interrupted manually.\n" "$0"
 106  	if [ "$mode" == "junit" ]; then
 107  		printf "%s: deleting incomplete xml output file.\n" "$0"
 108  	fi
 109  	exit 1
 110  }
 111  
 112  function debug
 113  {
 114  	test "$verbose" == "true" && echo "$*"
 115  }
 116  
 117  function junit
 118  {
 119  	test "$mode" == "junit" && echo "$*" >> "$XMLFILE"
 120  	return 0
 121  }
 122  
 123  function junitfile
 124  {
 125  	test "$mode" == "junit" && {
 126  		printf '<![CDATA[\n'
 127  		cat "$1"
 128  		printf ']]>\n'
 129  	} >> "$XMLFILE"
 130  }
 131  
 132  # Return mainboard descriptors.
 133  # By default all mainboards are listed, but when passing a two-level path
 134  # below src/mainboard, such as emulation/qemu-i440fx, or emulation/*, it
 135  # returns all board descriptors in that hierarchy.
 136  function get_mainboards
 137  {
 138  	local search_space=${1-*/*}
 139  	# shellcheck disable=SC2086
 140  	grep -h "^[[:space:]]*config\>[[:space:]]*\<BOARD_" \
 141  		${ROOT}/src/mainboard/${search_space}/Kconfig.name 2>/dev/null | \
 142  		sed "s,^.*\<BOARD_\([A-Z0-9_]*\)\>.*$,\1,"
 143  }
 144  
 145  # Given a mainboard descriptor, return its directory below src/mainboard
 146  function mainboard_directory
 147  {
 148  	local MAINBOARD=$1
 149  
 150  	# shellcheck disable=SC2086
 151  	grep -l "^[[:space:]]*config\>[[:space:]]*\<BOARD_${MAINBOARD}\>" \
 152  		${ROOT}/src/mainboard/*/*/Kconfig.name | \
 153  		sed "s:^$ROOT/src/mainboard/\(.*\)/Kconfig.name$:\1:"
 154  }
 155  
 156  # Given a mainboard descriptor, return its vendor (CONFIG_VENDOR_*)
 157  function mainboard_vendor
 158  {
 159  	local MAINBOARD=$1
 160  	local kconfig_file
 161  
 162  	# shellcheck disable=SC2086
 163  	kconfig_file=$( \
 164  	grep -l "^[[:space:]]*config\>[[:space:]]*\<BOARD_${MAINBOARD}\>" \
 165  		${ROOT}/src/mainboard/*/*/Kconfig.name | \
 166  		sed "s:^\(${ROOT}/src/mainboard/.*\)/.*/\(Kconfig.name\)$:\1/\2:" )
 167  	if [ ! -f "$kconfig_file" ]; then
 168  		exit 1
 169  	fi
 170  	grep "^[[:space:]]*config\>[[:space:]]*\<VENDOR_" "$kconfig_file" | \
 171  		sed "s,^.*\<VENDOR_\([A-Z0-9_]*\)\>.*$,\1,"
 172  }
 173  
 174  # Accepts directory names (eg. emulation/qemu-i440fx) and mainboard
 175  # descriptors (eg. EMULATION_QEMU_X86_I440F} and returns the latter
 176  # format.
 177  # If a directory contains multiple boards, returns them all.
 178  function normalize_target
 179  {
 180  	# TODO: Change 'targets' variable to an array
 181  	local targets
 182  	local VARIANT_UC
 183  
 184  	VARIANT_UC=$(echo "${variant}" | tr '[:lower:]' '[:upper:]' | tr '-' '_')
 185  
 186  	targets=$(get_mainboards "$1")
 187  	if [ -n "$targets" ]; then
 188  		# shellcheck disable=SC2086
 189  		targets="$(grep "${VARIANT_UC}\$" <<< ${targets})"
 190  		echo "$targets"
 191  		return
 192  	fi
 193  
 194  	targets=$(echo "$1" | tr ',' ' ')
 195  	for i in $targets; do
 196  		if [ -n "$(mainboard_directory "$i")" ]; then
 197  			echo "$i"
 198  		else
 199  			echo "$i is not a valid target" >&2
 200  			exit 1
 201  		fi
 202  	done
 203  }
 204  
 205  # shellcheck disable=SC2129
 206  function create_config
 207  {
 208  	local BUILD_NAME=$1
 209  	local build_dir=$2
 210  	local board_srcdir
 211  
 212  	local config_file="${build_dir}/config.build"
 213  	board_srcdir="$(mainboard_directory "${BUILD_NAME}")"
 214  
 215  	mkdir -p "${build_dir}"
 216  	mkdir -p "$TARGET/sharedutils"
 217  
 218  	if [ "$quiet" == "false" ]; then echo "  Creating config file for $BUILD_NAME..."; fi
 219  	echo "CONFIG_VENDOR_$(mainboard_vendor "${BUILD_NAME}")=y" > "${config_file}"
 220  	echo "CONFIG_BOARD_${BUILD_NAME}=y" >> "${config_file}"
 221  	grep "select[\t ]*ARCH" "${ROOT}/src/mainboard/${board_srcdir}/Kconfig" | \
 222  		sed "s,^.*\(ARCH_.*\)[^A-Z0-9_]*,CONFIG_\1=y," >> "${config_file}"
 223  	echo "CONFIG_MAINBOARD_DIR=\"${board_srcdir}\"" >> "${config_file}"
 224  
 225  	update_config "$BUILD_NAME" "$build_dir" "$config_file"
 226  
 227  	ret=$?
 228  	if [ $ret -eq 0 ]; then
 229  		if [ "$quiet" == "false" ]; then echo "  $BUILD_NAME config created."; fi
 230  		return 0
 231  	else
 232  		# Does this ever happen?
 233  		if [ "$quiet" == "false" ]; then printf "%s config creation FAILED!\nLog excerpt:\n" "$BUILD_NAME"; fi
 234  		tail -n $CONTEXT "$build_dir/config.log" 2> /dev/null || tail -$CONTEXT "$build_dir/config.log"
 235  		return 1
 236  	fi
 237  }
 238  
 239  function update_config
 240  {
 241  	local BUILD_NAME=$1
 242  	local build_dir=$2
 243  	local config_file=$3
 244  
 245  	local PAYLOAD
 246  	local defconfig_file
 247  	defconfig_file=${build_dir}/config.$(echo "${BUILD_NAME}" | tr '[:upper:]' '[:lower:]').default
 248  
 249  	# get a working payload for the board if we have one.
 250  	# the --payload option expects a directory containing
 251  	# a shell script payload.sh
 252  	#   Usage: payload.sh [BOARD]
 253  	# the script returns an absolute path to the payload binary.
 254  
 255  	if [ -f "$payloads/payload.sh" ]; then
 256  		PAYLOAD=$(sh "$payloads/payload.sh" "$BUILD_NAME")
 257  		local PAYLOAD_OK=$?
 258  		if [ $PAYLOAD_OK -gt 0 ]; then
 259  			echo "problem with payload"
 260  			exit 1
 261  		fi
 262  		if [ "$quiet" == "false" ]; then printf "Using payload %s\n" "$PAYLOAD"; fi
 263  	elif [ "$payloads" = "none" ]; then
 264  		PAYLOAD=none
 265  	fi
 266  
 267  	if [ "$PAYLOAD" = "none" ]; then
 268  		{
 269  			echo "CONFIG_PAYLOAD_NONE=y"
 270  			echo "# CONFIG_PAYLOAD_ELF is not set"
 271  		} >> "${config_file}"
 272  	elif [ "$PAYLOAD" != "/dev/null" ]; then
 273  		{
 274  			echo "# CONFIG_PAYLOAD_NONE is not set"
 275  			echo "CONFIG_PAYLOAD_ELF=y"
 276  			echo "CONFIG_PAYLOAD_FILE=\"$PAYLOAD\""
 277  		} >> "${config_file}"
 278  	fi
 279  	# Disable all other payload config options
 280  	{
 281  		echo "# CONFIG_PAYLOAD_SEABIOS is not set"
 282  		echo "# CONFIG_PAYLOAD_FILO is not set"
 283  		echo "# CONFIG_PAYLOAD_GRUB2 is not set"
 284  		echo "# CONFIG_PAYLOAD_DEPTHCHARGE is not set"
 285  		echo "# CONFIG_PAYLOAD_LINUXBOOT is not set"
 286  		echo "# CONFIG_PAYLOAD_UBOOT is not set"
 287  		echo "# CONFIG_PAYLOAD_EDK2 is not set"
 288  		echo "# CONFIG_PXE is not set"
 289  		echo "# CONFIG_BUILD_IPXE is not set"
 290  		echo "# CONFIG_MEMTEST_SECONDARY_PAYLOAD is not set"
 291  		echo "# CONFIG_COREINFO_SECONDARY_PAYLOAD is not set"
 292  		echo "# CONFIG_NVRAMCUI_SECONDARY_PAYLOAD is not set"
 293  		echo "# CONFIG_TINT_SECONDARY_PAYLOAD is not set"
 294  	} >> "${config_file}"
 295  
 296  	if [ "$quiet" == "false" ]; then echo "    $MAINBOARD ($customizing)"; fi
 297  	# shellcheck disable=SC2059
 298  	printf "$configoptions" >> "${config_file}"
 299  
 300  	$MAKE olddefconfig "$verboseopt" "DOTCONFIG=${config_file}" "obj=${build_dir}" "objutil=$TARGET/sharedutils" &> "${build_dir}/config.log" ; \
 301  		CONFIG_OK=$?
 302  	if [ $CONFIG_OK -eq 0 ]; then
 303  		$MAKE savedefconfig "$verboseopt" DEFCONFIG="${defconfig_file}" DOTCONFIG="${config_file}" obj="${build_dir}" objutil="$TARGET/sharedutils" &>> "${build_dir}/config.log"
 304  		return $?
 305  	else
 306  		return 1
 307  	fi
 308  }
 309  
 310  # shellcheck disable=SC2129
 311  function create_buildenv
 312  {
 313  	local BUILD_NAME=$1
 314  	local build_dir=$2
 315  	local config_file=$3
 316  
 317  	if [ -z "$config_file" ]; then
 318  		create_config "$BUILD_NAME" "$build_dir"
 319  	else
 320  		local new_config_file="${build_dir}/config.build"
 321  		cp "$config_file" "$new_config_file"
 322  		update_config "$BUILD_NAME" "$build_dir" "$new_config_file"
 323  	fi
 324  	local ret=$?
 325  
 326  	# Allow simple "make" in the target directory
 327  	local MAKEFILE=$TARGET/${BUILD_NAME}/Makefile
 328  	echo "# autogenerated" > "$MAKEFILE"
 329  	echo "TOP=$ROOT" >> "$MAKEFILE"
 330  	echo "BUILD=$TARGET" >> "$MAKEFILE"
 331  	echo "OBJ=\$(BUILD)/${MAINBOARD}" >> "$MAKEFILE"
 332  	echo "OBJUTIL=\$(BUILD)/sharedutils" >> "$MAKEFILE"
 333  	echo "all:" >> "$MAKEFILE"
 334  	echo "	@cp -a config.h config.h.bak" >> "$MAKEFILE"
 335  	echo "	@cd \$(TOP); \$(MAKE) olddefconfig DOTCONFIG=\$(OBJ)/config.build objutil=\$(OBJUTIL) obj=\$(OBJ)" >> "$MAKEFILE"
 336  	echo "	@tail -n+6 config.h > config.new; tail -n+6 config.h.bak > config.old" >> "$MAKEFILE"
 337  	echo "	@cmp -s config.new config.old && cp -a config.h.bak config.h || echo \"Config file changed\"" >> "$MAKEFILE"
 338  	echo "	@rm config.h.bak config.new config.old" >> "$MAKEFILE"
 339  	echo "	@cd \$(TOP); \$(MAKE) DOTCONFIG=\$(OBJ)/config.build objutil=\$(OBJUTIL) obj=\$(OBJ)" >> "$MAKEFILE"
 340  
 341  	return $ret
 342  }
 343  
 344  function check_config
 345  {
 346  	local BUILD_DIR="$1"
 347  	local TEST_TYPE="$2"
 348  	local TEST_STRING="$3"
 349  	local NEGATE="$4"
 350  
 351  	local CONFIG_FILE="$BUILD_DIR/config.build"
 352  	local CONFIG_LOG="$BUILD_DIR/config.log"
 353  
 354  	if [ -z "$NEGATE" ]; then
 355  		if ! grep -q "$TEST_STRING" "$CONFIG_FILE"; then
 356  			echo "config file: $CONFIG_FILE has incorrect $TEST_TYPE"
 357  			echo "Error: Expected '$TEST_STRING' in config file." >> "$CONFIG_LOG"
 358  			return 1
 359  		fi
 360  	else
 361  		if grep -q "$TEST_STRING" "$CONFIG_FILE"; then
 362  			echo "config file: $CONFIG_FILE has incorrect $TEST_TYPE"
 363  			echo "Error: Expected not to see '$TEST_STRING' in config file." >> "$CONFIG_LOG"
 364  			return 1
 365  		fi
 366  	fi
 367  
 368  	return 0
 369  }
 370  
 371  # Counting microseconds since start of shell
 372  function add_timestamp
 373  {
 374  	local now=${EPOCHREALTIME}
 375  	local seconds=$(echo $now | cut -f 1 -d '.')
 376  	local usecs=$(echo $now | cut -f 2 -d '.')
 377  	seconds=$(( seconds - ts_exec_shell ))
 378  	usecs=$(( seconds * 1000 * 1000 + 10#$usecs ))
 379  	printf "%s" $usecs
 380  }
 381  
 382  function ts_delta_seconds
 383  {
 384  	local delta=$(( ($2 - $1) / (1000 * 1000) ))
 385  	printf "%s" $delta
 386  }
 387  
 388  function ts_delta_string
 389  {
 390  	local ts_minutes
 391  	local ts_seconds
 392  	local delta
 393  
 394  	delta=$(( ($2 - $1) / 1000 ))
 395  	ts_minutes=$(( delta / (60 * 1000) ))
 396  	delta=$(( delta % (60 * 1000) ))
 397  	ts_seconds=$(( delta / 1000))
 398  	delta=$(( delta % 1000 ))
 399  
 400  	if [ $ts_minutes -ne 0 ] ; then
 401  		printf "%d min %d sec" $ts_minutes $ts_seconds
 402  	else
 403  		printf "%d.%03d seconds" $ts_seconds $delta
 404  	fi
 405  }
 406  
 407  function compile_target
 408  {
 409  	local BUILD_NAME=$1
 410  
 411  	if [ "$quiet" == "false" ]; then echo "  Compiling $MAINBOARD image$cpuconfig..."; fi
 412  
 413  	CURR=$( pwd )
 414  
 415  	ts_1=$(add_timestamp)
 416  	eval "$BUILDPREFIX" "$MAKE" "$verboseopt" DOTCONFIG="${build_dir}/config.build" obj="${build_dir}" objutil="$TARGET/sharedutils" BUILD_TIMELESS=$TIMELESS \
 417  		&> "${build_dir}/make.log" ; \
 418  		MAKE_FAILED=$?
 419  	ts_2=$(add_timestamp)
 420  
 421  	cd "${build_dir}" || return $?
 422  
 423  	timestamps="abuild.timestamps"
 424  	printf "Build started %s\n" "${ts_basetime_str}" > "${timestamps}"
 425  	printf "BASETIME_SECONDS %d\n" $ts_exec_shell >> "${timestamps}"
 426  	printf "TS_0 %d\n" $ts_0 >> "${timestamps}"
 427  	printf "TS_1 %d\n" $ts_1 >> "${timestamps}"
 428  	printf "TS_2 %d\n" $ts_2 >> "${timestamps}"
 429  
 430  	duration=$(ts_delta_seconds $ts_0 $ts_2)
 431  	duration_str=$(ts_delta_string $ts_0 $ts_2)
 432  	junit " <testcase classname='${TESTRUN}${testclass/#/.}' name='$BUILD_NAME' time='$duration' >"
 433  
 434  	if [ $MAKE_FAILED -eq 0 ]; then
 435  		junit "<system-out>"
 436  		junitfile make.log
 437  		junit "</system-out>"
 438  		printf "ok\n" > compile.status
 439  		printf "%s built successfully. (took %s)\n" "$BUILD_NAME" "${duration_str}"
 440  		echo "$BUILD_NAME" >> "$PASSED_BOARDS"
 441  	else
 442  		junit "<failure type='BuildFailed'>"
 443  		junitfile make.log
 444  		junit "</failure>"
 445  		printf "failed\n" > compile.status
 446  		printf "%s build FAILED after %s!\nLog excerpt:\n" "$BUILD_NAME" "${duration_str}"
 447  		tail -n $CONTEXT make.log 2> /dev/null || tail -$CONTEXT make.log
 448  		if [ "$clean_work" = "true" ]; then
 449  			echo "$BUILD_NAME" >> "$FAILED_BOARDS"
 450  		else
 451  			echo "$BUILD_NAME - Log: ${build_dir}/make.log" >> "$FAILED_BOARDS"
 452  		fi
 453  		failed=1
 454  	fi
 455  	cd "$CURR" || return $?
 456  	if [ -n "$checksum_file" ]; then
 457  		sha256sum "${build_dir}/coreboot.rom" >> "${checksum_file}_platform"
 458  		sort "${build_dir}/config.h" | grep CONFIG_ > "${build_dir}/config.h.sorted"
 459  		sha256sum "${build_dir}/config.h.sorted" >> "${checksum_file}_config"
 460  	fi
 461  
 462  	stats_files="${build_dir}/${timestamps}"
 463  	if [ -f ${build_dir}/ccache.stats ]; then
 464  		stats_files="${stats_files} ${build_dir}/ccache.stats"
 465  	fi
 466  	flock -F -w 0.1 $TARGET/.statslock tar -rf ${stats_archive} ${stats_files} 2> /dev/null
 467  
 468  	if [ "$clean_work" = "true" ]; then
 469  		rm -rf "${build_dir}"
 470  	fi
 471  	if [ "$clean_objs" = "true" ]; then
 472  		find ${build_dir} \! \( -name coreboot.rom -o -name config.h -o -name config.build -o -name make.log \) -type f -exec rm {} +
 473  		find ${build_dir} -type d -exec rmdir -p {} + 2>/dev/null
 474  	fi
 475  	return $MAKE_FAILED
 476  }
 477  
 478  function build_config
 479  {
 480  	local MAINBOARD=$1
 481  	local build_dir=$2
 482  	local BUILD_NAME=$3
 483  	local config_file=$4
 484  	local board_srcdir
 485  	local ret
 486  
 487  	board_srcdir=$(mainboard_directory "${MAINBOARD}")
 488  
 489  	if [ "$(cat "${build_dir}/compile.status" 2>/dev/null)" = "ok" ] && \
 490  		[ "$buildall" = "false" ]; then
 491  		echo "Skipping $BUILD_NAME; (already successful)"
 492  		return
 493  	fi
 494  
 495  	export HOSTCC='gcc'
 496  
 497  	if [ "$chromeos" = true ] && [ "$(grep -c "^[[:space:]]*select[[:space:]]*MAINBOARD_HAS_CHROMEOS\>" "${ROOT}/src/mainboard/${board_srcdir}/Kconfig")" -eq 0 ]; then
 498  		echo "${BUILD_NAME} doesn't support ChromeOS, skipping."
 499  		return
 500  	fi
 501  
 502  	if [ "$quiet" == "false" ]; then echo "Building $BUILD_NAME"; fi
 503  	mkdir -p "$TARGET/${BUILD_NAME}" "$TARGET/abuild"
 504  	ABSPATH="$(cd "$TARGET/abuild" && pwd)"
 505  	XMLFILE="$ABSPATH/${BUILD_NAME}.xml"
 506  	rm -f "${XMLFILE}"
 507  
 508  	ts_0=$(add_timestamp)
 509  
 510  	create_buildenv "$BUILD_NAME" "$build_dir" "$config_file"
 511  	local BUILDENV_CREATED=$?
 512  
 513  	check_config "$build_dir" "mainboard" "CONFIG_BOARD_${MAINBOARD}=y"
 514  	local MAINBOARD_OK=$?
 515  
 516  	check_config "$build_dir" "vendor" "CONFIG_VENDOR_$(mainboard_vendor "${MAINBOARD}")=y"
 517  	local VENDOR_OK=$?
 518  
 519  	if [ "$chromeos" = false ]; then
 520  		# Skip this rule for configs created from templates that already
 521  		# come with CHROMEOS enabled.
 522  		grep -q "^CONFIG_CHROMEOS=y" ${config_file:-/dev/null} || \
 523  		check_config "$build_dir" "ChromeOS" "CONFIG_CHROMEOS=y" negate
 524  		local FORCE_ENABLED_CROS=$?
 525  	else
 526  		local FORCE_ENABLED_CROS=0
 527  	fi
 528  
 529  	if [ "$clang" = true ]; then
 530  		check_config "$build_dir" "clang" "CONFIG_COMPILER_LLVM_CLANG=y"
 531  		if [ $? -ne 0 ]; then
 532  			echo "${MAINBOARD} doesn't support clang, skipping."
 533  			return
 534  		fi
 535  	fi
 536  
 537  	if [ -n "${skipconfig_set}" ]; then
 538  		check_config "${build_dir}" "config value" "CONFIG_${skipconfig_set}=y" negate
 539  		if [ $? -ne 0 ]; then
 540  			echo "${MAINBOARD} has ${skipconfig_set} set. Skipping at user's request."
 541  			return
 542  		fi
 543  	fi
 544  
 545  	if [ -n "${skipconfig_unset}" ]; then
 546  		check_config "${build_dir}" "config value" "CONFIG_${skipconfig_unset}=y"
 547  		if [ $? -ne 0 ]; then
 548  			echo "${MAINBOARD} does not have ${skipconfig_unset} set. Skipping at user's request."
 549  			return
 550  		fi
 551  	fi
 552  
 553  	if [ $BUILDENV_CREATED -ne 0 ] || [ $MAINBOARD_OK -ne 0 ] || [ $VENDOR_OK -ne 0 ] || [ $FORCE_ENABLED_CROS -eq 1 ]; then
 554  		junit " <testcase classname='${TESTRUN}${testclass/#/.}' name='$BUILD_NAME' >"
 555  
 556  		junit "<failure type='BuildFailed'>"
 557  		junitfile "$build_dir/config.log"
 558  		junit "</failure>"
 559  		printf "failed\n" > compile.status
 560  		printf "%s build configuration FAILED!\nLog excerpt:\n" "$BUILD_NAME"
 561  		tail -n $CONTEXT "$build_dir/config.log" 2> /dev/null || tail -$CONTEXT "$build_dir/config.log"
 562  
 563  		junit "</testcase>"
 564  		echo "$BUILD_NAME - Log: $build_dir/config.log" >> "$FAILED_BOARDS"
 565  		return
 566  	fi
 567  
 568  	local required_arches
 569  
 570  	required_arches=$(grep -E "^CONFIG_ARCH_(BOOTBLOCK|R.MSTAGE|VERSTAGE)" "$TARGET/${BUILD_NAME}/config.build" | \
 571  			sed "s,^CONFIG_ARCH_[^_]*_\([^=]*\)=.*$,\1," |sort -u |tr 'A-Z\n\r' 'a-z  ')
 572  
 573  	missing_arches="$($MAKE --no-print-directory -f - \
 574  		REQUIRED_ARCHES="$required_arches" <<'EOF'
 575  include $(xcompile)
 576  .PHONY: missing_arches
 577  missing_arches:
 578  	$(if $(XCOMPILE_COMPLETE),,$(error $(xcompile) is invalid.))
 579  	@echo $(foreach arch,$(REQUIRED_ARCHES),$(if $(filter $(arch),$(SUBARCH_SUPPORTED)),,$(arch)))
 580  EOF
 581  )"
 582  	# shellcheck disable=SC2181
 583  	if [[ $? -ne 0 ]]; then
 584  		echo "Calculating missing_arches failed" >&2
 585  		exit 1
 586  	fi
 587  
 588  	if [ -n "$missing_arches" ]; then
 589  		printf "skipping %s because we're missing compilers for (%s)\n" "$BUILD_NAME" "$missing_arches"
 590  		return
 591  	fi
 592  
 593  	if [ $BUILDENV_CREATED -eq 0 ] && [ $configureonly -eq 0 ]; then
 594  		BUILDPREFIX=
 595  		if [ "$scanbuild" = "true" ]; then
 596  			scanbuild_out=$TARGET/${BUILD_NAME}-scanbuild
 597  			rm -rf "${scanbuild_out}"
 598  			BUILDPREFIX="scan-build ${SCANBUILD_ARGS} -o ${scanbuild_out}tmp"
 599  		fi
 600  		compile_target "${BUILD_NAME}"
 601  		if [ "$scanbuild" = "true" ]; then
 602  			mv "${scanbuild_out}"tmp/* "${scanbuild_out}"
 603  			rmdir "${scanbuild_out}tmp"
 604  		fi
 605  	fi
 606  
 607  	junit "</testcase>"
 608  }
 609  
 610  function record_mainboard
 611  {
 612  	local log=$1
 613  
 614  	if test "$mode" != "text" && test -f "$TARGET/abuild/${log}.xml"; then
 615  		cat "$TARGET/abuild/${log}.xml" >> "$REAL_XMLFILE"
 616  		echo "$TARGET/abuild/${log}.xml written to $REAL_XMLFILE" >&2
 617  	else
 618  		echo "Warning: $TARGET/abuild/${log}.xml not found." >&2
 619  	fi
 620  }
 621  
 622  # One target may build several configs
 623  function build_target
 624  {
 625  	local MAINBOARD=$1
 626  	local MAINBOARD_LC
 627  	MAINBOARD_LC=$(echo "$MAINBOARD" | tr '[:upper:]' '[:lower:]')
 628  
 629  	# look for config files in the config directory that match the boardname
 630  	if [ -n "$( find "$configdir" -maxdepth 1 -name "config.${MAINBOARD_LC}*" -print -quit )" ]; then
 631  		for config in "$configdir/config.${MAINBOARD_LC}"*; do
 632  			BUILD_NAME="${config##*/}"
 633  			BUILD_NAME="${BUILD_NAME##config.}"
 634  			BUILD_NAME=$(echo "${BUILD_NAME}" | tr '[:lower:]' '[:upper:]')
 635  			echo $BUILD_NAME $MAINBOARD
 636  			# If the file in configs/ results in the same build_name as the default config
 637  			# append a '_' to differentiate. Otherwise the default configuration would
 638  			# override the results.
 639  			if [ "${MAINBOARD}" = "${BUILD_NAME}" ]; then
 640  				BUILD_NAME=${BUILD_NAME}"_"
 641  			fi
 642  			echo "Building config $BUILD_NAME"
 643  			build_dir=$TARGET/${BUILD_NAME}
 644  			build_config "$MAINBOARD" "$build_dir" "$BUILD_NAME" "$config"
 645  			record_mainboard "$BUILD_NAME"
 646  			remove_target "$BUILD_NAME"
 647  		done
 648  	fi
 649  
 650  	echo "Building board $MAINBOARD (using default config)"
 651  	build_dir=$TARGET/${MAINBOARD}
 652  	build_config "$MAINBOARD" "$build_dir" "$MAINBOARD"
 653  	record_mainboard "$MAINBOARD"
 654  	remove_target "$MAINBOARD"
 655  }
 656  
 657  function remove_target
 658  {
 659  	if [ "$remove" != "true" ]; then
 660  		return
 661  	fi
 662  
 663  	local BUILD_NAME=$1
 664  
 665  	# Save the generated coreboot.rom file of each board.
 666  	if [ -r "$TARGET/${BUILD_NAME}/coreboot.rom" ]; then
 667  		cp "$TARGET/${BUILD_NAME}/coreboot.rom" \
 668  		   "${BUILD_NAME}_coreboot.rom"
 669  	fi
 670  
 671  	echo "Removing build dir for $BUILD_NAME..."
 672  	rm -rf "${TARGET:?}/${BUILD_NAME}"
 673  
 674  	return
 675  }
 676  
 677  function myhelp
 678  {
 679  cat << __END_OF_HELP
 680  Usage: $0 [options]
 681         $0 [-V|--version]
 682         $0 [-h|--help]
 683  
 684  Options:\n
 685      [-a|--all]                    Build previously succeeded ports as well
 686      [-A|--any-toolchain]          Use any toolchain
 687      [-b|--board-variant <name>]   Build specific board variant under the
 688                                    given target.
 689      [-B|--blobs]                  Allow using binary files
 690      [--checksum <path/basefile>]  Store checksums at path/basefile
 691      [-c|--cpus <numcpus>]         Build on <numcpus> at the same time
 692      [-C|--config]                 Configure-only mode
 693      [-d|--dir <dir>]              Directory containing config files
 694      [-e|--exitcode]               Exit with a non-zero errorlevel on failure
 695      [-J|--junit]                  Write JUnit formatted xml log file
 696      [-K|--kconfig <name>]         Prepend file to generated Kconfig
 697      [-l|--loglevel <num>]         Set loglevel
 698      [-L|--clang]                  Use clang on supported arch
 699      [-n|--name]                   Set build name - also sets xmlfile if not
 700                                    already set
 701      [-o|--outdir <path>]          Store build results in path
 702                                    (defaults to $TARGET)
 703      [-p|--payloads <dir>]         Use payloads in <dir> to build images
 704      [-P|--prefix <name>]          File name prefix in CBFS
 705      [-q|--quiet]                  Print fewer messages
 706      [-r|--remove]                 Remove output dir after build
 707      [-R|--root <path>]            Absolute path to coreboot sources
 708                                    (defaults to $ROOT)
 709      [--scan-build]                Use clang's static analyzer
 710      [--skip_set <value>]          Skip building boards with this Kconfig set
 711      [--skip_unset <value>]        Skip building boards with this Kconfig not set
 712      [--timeless]                  Generate timeless builds
 713      [-t|--target <vendor/board>]  Attempt to build target vendor/board only
 714      [-T|--test]                   Submit image(s) to automated test system
 715      [-u|--update]                 Update existing image
 716      [-v|--verbose]                Print more messages
 717      [-x|--chromeos]               Build with CHROMEOS enabled
 718                                    Skip boards without ChromeOS support
 719      [-X|--xmlfile <name>]         Set JUnit XML log file filename
 720                                    (defaults to $XMLFILE)
 721      [-y|--ccache]                 Use ccache
 722      [-z|--clean]                  Remove build results when finished
 723      [-Z|--clean-somewhat]         Remove build but keep coreboot.rom + config
 724  
 725      [-V|--version]                Print version number and exit
 726      [-h|--help]                   Print this help and exit
 727  
 728      [-s|--silent]                 obsolete
 729  __END_OF_HELP
 730  }
 731  
 732  function myversion
 733  {
 734  	cat << EOF
 735  
 736  coreboot autobuild v$ABUILD_VERSION ($ABUILD_DATE)
 737  
 738  Copyright (C) 2004 by Stefan Reinauer <stepan@openbios.org>
 739  Copyright (C) 2006-2010 by coresystems GmbH <info@coresystems.de>
 740  
 741  This program is free software; you may redistribute it under the terms
 742  of the GNU General Public License. This program has absolutely no
 743  warranty.
 744  
 745  EOF
 746  }
 747  
 748  # default options
 749  target=""
 750  buildall=false
 751  verbose=false
 752  
 753  test -f util/sconfig/sconfig.l && ROOT=$( pwd )
 754  test -f ../util/sconfig/sconfig.l && ROOT=$( cd .. && pwd )
 755  test "$ROOT" = "" && ROOT=$( cd ../.. && pwd )
 756  
 757  # Look if we have getopt. If not, build it.
 758  export PATH=$PATH:util/abuild
 759  getopt - > /dev/null 2>/dev/null || gcc -o util/abuild/getopt util/abuild/getopt.c
 760  
 761  # Save command line for xargs parallelization.
 762  cmdline=("$@")
 763  
 764  # parse parameters.. try to find out whether we're running GNU getopt
 765  getoptbrand="$(getopt -V)"
 766  
 767  # shellcheck disable=SC2086
 768  if [ "${getoptbrand:0:6}" == "getopt" ]; then
 769  	# Detected GNU getopt that supports long options.
 770  	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
 771  	eval set -- $args
 772  	retval=$?
 773  else
 774  	# Detected non-GNU getopt
 775  	args=$(getopt Vvqhat:b:p:c:sJCl:rP:uyBLAZzo:xX:K:d:R:Ien: "$@")
 776  	set -- $args
 777  	retval=$?
 778  fi
 779  
 780  if [ $retval != 0 ]; then
 781  	myhelp
 782  	exit 1
 783  fi
 784  
 785  chromeos=false
 786  clang=false
 787  clean_work=false
 788  clean_objs=false
 789  verboseopt='V=0'
 790  customizing=""
 791  configoptions=""
 792  # testclass needs to be undefined if not used for variable expansion to work
 793  unset testclass
 794  while true ; do
 795  	case "$1" in
 796  		-J|--junit)     shift; mode=junit; rm -f "$XMLFILE" ;;
 797  		-t|--target)	shift; target="$1"; shift;;
 798  		-b|--board-variant)	shift; variant="$1"; shift;;
 799  		-a|--all)	shift; buildall=true;;
 800  		-d|--dir)	shift; configdir="$1"; shift;;
 801  		-e|--exitcode)	shift; exitcode=1;;
 802  		-r|--remove)	shift; remove=true;;
 803  		-v|--verbose)	shift; verbose=true; verboseopt='V=1';;
 804  		-q|--quiet)	shift; quiet=true;;
 805  		-V|--version)	shift; myversion; exit 0;;
 806  		-h|--help)	shift; myversion; myhelp; exit 0;;
 807  		-p|--payloads)  shift; payloads="$1"; shift;;
 808  		-R|--root)	shift; ROOT="$1"; MAKE="$MAKE -C $1"; shift;;
 809  		-c|--cpus)	shift
 810  			export MAKEFLAGS="-j $1"
 811  			cpus=$1
 812  			test "$MAKEFLAGS" == "-j max" && export MAKEFLAGS="-j" && cpuconfig=" in parallel"
 813  			test "$1" == "1" && cpuconfig=" on 1 cpu"
 814  			expr "$1" : '-\?[0-9]\+$' > /dev/null && test "0$1" -gt 1 && cpuconfig=" on $1 cpus in parallel"
 815  			shift;;
 816  		# obsolete option
 817  		-s|--silent)    shift;;
 818  		--scan-build)   shift
 819  			scanbuild=true
 820  			customizing="${customizing}, scan-build"
 821  			SCANBUILD_ARGS=${SCANBUILD_ARGS:-'-k'}
 822  			configoptions="${configoptions}CONFIG_FATAL_ASSERTS=y\n"
 823  			;;
 824  		--skip_set)	shift
 825  			skipconfig_set="$1"
 826  			customizing="${customizing}, Skipping builds with CONFIG_${skipconfig_set}=Y"
 827  			shift
 828  			;;
 829  		--skip_unset)	shift
 830  			skipconfig_unset="$1"
 831  			customizing="${customizing}, Skipping builds with CONFIG_${skipconfig_unset} not set"
 832  			shift
 833  			;;
 834  		--asserts)	shift
 835  			configoptions="${configoptions}CONFIG_FATAL_ASSERTS=y\n"
 836  			;;
 837  		-y|--ccache)    shift
 838  			customizing="${customizing}, ccache"
 839  			configoptions="${configoptions}CONFIG_CCACHE=y\n"
 840  			;;
 841  		-C|--config)    shift; configureonly=1;;
 842  		-l|--loglevel)  shift
 843  			customizing="${customizing}, loglevel $1"
 844  			configoptions="${configoptions}CONFIG_DEFAULT_CONSOLE_LOGLEVEL_$1=y\n"
 845  			configoptions="${configoptions}CONFIG_DEFAULT_CONSOLE_LOGLEVEL=$1\n"
 846  			shift;;
 847  		-u|--update)    shift
 848  			customizing="${customizing}, update"
 849  			configoptions="${configoptions}CONFIG_UPDATE_IMAGE=y\n"
 850  			;;
 851  		-P|--prefix)    shift
 852  			customizing="${customizing}, cbfs prefix $1"
 853  			configoptions="${configoptions}CONFIG_CBFS_PREFIX=\"$1\""
 854  			shift;;
 855  		-B|--blobs)	shift
 856  			customizing="${customizing}, blobs"
 857  			configoptions="${configoptions}CONFIG_USE_AMD_BLOBS=y\nCONFIG_USE_QC_BLOBS=y\nCONFIG_FSP_USE_REPO=y\n"
 858  			;;
 859  		-A|--any-toolchain)	shift
 860  			customizing="${customizing}, any-toolchain"
 861  			configoptions="${configoptions}CONFIG_ANY_TOOLCHAIN=y\n"
 862  			;;
 863  		-L|--clang)	shift
 864  			clang=true
 865  			customizing="${customizing}, clang"
 866  			configoptions="${configoptions}CONFIG_COMPILER_LLVM_CLANG=y\n# CONFIG_COMPILER_GCC is not set\n"
 867  			;;
 868  		-z|--clean)	shift
 869  			customizing="${customizing}, clean"
 870  			clean_work=true
 871  			;;
 872  		-Z|--clean-somewhat)	shift
 873  			customizing="${customizing}, clean-somewhat"
 874  			clean_objs=true
 875  			;;
 876  		-o|--outdir)	shift
 877  			TARGET=$1; shift
 878  			;;
 879  		-n|--name)	shift
 880  			TESTRUN=$1
 881  			shift;;
 882  		-x|--chromeos)	shift
 883  			chromeos=true
 884  			testclass=chromeos
 885  			customizing="${customizing}, chromeos"
 886  			configoptions="${configoptions}CONFIG_CHROMEOS=y\n"
 887  			;;
 888  		-X|--xmlfile)	shift
 889  			XMLFILE=$1
 890  			REAL_XMLFILE=$1
 891  			shift;;
 892  		-I|--recursive) shift; recursive=true;;
 893  		-K|--kconfig)	shift
 894  			testclass="$(basename "$1" | tr '.' '_' )"
 895  			customizing="${customizing}, $1 config"
 896  			configoptions="$(cat "$1")${configoptions}\n"
 897  			shift;;
 898  		--checksum)	shift; checksum_file="$1"; shift;;
 899  		--timeless)	shift; TIMELESS=1;;
 900  		--)		shift; break;;
 901  		-*)		printf "Invalid option '%s'\n\n" "$1"; myhelp; exit 1;;
 902  		*)		break;;
 903  	esac
 904  done
 905  
 906  if [[ "${TESTRUN}" != "${TESTRUN_DEFAULT}" ]]; then
 907  	unset testclass
 908  	if [[ "${XML_UPDATED}" != "${XML_DEFAULT}" ]]; then
 909  		XMLFILE="abuild-${TESTRUN}.xml"
 910  		REAL_XMLFILE="${XMLFILE}"
 911  	fi
 912  	if [[ "${TARGET}" == "${TARGET_DEFAULT}" ]]; then
 913  		TARGET="${TESTRUN}"
 914  	fi
 915  fi
 916  
 917  if [ -n "$1" ]; then
 918  	printf "Invalid option '%s'\n\n" "$1"; myhelp; exit 1;
 919  fi
 920  
 921  if [ -z "$TARGET" ] || [ "$TARGET" = "/" ]; then
 922  	echo "Please specify a valid, non-root build directory."
 923  	exit 1
 924  fi
 925  
 926  if ! mkdir -p "$TARGET"; then
 927  	echo "Unable to create build directory"
 928  	exit 1
 929  fi
 930  
 931  if echo "${skipconfig_set}${skipconfig_unset}" | grep -q "CONFIG_" >/dev/null 2>&1; then
 932  	echo "Error: Do not include CONFIG_ in the Kconfig value to skip"
 933  	exit 1
 934  fi
 935  
 936  customizing=$(echo "$customizing" | cut -c3-)
 937  if [ -z "$customizing" ]; then
 938  	customizing="Default configuration"
 939  fi
 940  customizing="Config: ${customizing}"
 941  FAILED_BOARDS="$(realpath ${TARGET}/failed_boards)"
 942  PASSED_BOARDS="$(realpath ${TARGET}/passing_boards)"
 943  
 944  stats_archive="$TARGET/statistics.tar"
 945  
 946  # Generate a single xcompile for all boards
 947  export xcompile="${TARGET}/xcompile"
 948  
 949  if [ "$recursive" = "false" ]; then
 950  	rm -f "${xcompile}"
 951  	$MAKE -C"${ROOT}" obj="$TARGET/temp" objutil="$TARGET/sharedutils" UPDATED_SUBMODULES=1 "${xcompile}" || exit 1
 952  	rm -f "$FAILED_BOARDS" "$PASSED_BOARDS"
 953  
 954  	# Initialize empty statistics archive
 955  	tar -cf "${stats_archive}" "${xcompile}" 2> /dev/null
 956  fi
 957  
 958  USE_XARGS=0
 959  if [ "$cpus" != "1" ]; then
 960  	# Limit to 32 parallel builds for now.
 961  	# Thrashing all caches because we run
 962  	# 160 abuilds in parallel is no fun.
 963  	if [ "$cpus" = "max" ]; then
 964  		cpus=32
 965  	fi
 966  	# Test if xargs supports the non-standard -P flag
 967  	# FIXME: disabled until we managed to eliminate all the make(1) quirks
 968  	echo | xargs -P ${cpus:-0} -n 1 echo 2>/dev/null >/dev/null && USE_XARGS=1
 969  fi
 970  
 971  if [ "$USE_XARGS" = "0" ]; then
 972  test "$MAKEFLAGS" == "" && test "$cpus" != "" && export MAKEFLAGS="-j $cpus"
 973  export MAKEFLAGS="$MAKEFLAGS UPDATED_SUBMODULES=1" # no need to re-download
 974  build_targets()
 975  {
 976  	local targets=${*-$(get_mainboards)}
 977  	for MAINBOARD in $targets; do
 978  		build_target "${MAINBOARD}"
 979  	done
 980  }
 981  else
 982  build_targets()
 983  {
 984  	local ABSPATH
 985  	local stime
 986  	local etime
 987  	local num_targets
 988  	local cpus_per_target
 989  
 990  	local targets=${*-$(get_mainboards)}
 991  	# seed shared utils
 992  	TMPCFG=$(mktemp)
 993  	printf "%s" "$configoptions" > "$TMPCFG"
 994  	$MAKE -j "$cpus" DOTCONFIG="$TMPCFG" obj="$TARGET/temp" objutil="$TARGET/sharedutils" olddefconfig 2>/dev/null
 995  	BUILDPREFIX=
 996  	if [ "$scanbuild" = "true" ]; then
 997  		scanbuild_out=$TARGET/sharedutils-scanbuild
 998  		rm -rf "${scanbuild_out}"
 999  		BUILDPREFIX="scan-build -o ${scanbuild_out}tmp"
1000  	fi
1001  	mkdir -p "$TARGET/abuild"
1002  	ABSPATH="$(cd "$TARGET/abuild" && pwd)"
1003  	local XMLFILE="$ABSPATH/__util.xml"
1004  	rm -f "${XMLFILE}"
1005  	stime=$(add_timestamp)
1006  	$BUILDPREFIX "$MAKE" -j "$cpus" DOTCONFIG="$TMPCFG" obj="$TARGET/temp" objutil="$TARGET/sharedutils" tools > "$TARGET/sharedutils/make.log" 2>&1
1007  	local ret=$?
1008  	etime=$(add_timestamp)
1009  	local duration=$(ts_delta_seconds $stime $etime)
1010  
1011  	junit " <testcase classname='util' name='all' time='$duration' >"
1012  	if [ $ret -eq 0 ]; then
1013  		junit "<system-out>"
1014  		junitfile "$TARGET/sharedutils/make.log"
1015  		junit "</system-out>"
1016  		junit "</testcase>"
1017  	else
1018  		junit "<failure type='BuildFailed'>"
1019  		junitfile "$TARGET/sharedutils/make.log"
1020  		junit "</failure>"
1021  		junit "</testcase>"
1022  		echo "Shared Utilities - Log: $TARGET/sharedutils/make.log" >> "$FAILED_BOARDS"
1023  		rm "$TMPCFG"
1024  		return
1025  	fi
1026  
1027  	if [ "$scanbuild" = "true" ]; then
1028  		mv "${scanbuild_out}tmp/"* "${scanbuild_out}"
1029  		rmdir "${scanbuild_out}tmp"
1030  	fi
1031  	rm -rf "$TARGET/temp" "$TMPCFG"
1032  	num_targets=$(wc -w <<<"$targets")
1033  	cpus_per_target=$(((${cpus:-1} + num_targets - 1) / num_targets))
1034  	echo "$targets" | xargs -P ${cpus:-0} -n 1 "$0" "${cmdline[@]}" -I -c "$cpus_per_target" -t
1035  }
1036  fi
1037  
1038  junit '<?xml version="1.0" encoding="utf-8"?>'
1039  junit '<testsuite>'
1040  
1041  if [ "$target" != "" ]; then
1042  	# build a single board
1043  	MAINBOARD=$(normalize_target "${target}")
1044  	if [ -z "${MAINBOARD}" ]; then
1045  		printf "No such target: %s" "${target}"
1046  		if [ -n "${variant}" ]; then
1047  			printf ", variant: %s" "${variant}"
1048  		fi
1049  		printf "\n"
1050  		exit 1
1051  	fi
1052  	build_srcdir="$(mainboard_directory "${MAINBOARD}")"
1053  	if [ "$(echo "${MAINBOARD}" | wc -w)" -gt 1 ]; then
1054  		build_targets "${MAINBOARD}"
1055  	elif [ ! -r "$ROOT/src/mainboard/${build_srcdir}" ]; then
1056  		echo "No such target: ${MAINBOARD}"
1057  		exit 1
1058  	else
1059  		build_target "${MAINBOARD}"
1060  		XMLFILE=$REAL_XMLFILE
1061  	fi
1062  else
1063  	build_targets
1064  	rm -f "$REAL_XMLFILE"
1065  	XMLFILE="$REAL_XMLFILE"
1066  	junit '<?xml version="1.0" encoding="utf-8"?>'
1067  	junit '<testsuite>'
1068  	if [ "$mode" != "text" ]; then
1069  		for xmlfile in $TARGET/abuild/*_*.xml; do
1070  			cat "$xmlfile" >> "$REAL_XMLFILE"
1071  		done
1072  	fi
1073  	XMLFILE=$REAL_XMLFILE
1074  fi
1075  junit '</testsuite>'
1076  
1077  if [ "$recursive" = "false" ]; then
1078  
1079  	# Print the list of failed configurations
1080  	if [ -f "$FAILED_BOARDS" ]; then
1081  		printf "%s configuration(s) failed:\n" "$( wc -l < "$FAILED_BOARDS" )"
1082  		cat "$FAILED_BOARDS"
1083  		echo
1084  		if [ "$exitcode" != "0" ]; then
1085  			failed=1
1086  		fi
1087  	elif [ -f "$PASSED_BOARDS" ]; then
1088  		printf "All %s tested configurations passed.\n" "$( wc -l < "$PASSED_BOARDS" )"
1089  	else
1090  		printf "No boards tested.\n"
1091  	fi
1092  fi
1093  
1094  exit $failed