summaryrefslogtreecommitdiff
path: root/tcwg_aosp-build.sh
diff options
context:
space:
mode:
Diffstat (limited to 'tcwg_aosp-build.sh')
-rwxr-xr-xtcwg_aosp-build.sh498
1 files changed, 498 insertions, 0 deletions
diff --git a/tcwg_aosp-build.sh b/tcwg_aosp-build.sh
new file mode 100755
index 00000000..06556823
--- /dev/null
+++ b/tcwg_aosp-build.sh
@@ -0,0 +1,498 @@
+#!/bin/bash
+
+set -euf -o pipefail
+
+scripts=$(dirname $0)
+# shellcheck source=jenkins-helpers.sh
+. $scripts/jenkins-helpers.sh
+# shellcheck source=round-robin.sh
+. $scripts/round-robin.sh
+
+convert_args_to_variables "$@"
+
+obligatory_variables rr[ci_project] rr[ci_config]
+declare -A rr
+
+# tcwg_aosp-code_size-{aosp_modules}
+IFS=- read -a ci_project <<EOF
+${rr[ci_project]}
+EOF
+aosp_modules=("${aosp_modules[@]-${ci_project[2]}}")
+
+# {aosp_target}-{aosp_ver}
+IFS=- read -a ci_config <<EOF
+${rr[ci_config]}
+EOF
+aosp_target="${aosp_target-${ci_config[0]}}"
+
+# Execution mode: build or bisect
+rr[mode]="${rr[mode]-build}"
+
+# Set custom revision for one of the projects, and use baseline revisions
+# for all other projects.
+rr[baseline_branch]="${rr[baseline_branch]-linaro-local/ci/${rr[ci_project]}/${rr[ci_config]}}"
+rr[update_baseline]="${rr[update_baseline]-ignore}"
+rr[top_artifacts]="${rr[top_artifacts]-$(pwd)/artifacts}"
+
+rr[components]="aosp_superproject llvm toolchain_superproject"
+
+# Use baseline branches by default.
+for c in ${rr[components]}; do
+ rr[${c}_git]=${rr[${c}_git]-baseline}
+done
+
+# Artifacts version
+rr[major]=4
+rr[minor]=0
+
+start_at="${start_at-default}"
+finish_at="${finish_at-default}"
+verbose="${verbose-true}"
+verbose2="${verbose2-false}"
+
+if $verbose2; then set -x; fi
+
+trap print_traceback EXIT
+
+# Set start and finish steps for different modes.
+default_start_at=""
+default_finish_at=""
+case "${rr[mode]}" in
+ "bisect")
+ case "$(print_single_updated_component)" in
+ toolchain_superproject) default_start_at="build_aosp_toolchain" ;;
+ llvm) default_start_at="build_shadow_llvm" ;;
+ aosp_superproject) default_start_at="build_aosp" ;;
+ *) assert false ;;
+ esac
+ ;;
+esac
+if [ x"$start_at" = x"default" ]; then
+ start_at="$default_start_at"
+fi
+if [ x"$finish_at" = x"default" ]; then
+ finish_at="$default_finish_at"
+fi
+
+run_step_init "$start_at" "$finish_at" "${rr[top_artifacts]}" "$verbose"
+
+# Clone AOSP repo
+clone_aosp ()
+{
+ (
+ set -euf -o pipefail
+
+ local dir="$1"
+ local superproject_rev="$2"
+ local supermanifest="$3"
+
+ mkdir -p "$dir"
+ cd "$dir"
+
+ # Remove locks from previous runs
+ find .repo -type f -name "shallow.lock" -delete || true
+ # Clean AOSP checkout
+ .repo/repo/repo forall -c "git reset --hard; git clean -fdx" \
+ >/dev/null &
+ if ! wait $!; then
+ # Wipe AOSP checkout on clean errors
+ cd ..
+ rm -rf "$dir"
+ mkdir "$dir"
+ cd "$dir"
+ fi
+
+ # Resync on superproject change
+ if ! [ -f repo_synced ] \
+ || [ x"$(cat repo_synced)" != x"$superproject_rev" ]; then
+ rm -f repo_synced
+
+ local manifest_url manifest_branch manifest_rev
+ # Add a newline after supermanifest file to avoid "read" exiting with
+ # "1" due to EOF.
+ read manifest_url manifest_branch manifest_rev \
+ < <(cat "$supermanifest"; echo)
+
+ manifest_url="https://android.googlesource.com/$manifest_url"
+
+ rm -rf ./.repo/repo
+ repo init --partial-clone --clone-filter=blob:limit=10M \
+ --use-superproject -u "$manifest_url" -b "$manifest_branch"
+
+ # Use manifest specified in superproject. We then use --nmu option
+ # to "repo sync" to avoid manifest update.
+ git -C .repo/manifests fetch "$manifest_url" "$manifest_rev"
+ git -C .repo/manifests checkout FETCH_HEAD
+
+ # FIXME:
+ # Repo doesn't [yet] support fetching custom revisions of
+ # superproject. We workaround that by hacking command line
+ # for "git fetch branch:branch" to "git fetch SHA1:branch".
+ # This hack is effective only when we have $manifest_branch
+ # specified in "repo init", so don't remove "-b" option in "repo init".
+ sed -i -e "s#\[self._branch + \":\" + self._branch\]#\['$superproject_rev' + \":\" + self._branch\]#" ./.repo/repo/git_superproject.py
+
+ ./.repo/repo/repo sync --nmu -j"$(nproc --all)" -q &
+ if ! wait $!; then
+ ./.repo/repo/repo sync --nmu -j1 --fail-fast
+ fi
+
+ # FIXME:
+ # Verify that "repo sync" fetched the desired revision of superproject.
+ local super_git super_rev
+ super_git=$(find ./.repo/exp-superproject/ -name "*-superproject.git")
+ super_rev=$(git -C "$super_git" rev-parse "$manifest_branch")
+ assert_with_msg "Could not hack superproject repo" \
+ [ "$super_rev" = "$superproject_rev" ]
+
+ echo "$superproject_rev" > repo_synced
+ fi
+ )
+}
+
+# Build AOSP's LLVM
+build_aosp_toolchain ()
+{
+ (
+ set -euf -o pipefail
+
+ clone_repo toolchain_superproject
+
+ clone_aosp llvm-toolchain "$(get_current_git toolchain_superproject_rev)" \
+ "$(pwd)/toolchain_superproject/.supermanifest"
+
+ cd llvm-toolchain
+
+ assert_with_msg "Missing repo_synced file" [ -f repo_synced ]
+
+ # Check if sources were updated and rebuild AOSP toolchain on re-syncs.
+ if [ -f out/stage2/cmake_invocation.sh ] \
+ && [ out/stage2/cmake_invocation.sh -ot repo_synced ]; then
+ # Re-generate cmake recipe
+ rm out/stage2/cmake_invocation.sh
+ fi
+
+ # Reproduce AOSP toolchain to get the cmake recipe
+ if ! [ -f out/stage2/cmake_invocation.sh ]; then
+ rm -rf out
+
+ local tf_loc
+ tf_loc="$(pip show tensorflow | grep Location | cut -d' ' -f2)" || true
+ [ -d "$tf_loc" ] && export TENSORFLOW_INSTALL="$tf_loc/tensorflow"
+
+ python toolchain/llvm_android/build.py --no-build lldb,windows \
+ --skip-runtimes --skip-tests --skip-package --mlgo
+ fi
+ )
+}
+
+# Build LLVM
+build_shadow_llvm ()
+{
+ (
+ set -euf -o pipefail
+
+ clone_repo llvm
+
+ cd llvm-toolchain
+
+ local cc cxx ninja
+ cc=$(cat out/stage2/cmake_invocation.sh \
+ | grep -e " -DCMAKE_C_COMPILER=" \
+ | sed -e "s/.* -DCMAKE_C_COMPILER=\([^ ]*\).*/\1/")
+ cxx=$(cat out/stage2/cmake_invocation.sh \
+ | grep -e " -DCMAKE_CXX_COMPILER=" \
+ | sed -e "s/.* -DCMAKE_CXX_COMPILER=\([^ ]*\).*/\1/")
+ ninja=$(cat out/stage2/cmake_invocation.sh \
+ | grep -e " -DCMAKE_MAKE_PROGRAM=" \
+ | sed -e "s/.* -DCMAKE_MAKE_PROGRAM=\([^ ]*\).*/\1/")
+ cd ..
+
+ local workspace
+ workspace=$(pwd)
+
+ # ${workspace:?}/bin is to avoid shellcheck warning that below never
+ # expands to "rm -rf /bin"
+ rm -rf "${workspace:?}/bin"
+ mkdir "$workspace/bin"
+
+ cat > "$workspace/bin/cc" <<EOF
+#!/bin/sh
+CCACHE_BASEDIR=$workspace exec ccache $cc "\$@"
+EOF
+ chmod +x "$workspace/bin/cc"
+
+ cat > "$workspace/bin/c++" <<EOF
+#!/bin/sh
+CCACHE_BASEDIR=$workspace exec ccache $cxx "\$@"
+EOF
+ chmod +x "$workspace/bin/c++"
+
+ rm -rf llvm-install
+
+ cd llvm-toolchain/out
+
+ cp stage2/cmake_invocation.sh ./
+
+
+ sed -i \
+ -e "s#/llvm-toolchain/out/llvm-project/llvm #/llvm/llvm #" \
+ -e "s#/llvm-toolchain/out/stage2-install #/llvm-install #" \
+ -e "s# -DCMAKE_C_COMPILER=[^ ]* # -DCMAKE_C_COMPILER=$workspace/bin/cc #" \
+ -e "s# -DCMAKE_CXX_COMPILER=[^ ]* # -DCMAKE_CXX_COMPILER=$workspace/bin/c++ #" \
+ cmake_invocation.sh
+
+ ccache -z
+
+ rm -rf stage3
+ mkdir stage3
+ cd stage3
+
+ set +e
+ source ../cmake_invocation.sh
+ local res=$?
+ set -e
+ if [ $res != 0 ] || ! $ninja; then
+ # Attempt to workaround failures in past LLVM versions.
+ cd ..
+
+ # Workaround failures to find BOLT, which seems to be unused anyway.
+ sed -i -e "s/bolt;//" cmake_invocation.sh
+
+ # Workaround failure to link x86_64's libc++.so against non-PIC
+ # libc++abi.a. Add -fPIC to x86_64 runtime's CFLAGS.
+ sed -i -e "s/ '-DRUNTIMES_x86_64-unknown-linux-gnu_CMAKE_C_FLAGS=/ '-DRUNTIMES_x86_64-unknown-linux-gnu_CMAKE_C_FLAGS=-fPIC /g" \
+ cmake_invocation.sh
+ sed -i -e "s/ '-DRUNTIMES_x86_64-unknown-linux-gnu_CMAKE_CXX_FLAGS=/ '-DRUNTIMES_x86_64-unknown-linux-gnu_CMAKE_CXX_FLAGS=-fPIC /g" \
+ cmake_invocation.sh
+
+ rm -rf stage3
+ mkdir stage3
+ cd stage3
+ source ../cmake_invocation.sh
+ $ninja
+ fi
+ $ninja install
+
+ ccache -s
+ )
+}
+
+# Build AOSP
+build_aosp ()
+{
+ (
+ set -euf -o pipefail
+
+ # Clone AOSP superproject
+ clone_repo aosp_superproject
+
+ # Clone AOSP
+ clone_aosp aosp "$(get_current_git aosp_superproject_rev)" \
+ "$(pwd)/aosp_superproject/.supermanifest"
+
+ # Remove old binaries
+ (set +f; rm -rf shadow*)
+ rm -rf aosp/out
+
+ # Install build wrappers
+ local prebuilts_clang_bin
+ while read -r prebuilts_clang_bin; do
+ $scripts/wrappers/install-wrappers.sh \
+ $prebuilts_clang_bin llvm-install/bin aosp shadow
+ done < <(find aosp/prebuilts/clang/host/linux-x86/ \
+ -maxdepth 2 -name "bin" -type d)
+
+ $scripts/wrappers/install-wrappers.sh \
+ aosp/build/soong/scripts llvm-install/bin aosp shadow strip.sh
+
+ # Build AOSP
+ (
+ cd aosp
+ set -e +ufx +o pipefail
+ local release=trunk_staging
+ source build/envsetup.sh
+ echo "RUN: lunch aosp_${aosp_target}-${release}-user"
+ lunch "aosp_${aosp_target}-${release}-user"
+ echo "RUN: m ${aosp_modules[*]}"
+ if [ x"${aosp_modules[*]}" = x"aosp" ]; then
+ m
+ else
+ m "${aosp_modules[@]}"
+ fi
+ )
+ )
+}
+
+# Check shadow build
+process_shadow_data ()
+{
+ (
+ set -euf -o pipefail
+
+ local success_code="$2"
+
+ if ! [ -f shadow.errors ]; then
+ success_code=$(($success_code + 1))
+ echo "# shadow build has no errors" >> $run_step_top_artifacts/results
+ echo "$success_code" >> $run_step_top_artifacts/results
+ else
+ echo "# shadow build has errors" >> $run_step_top_artifacts/results
+ cp shadow.errors $run_step_top_artifacts/
+ exit 1
+ fi
+
+ if [ -f shadow.size ]; then
+ cp shadow.size $run_step_top_artifacts/size.csv
+ success_code=$(($success_code + 1))
+ echo "# shadow.size present" >> $run_step_top_artifacts/results
+ echo "$success_code" >> $run_step_top_artifacts/results
+ fi
+ )
+}
+
+# Exit with code 0 if no regression compared to base-artifacts/results.
+no_regression_p ()
+{
+ (
+ set -euf -o pipefail
+
+ no_build_regression_p "$@"
+
+ if ! [ -f base-artifacts/size.csv ]; then
+ return 0
+ elif ! [ -f $run_step_top_artifacts/size.csv ]; then
+ return 1
+ fi
+
+ # Generate results-vs-prev/results.csv
+ mkdir -p $run_step_top_artifacts/results-vs-prev
+ $scripts/../bmk-scripts/csvs2table.py \
+ --relative "$(pwd)/base-artifacts/size.csv" \
+ $run_step_top_artifacts/size.csv \
+ > $run_step_top_artifacts/results-vs-prev/results.csv
+
+ # Read result lines from <(tail -n +2 ...) below.
+ # "-n +2" is to skip the header line.
+ local -a arr
+ local rel_text text1 text2 metric total1=0 total2=0
+ while IFS=, read -a arr; do
+ binary=${arr[0]}
+ rel_text=${arr[2]}
+ text1=${arr[3]}
+ text2=${arr[4]}
+
+ if [ x"$rel_text" = x"n/a" ]; then
+ case "$text1":"$text2" in
+ "-1:-1") ;;
+ "-1":*)
+ echo "-1,$binary now builds successfully" \
+ >> $run_step_artifacts/binary.improvements
+ ;;
+ *:"-1")
+ echo "1,$binary now fails to build" \
+ >> $run_step_artifacts/binary.regressions
+ ;;
+ esac
+ continue
+ fi
+
+ metric=$(($rel_text - 100))
+
+ if [ $metric -lt 0 ]; then
+ echo "$metric,$binary reduced in size by ${metric}% from $text1 to $text2" \
+ >> $run_step_artifacts/binary.improvements
+ elif [ $metric -gt 0 ]; then
+ echo "$metric,$binary increased in size by ${metric}% from $text1 to $text2" \
+ >> $run_step_artifacts/binary.regressions
+ fi
+
+ total1=$(($total1 + $text1))
+ total2=$(($total2 + $text2))
+ done < <(tail -n +2 $run_step_top_artifacts/results-vs-prev/results.csv)
+
+ if [ $total1 != 0 ]; then
+ metric=$(((100 * $total2) / $total1 - 100))
+ else
+ # Corner case when baseline results have no intersection with
+ # the current results. This happens when AOSP build fails and
+ # size.csv only has the header.
+ metric=0
+ fi
+ if [ $metric -lt 0 ]; then
+ echo "$metric,AOSP reduced in size by ${metric}% from $total1 to $total2" \
+ >> $run_step_artifacts/aosp.improvements
+ elif [ $metric -gt 0 ]; then
+ echo "$metric,AOSP increased in size by ${metric}% from $total1 to $total2" \
+ >> $run_step_artifacts/aosp.regressions
+ fi
+
+ local primary_change="" regression=false
+ if [ -f $run_step_artifacts/aosp.regressions ]; then
+ primary_change=$run_step_artifacts/aosp.regressions
+ regression=true
+ elif [ -f $run_step_artifacts/binary.regressions ]; then
+ primary_change=$run_step_artifacts/binary.regressions
+ regression=true
+ elif [ -f $run_step_artifacts/aosp.improvements ]; then
+ primary_change=$run_step_artifacts/aosp.improvements
+ elif [ -f $run_step_artifacts/binary.improvements ]; then
+ primary_change=$run_step_artifacts/binary.improvements
+ fi
+
+ if [ x"$primary_change" != x"" ]; then
+ sort -gr -o "$primary_change" "$primary_change"
+ if $regression; then
+ head -n1 "$primary_change" \
+ | sed -e "s/^/# /" > $run_step_artifacts/results.regressions
+ fi
+ fi
+
+ case "${rr[ci_project]}" in
+ "tcwg_aosp-build"*)
+ # We have passed no_build_regression_p() check above,
+ # so succeed.
+ return 0
+ ;;
+ esac
+
+ local -a changed_components
+ IFS=" " read -r -a changed_components <<< "$(print_changed_components)"
+ case " ${changed_components[*]} " in
+ *" llvm "*) ;;
+ *)
+ # Code-size builds without changed LLVM we declare as
+ # non-regressing. Code-size changes are due to changes in AOSP,
+ # not in the compiler.
+ return 0
+ ;;
+ esac
+
+ if $regression; then
+ return 1
+ fi
+ return 0
+ )
+}
+
+rr[breakup_changed_components]="breakup_changed_components llvm"
+
+case "${rr[ci_project]}" in
+ "tcwg_aosp-build"*)
+ run_step stop_on_fail 0 reset_artifacts
+ run_step skip_on_fail 1 build_aosp_toolchain
+ run_step skip_on_fail 2 build_shadow_llvm
+ run_step skip_on_fail 3 build_aosp
+ run_step skip_on_fail x process_shadow_data -- 3
+ ;;
+ *)
+ run_step stop_on_fail -10 reset_artifacts
+ run_step skip_on_fail -3 build_aosp_toolchain
+ run_step skip_on_fail -2 build_shadow_llvm
+ run_step skip_on_fail -1 build_aosp
+ run_step skip_on_fail x process_shadow_data -- -1
+ ;;
+esac
+run_step reset_on_fail x check_regression
+
+trap "" EXIT