1 #!/bin/bash -eu 2 # 3 # Copyright 2017 Google Inc. All rights reserved. 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 set -e 18 19 # This file makes it easy to confirm that a set of changes in source code don't result in any 20 # changes to the generated ninja files. This is to reduce the effort required to be confident 21 # in the correctness of refactorings 22 23 function die() { 24 echo "$@" >&2 25 exit 1 26 } 27 28 function usage() { 29 violation="$1" 30 die "$violation 31 32 Usage: diff_build_graphs.sh [--products=product1,product2...] <OLD_VERSIONS> <NEW_VERSIONS> 33 34 This file builds and parses the build files (Android.mk, Android.bp, etc) for each requested 35 product and for both sets of versions, and checks whether the ninja files (which implement 36 the build graph) changed between the two versions. 37 38 Example: diff_build_graphs.sh 'build/soong:work^ build/blueprint:work^' 'build/soong:work build/blueprint:work' 39 40 Options: 41 --products=PRODUCTS comma-separated list of products to check" 42 } 43 44 PRODUCTS_ARG="" 45 OLD_VERSIONS="" 46 NEW_VERSIONS="" 47 function parse_args() { 48 # parse optional arguments 49 while true; do 50 arg="${1-}" 51 case "$arg" in 52 --products=*) PRODUCTS_ARG="$arg";; 53 *) break;; 54 esac 55 shift 56 done 57 # parse required arguments 58 if [ "$#" != "2" ]; then 59 usage "" 60 fi 61 #argument validation 62 OLD_VERSIONS="$1" 63 NEW_VERSIONS="$2" 64 65 } 66 parse_args "$@" 67 68 69 # find some file paths 70 cd "$(dirname $0)" 71 SCRIPT_DIR="$PWD" 72 cd ../../.. 73 CHECKOUT_ROOT="$PWD" 74 OUT_DIR="${OUT_DIR-}" 75 if [ -z "$OUT_DIR" ]; then 76 OUT_DIR=out 77 fi 78 WORK_DIR="$OUT_DIR/diff" 79 OUT_DIR_OLD="$WORK_DIR/out_old" 80 OUT_DIR_NEW="$WORK_DIR/out_new" 81 OUT_DIR_TEMP="$WORK_DIR/out_temp" 82 83 84 function checkout() { 85 versionSpecs="$1" 86 for versionSpec in $versionSpecs; do 87 project="$(echo $versionSpec | sed 's|\([^:]*\):\([^:]*\)|\1|')" 88 ref="$(echo $versionSpec | sed 's|\([^:]*\):\([^:]*\)|\2|')" 89 echo "checking out ref $ref in project $project" 90 git -C "$project" checkout "$ref" 91 done 92 } 93 94 function run_build() { 95 echo 96 echo "Starting build" 97 # rebuild multiproduct_kati, in case it was missing before, 98 # or in case it is affected by some of the changes we're testing 99 make blueprint_tools 100 # find multiproduct_kati and have it build the ninja files for each product 101 builder="$(echo $OUT_DIR/soong/host/*/bin/multiproduct_kati)" 102 BUILD_NUMBER=sample "$builder" $PRODUCTS_ARG --keep --out "$OUT_DIR_TEMP" || true 103 echo 104 } 105 106 function diffProduct() { 107 product="$1" 108 109 zip1="$OUT_DIR_OLD/${product}.zip" 110 unzipped1="$OUT_DIR_OLD/$product" 111 112 zip2="$OUT_DIR_NEW/${product}.zip" 113 unzipped2="$OUT_DIR_NEW/$product" 114 115 unzip -qq "$zip1" -d "$unzipped1" 116 unzip -qq "$zip2" -d "$unzipped2" 117 118 #do a diff of the ninja files 119 diffFile="$WORK_DIR/diff.txt" 120 diff -r "$unzipped1" "$unzipped2" -x build_date.txt -x build_number.txt -x '\.*' -x '*.log' -x build_fingerprint.txt -x build.ninja.d -x '*.zip' > $diffFile || true 121 if [[ -s "$diffFile" ]]; then 122 # outputs are different, so remove the unzipped versions but keep the zipped versions 123 echo "First few differences (total diff linecount=$(wc -l $diffFile)) for product $product:" 124 cat "$diffFile" | head -n 10 125 echo "End of differences for product $product" 126 rm -rf "$unzipped1" "$unzipped2" 127 else 128 # outputs are the same, so remove all of the outputs 129 rm -rf "$zip1" "$unzipped1" "$zip2" "$unzipped2" 130 fi 131 } 132 133 function do_builds() { 134 #reset work dir 135 rm -rf "$WORK_DIR" 136 mkdir "$WORK_DIR" 137 138 #build new code 139 checkout "$NEW_VERSIONS" 140 run_build 141 mv "$OUT_DIR_TEMP" "$OUT_DIR_NEW" 142 143 #build old code 144 #TODO do we want to cache old results? Maybe by the time we care to cache old results this will 145 #be running on a remote server somewhere and be completely different 146 checkout "$OLD_VERSIONS" 147 run_build 148 mv "$OUT_DIR_TEMP" "$OUT_DIR_OLD" 149 150 #cleanup 151 echo created "$OUT_DIR_OLD" and "$OUT_DIR_NEW" 152 } 153 154 function main() { 155 do_builds 156 checkout "$NEW_VERSIONS" 157 158 #find all products 159 productsFile="$WORK_DIR/all_products.txt" 160 find $OUT_DIR_OLD $OUT_DIR_NEW -mindepth 1 -maxdepth 1 -name "*.zip" | sed "s|^$OUT_DIR_OLD/||" | sed "s|^$OUT_DIR_NEW/||" | sed "s|\.zip$||" | sort | uniq > "$productsFile" 161 echo Diffing products 162 for product in $(cat $productsFile); do 163 diffProduct "$product" 164 done 165 echo Done diffing products 166 echo "Any differing outputs can be seen at $OUT_DIR_OLD/*.zip and $OUT_DIR_NEW/*.zip" 167 echo "See $WORK_DIR/diff.txt for the full list of differences for the latest product checked" 168 } 169 170 main 171