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