arch/arm64: Support FEAT_CCIDX
[coreboot2.git] / util / abuild / abuild
blob58f48f2b05f242de53ed0d0a2c15bd402d4fa616
1 #!/usr/bin/env bash
3 # coreboot autobuild
5 # This script builds coreboot images for all available targets.
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.
12 #set -x # Turn echo on....
14 ABUILD_DATE="Nov 18, 2023"
15 ABUILD_VERSION="0.11.03"
17 TOP=$PWD
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}"
26 # Name associated with a run of abuild
27 TESTRUN_DEFAULT=default
28 TESTRUN="${TESTRUN_DEFAULT}"
30 export KCONFIG_OVERWRITECONFIG=1
32 # path to payload. Should be more generic
33 PAYLOAD=/dev/null
35 # get path to coreboot XGCC if it's not already set
36 if [ -z "$XGCCPATH" ]; then
37 XGCCPATH="${TOP}/util/crossgcc/xgcc/bin/"
40 # Add XGCC to the path.
41 if [ -d "$XGCCPATH" ] && [[ ":$PATH:" != *":$XGCCPATH:"* ]]; then
42 PATH="$XGCCPATH:$PATH"
45 # Lines of error context to be printed in FAILURE case
46 CONTEXT=12
48 # Configure-only mode
49 configureonly=0
51 # Did any board fail to build?
52 failed=0
54 # Exit with a non-zero errorlevel on failure
55 exitcode=0
57 # default: don't save checksums
58 checksum_file=""
60 # default: single CPU build
61 cpus=1
63 # change with -d <directory>
64 configdir="$TOP/configs"
66 # Timeless builds
67 TIMELESS=0
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
77 MAKE=$i
79 # this can be changed to junit by -J
80 mode=text
82 # quiet mode: only print pass, failure, and 'skipped' messages
83 quiet=false
85 # clang mode enabled by -sb option.
86 scanbuild=false
88 # Mark whether abuild was called recursively
89 recursive=false
91 # Skip builds with this Kconfig value set
92 skipconfig_set=""
94 # Skip builds with this Kconfig value notset
95 skipconfig_unset=""
97 # EPOCHTIME
98 ts_exec_shell=$(printf "%(%s)T" -2)
99 ts_basetime_str=$(date -u --date=@${ts_exec_shell})
101 trap interrupt INT
103 function interrupt
105 printf "\n%s: execution interrupted manually.\n" "$0"
106 if [ "$mode" == "junit" ]; then
107 printf "%s: deleting incomplete xml output file.\n" "$0"
109 exit 1
112 function debug
114 test "$verbose" == "true" && echo "$*"
117 function junit
119 test "$mode" == "junit" && echo "$*" >> "$XMLFILE"
120 return 0
123 function junitfile
125 test "$mode" == "junit" && {
126 printf '<![CDATA[\n'
127 cat "$1"
128 printf ']]>\n'
129 } >> "$XMLFILE"
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
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,"
145 # Given a mainboard descriptor, return its directory below src/mainboard
146 function mainboard_directory
148 local MAINBOARD=$1
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:"
156 # Given a mainboard descriptor, return its vendor (CONFIG_VENDOR_*)
157 function mainboard_vendor
159 local MAINBOARD=$1
160 local kconfig_file
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
170 grep "^[[:space:]]*config\>[[:space:]]*\<VENDOR_" "$kconfig_file" | \
171 sed "s,^.*\<VENDOR_\([A-Z0-9_]*\)\>.*$,\1,"
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
180 # TODO: Change 'targets' variable to an array
181 local targets
182 local VARIANT_UC
184 VARIANT_UC=$(echo "${variant}" | tr '[:lower:]' '[:upper:]' | tr '-' '_')
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
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
202 done
205 # shellcheck disable=SC2129
206 function create_config
208 local BUILD_NAME=$1
209 local build_dir=$2
210 local board_srcdir
212 local config_file="${build_dir}/config.build"
213 board_srcdir="$(mainboard_directory "${BUILD_NAME}")"
215 mkdir -p "${build_dir}"
216 mkdir -p "$TARGET/sharedutils"
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}"
225 update_config "$BUILD_NAME" "$build_dir" "$config_file"
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
239 function update_config
241 local BUILD_NAME=$1
242 local build_dir=$2
243 local config_file=$3
245 local PAYLOAD
246 local defconfig_file
247 defconfig_file=${build_dir}/config.$(echo "${BUILD_NAME}" | tr '[:upper:]' '[:lower:]').default
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.
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
262 if [ "$quiet" == "false" ]; then printf "Using payload %s\n" "$PAYLOAD"; fi
263 elif [ "$payloads" = "none" ]; then
264 PAYLOAD=none
267 if [ "$PAYLOAD" = "none" ]; then
269 echo "CONFIG_PAYLOAD_NONE=y"
270 echo "# CONFIG_PAYLOAD_ELF is not set"
271 } >> "${config_file}"
272 elif [ "$PAYLOAD" != "/dev/null" ]; then
274 echo "# CONFIG_PAYLOAD_NONE is not set"
275 echo "CONFIG_PAYLOAD_ELF=y"
276 echo "CONFIG_PAYLOAD_FILE=\"$PAYLOAD\""
277 } >> "${config_file}"
279 # Disable all other payload config options
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}"
296 if [ "$quiet" == "false" ]; then echo " $MAINBOARD ($customizing)"; fi
297 # shellcheck disable=SC2059
298 printf "$configoptions" >> "${config_file}"
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
310 # shellcheck disable=SC2129
311 function create_buildenv
313 local BUILD_NAME=$1
314 local build_dir=$2
315 local config_file=$3
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"
324 local ret=$?
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"
341 return $ret
344 function check_config
346 local BUILD_DIR="$1"
347 local TEST_TYPE="$2"
348 local TEST_STRING="$3"
349 local NEGATE="$4"
351 local CONFIG_FILE="$BUILD_DIR/config.build"
352 local CONFIG_LOG="$BUILD_DIR/config.log"
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
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
368 return 0
371 # Counting microseconds since start of shell
372 function add_timestamp
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
382 function ts_delta_seconds
384 local delta=$(( ($2 - $1) / (1000 * 1000) ))
385 printf "%s" $delta
388 function ts_delta_string
390 local ts_minutes
391 local ts_seconds
392 local delta
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 ))
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
407 function compile_target
409 local BUILD_NAME=$1
411 if [ "$quiet" == "false" ]; then echo " Compiling $MAINBOARD image$cpuconfig..."; fi
413 CURR=$( pwd )
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)
421 cd "${build_dir}" || return $?
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}"
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' >"
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"
453 failed=1
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"
462 stats_files="${build_dir}/${timestamps}"
463 if [ -f ${build_dir}/ccache.stats ]; then
464 stats_files="${stats_files} ${build_dir}/ccache.stats"
466 flock -F -w 0.1 $TARGET/.statslock tar -rf ${stats_archive} ${stats_files} 2> /dev/null
468 if [ "$clean_work" = "true" ]; then
469 rm -rf "${build_dir}"
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
475 return $MAKE_FAILED
478 function build_config
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
487 board_srcdir=$(mainboard_directory "${MAINBOARD}")
489 if [ "$(cat "${build_dir}/compile.status" 2>/dev/null)" = "ok" ] && \
490 [ "$buildall" = "false" ]; then
491 echo "Skipping $BUILD_NAME; (already successful)"
492 return
495 export HOSTCC='gcc'
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
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}"
508 ts_0=$(add_timestamp)
510 create_buildenv "$BUILD_NAME" "$build_dir" "$config_file"
511 local BUILDENV_CREATED=$?
513 check_config "$build_dir" "mainboard" "CONFIG_BOARD_${MAINBOARD}=y"
514 local MAINBOARD_OK=$?
516 check_config "$build_dir" "vendor" "CONFIG_VENDOR_$(mainboard_vendor "${MAINBOARD}")=y"
517 local VENDOR_OK=$?
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
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
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
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
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' >"
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"
563 junit "</testcase>"
564 echo "$BUILD_NAME - Log: $build_dir/config.log" >> "$FAILED_BOARDS"
565 return
568 local required_arches
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 ')
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)))
582 # shellcheck disable=SC2181
583 if [[ $? -ne 0 ]]; then
584 echo "Calculating missing_arches failed" >&2
585 exit 1
588 if [ -n "$missing_arches" ]; then
589 printf "skipping %s because we're missing compilers for (%s)\n" "$BUILD_NAME" "$missing_arches"
590 return
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"
600 compile_target "${BUILD_NAME}"
601 if [ "$scanbuild" = "true" ]; then
602 mv "${scanbuild_out}"tmp/* "${scanbuild_out}"
603 rmdir "${scanbuild_out}tmp"
607 junit "</testcase>"
610 function record_mainboard
612 local log=$1
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
622 # One target may build several configs
623 function build_target
625 local MAINBOARD=$1
626 local MAINBOARD_LC
627 MAINBOARD_LC=$(echo "$MAINBOARD" | tr '[:upper:]' '[:lower:]')
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}"_"
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
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"
657 function remove_target
659 if [ "$remove" != "true" ]; then
660 return
663 local BUILD_NAME=$1
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"
671 echo "Removing build dir for $BUILD_NAME..."
672 rm -rf "${TARGET:?}/${BUILD_NAME}"
674 return
677 function myhelp
679 cat << __END_OF_HELP
680 Usage: $0 [options]
681 $0 [-V|--version]
682 $0 [-h|--help]
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
725 [-V|--version] Print version number and exit
726 [-h|--help] Print this help and exit
728 [-s|--silent] obsolete
729 __END_OF_HELP
732 function myversion
734 cat << EOF
736 coreboot autobuild v$ABUILD_VERSION ($ABUILD_DATE)
738 Copyright (C) 2004 by Stefan Reinauer <stepan@openbios.org>
739 Copyright (C) 2006-2010 by coresystems GmbH <info@coresystems.de>
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.
748 # default options
749 target=""
750 buildall=false
751 verbose=false
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 )
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
761 # Save command line for xargs parallelization.
762 cmdline=("$@")
764 # parse parameters.. try to find out whether we're running GNU getopt
765 getoptbrand="$(getopt -V)"
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=$?
780 if [ $retval != 0 ]; then
781 myhelp
782 exit 1
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"
824 --skip_set) shift
825 skipconfig_set="$1"
826 customizing="${customizing}, Skipping builds with CONFIG_${skipconfig_set}=Y"
827 shift
829 --skip_unset) shift
830 skipconfig_unset="$1"
831 customizing="${customizing}, Skipping builds with CONFIG_${skipconfig_unset} not set"
832 shift
834 --asserts) shift
835 configoptions="${configoptions}CONFIG_FATAL_ASSERTS=y\n"
837 -y|--ccache) shift
838 customizing="${customizing}, ccache"
839 configoptions="${configoptions}CONFIG_CCACHE=y\n"
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"
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"
859 -A|--any-toolchain) shift
860 customizing="${customizing}, any-toolchain"
861 configoptions="${configoptions}CONFIG_ANY_TOOLCHAIN=y\n"
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"
868 -z|--clean) shift
869 customizing="${customizing}, clean"
870 clean_work=true
872 -Z|--clean-somewhat) shift
873 customizing="${customizing}, clean-somewhat"
874 clean_objs=true
876 -o|--outdir) shift
877 TARGET=$1; shift
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"
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
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}"
912 if [[ "${TARGET}" == "${TARGET_DEFAULT}" ]]; then
913 TARGET="${TESTRUN}"
917 if [ -n "$1" ]; then
918 printf "Invalid option '%s'\n\n" "$1"; myhelp; exit 1;
921 if [ -z "$TARGET" ] || [ "$TARGET" = "/" ]; then
922 echo "Please specify a valid, non-root build directory."
923 exit 1
926 if ! mkdir -p "$TARGET"; then
927 echo "Unable to create build directory"
928 exit 1
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
936 customizing=$(echo "$customizing" | cut -c3-)
937 if [ -z "$customizing" ]; then
938 customizing="Default configuration"
940 customizing="Config: ${customizing}"
941 FAILED_BOARDS="$(realpath ${TARGET}/failed_boards)"
942 PASSED_BOARDS="$(realpath ${TARGET}/passing_boards)"
944 stats_archive="$TARGET/statistics.tar"
946 # Generate a single xcompile for all boards
947 export xcompile="${TARGET}/xcompile"
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"
954 # Initialize empty statistics archive
955 tar -cf "${stats_archive}" "${xcompile}" 2> /dev/null
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
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
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()
976 local targets=${*-$(get_mainboards)}
977 for MAINBOARD in $targets; do
978 build_target "${MAINBOARD}"
979 done
981 else
982 build_targets()
984 local ABSPATH
985 local stime
986 local etime
987 local num_targets
988 local cpus_per_target
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"
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)
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
1027 if [ "$scanbuild" = "true" ]; then
1028 mv "${scanbuild_out}tmp/"* "${scanbuild_out}"
1029 rmdir "${scanbuild_out}tmp"
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
1038 junit '<?xml version="1.0" encoding="utf-8"?>'
1039 junit '<testsuite>'
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}"
1049 printf "\n"
1050 exit 1
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
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
1073 XMLFILE=$REAL_XMLFILE
1075 junit '</testsuite>'
1077 if [ "$recursive" = "false" ]; then
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
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"
1094 exit $failed