#!/usr/bin/env bash # SPDX-License-Identifier: Apache-2.0 # Copyright (C) 2021 Arm Limited or its affiliates and Contributors. All rights reserved. [[ "$TRACE" ]] && set -x set -euo pipefail ### Example: ### Building a series of commits in distinct worktrees under d-aarch64/ ### ### export CCACHE_DIR="$(git rev-parse --show-toplevel)/ccache" CCACHE_MAXSIZE=50G ### git-list-between 03/01/2021 04/01/2021 llvm clang | awk '{print $1}' | cmake-build-commits d-aarch64 llvm clang print_help() { echo 'Usage: manyclangs-build-commits [WORKTREES] [PATHSPEC]...' echo '' echo 'Builds the commits read in on the standard input in separate worktrees under WORKTREES.' echo 'Only the directories matched by PATHSPEC are checked out before each build.' } # create_build COMMIT # Checks out COMMIT and builds it, then stores a copy in $WORKTREES/$COMMIT create_build() { COMMIT="$1" CHECKOUT_PATHSPEC=(${@:2}) # Checksum all CMakeLists.txt CMAKELISTS_SHA1_OLD="$(sha1sum <(find . -name 'CMakeLists.txt' -exec sha1sum {} +) | awk '{print $1}')" echo "Processing commit $COMMIT... ($(pwd))" git reset --soft "$COMMIT" # Do the checkout; --no-overlay also removes old files git -c core.checkStat=minimal -c advice.detachedHead=false checkout --no-overlay --progress "$COMMIT" -- "${CHECKOUT_PATHSPEC[@]}" # Print commit git show --format=fuller -s # Checksum again to compare CMAKELISTS_SHA1_NEW="$(sha1sum <(find . -name 'CMakeLists.txt' -exec sha1sum {} +) | awk '{print $1}')" echo "$COMMIT" > ./COMMIT ccache -z BUILD_LOGS="$(mktemp -d)" # Do not fail script if build fails set +e ( # Exit subshell early set -e mkdir -p ./build cd ./build if [[ "$CMAKELISTS_SHA1_OLD" == "$CMAKELISTS_SHA1_NEW" && -d ./CMakeFiles && ! -f ./.FAILED ]]; then echo 'Build configuration is unchanged.' else rm -r ./.FAILED ./CMakeFiles ./CMakeCache.txt || true "$SOURCE_DIR"/manyclangs-cmake ../llvm fi "$SOURCE_DIR"/manyclangs-build . |& tee -a "$BUILD_LOGS/build.log" # Add license information cp ../llvm/LICENSE.TXT ./LICENSE-llvm.txt cp ../clang/LICENSE.TXT ./LICENSE-clang.txt { echo 'These builds were done as part of the elfshaker/manyclangs project,' echo 'please see https://github.com/elfshaker/manyclangs for more information' } > ./README ) BUILD_STATUS="$?" set -e ccache -s # Create a copy of the current tree (cd "$ROOT_DIR" && "$SOURCE_DIR"/git-checkout-worktree "$WORKTREES/$BUILD_WORKTREE" "$WORKTREES/$COMMIT") # Clean failed builds and store logs if [[ "$BUILD_STATUS" != "0" ]]; then echo "Build of $COMMIT failed with exit code: $BUILD_STATUS!" cp "$ROOT_DIR/$WORKTREES/$COMMIT/build/CMakeFiles/CMake"*.log "$BUILD_LOGS"/ || true rm -r "$ROOT_DIR/$WORKTREES/$COMMIT/build/"* cp "$BUILD_LOGS"/* "$ROOT_DIR/$WORKTREES/$COMMIT/build/" || true # Create .FAILED to re-run cmake later touch "$ROOT_DIR/$WORKTREES/$BUILD_WORKTREE/build/.FAILED" fi rm -r "$BUILD_LOGS" } main() { SOURCE_DIR="$(realpath "$(dirname "${BASH_SOURCE[0]}")")" # The directory containing the worktrees to create WORKTREES="$1" CHECKOUT_PATHSPEC=(${@:2}) ROOT_DIR="$(pwd)" # Worktree used for building. Ensures consistent paths. BUILD_WORKTREE="build-$(openssl rand -hex 8)" # Do the initial checkout git worktree add --no-checkout -B "$WORKTREES/$BUILD_WORKTREE" "$WORKTREES/$BUILD_WORKTREE" cd "$WORKTREES/$BUILD_WORKTREE" read -r COMMIT git checkout "$COMMIT" -- "${CHECKOUT_PATHSPEC[@]}" time create_build "$COMMIT" "${CHECKOUT_PATHSPEC[@]}" while read -r COMMIT; do time create_build "$COMMIT" "${CHECKOUT_PATHSPEC[@]}" done } case "${1:--h}" in -h | --help) print_help [ $# -ne 0 ] exit $? ;; *) main "$@" ;; esac