Jeff Gaston | cc0993d | 2019-04-02 18:02:44 -0400 | [diff] [blame] | 1 | #!/bin/bash |
| 2 | set -e |
| 3 | set -u |
| 4 | |
| 5 | scriptName="$(basename $0)" |
| 6 | |
| 7 | function usage() { |
| 8 | echo "usage: $0 <tasks>" |
| 9 | echo "Attempts to diagnose why "'`'"./gradlew <tasks>"'`'" fails" |
| 10 | echo |
| 11 | echo "For example:" |
| 12 | echo |
| 13 | echo " $0 assembleDebug # or any other arguments you would normally give to ./gradlew" |
| 14 | echo |
| 15 | echo "These are the types of diagnoses that $scriptName can make:" |
| 16 | echo |
| 17 | echo " A) Some state saved in memory by the Gradle daemon is triggering an error" |
| 18 | echo " B) Your source files have been changed" |
| 19 | echo " C) Some file in the out/ dir is triggering an error" |
| 20 | echo " If this happens, $scriptName will identify which file(s) specifically" |
| 21 | echo " D) The build is nondeterministic and/or affected by timestamps" |
| 22 | echo " E) The build via gradlew actually passes" |
| 23 | exit 1 |
| 24 | } |
| 25 | |
| 26 | gradleArgs="$*" |
| 27 | if [ "$gradleArgs" == "" ]; then |
| 28 | usage |
| 29 | fi |
| 30 | |
Jeff Gaston | 6323450 | 2019-07-09 13:47:31 -0400 | [diff] [blame] | 31 | workingDir="$(pwd)" |
| 32 | if [ ! -e "$workingDir/gradlew" ]; then |
| 33 | echo "Error; ./gradlew does not exist. Must cd to a dir containing a ./gradlew first" |
| 34 | # so that this script knows which gradlew to use (in frameworks/support or frameworks/support/ui) |
| 35 | exit 1 |
| 36 | fi |
| 37 | |
Jeff Gaston | cc0993d | 2019-04-02 18:02:44 -0400 | [diff] [blame] | 38 | scriptPath="$(cd $(dirname $0) && pwd)" |
| 39 | supportRoot="$(cd $scriptPath/../.. && pwd)" |
| 40 | checkoutRoot="$(cd $supportRoot/../.. && pwd)" |
Jeff Gaston | a58e308 | 2019-08-05 19:44:26 -0400 | [diff] [blame] | 41 | tempDir="$checkoutRoot/diagnose-build-failure/" |
Jeff Gaston | cc0993d | 2019-04-02 18:02:44 -0400 | [diff] [blame] | 42 | if [ "${GRADLE_USER_HOME:-}" == "" ]; then |
| 43 | GRADLE_USER_HOME="$(cd ~ && pwd)/.gradle" |
| 44 | fi |
| 45 | COLOR_WHITE="\e[97m" |
| 46 | COLOR_GREEN="\e[32m" |
| 47 | |
| 48 | function checkStatusRepo() { |
| 49 | repo status |
| 50 | } |
| 51 | |
| 52 | function checkStatusGit() { |
| 53 | git status |
| 54 | git log -1 |
| 55 | } |
| 56 | |
| 57 | function checkStatus() { |
| 58 | cd "$checkoutRoot" |
| 59 | if [ "-e" .repo ]; then |
| 60 | checkStatusRepo |
| 61 | else |
| 62 | checkStatusGit |
| 63 | fi |
| 64 | } |
| 65 | |
| 66 | function runBuild() { |
| 67 | echo -e "$COLOR_GREEN" |
| 68 | args="$*" |
| 69 | cd "$supportRoot" |
| 70 | if eval $args; then |
| 71 | echo -e "$COLOR_WHITE" |
| 72 | echo |
| 73 | echo '`'$args'`' succeeded |
| 74 | return 0 |
| 75 | else |
| 76 | echo -e "$COLOR_WHITE" |
| 77 | echo |
| 78 | echo '`'$args'`' failed |
| 79 | return 1 |
| 80 | fi |
| 81 | } |
| 82 | |
| 83 | function backupState() { |
| 84 | cd "$scriptPath" |
| 85 | backupDir="$1" |
Jeff Gaston | 6323450 | 2019-07-09 13:47:31 -0400 | [diff] [blame] | 86 | ./impl/backup-state.sh "$backupDir" "$workingDir" |
Jeff Gaston | cc0993d | 2019-04-02 18:02:44 -0400 | [diff] [blame] | 87 | } |
| 88 | |
| 89 | function restoreState() { |
| 90 | cd "$scriptPath" |
| 91 | backupDir="$1" |
Jeff Gaston | 6323450 | 2019-07-09 13:47:31 -0400 | [diff] [blame] | 92 | ./impl/restore-state.sh "$backupDir" "$workingDir" |
Jeff Gaston | cc0993d | 2019-04-02 18:02:44 -0400 | [diff] [blame] | 93 | } |
| 94 | |
| 95 | function clearState() { |
| 96 | restoreState /dev/null |
| 97 | } |
| 98 | |
| 99 | echo |
| 100 | echo "Making sure that we can reproduce the build failure" |
| 101 | if runBuild ./gradlew $gradleArgs; then |
| 102 | echo |
| 103 | echo "This script failed to reproduce the build failure." |
| 104 | echo "If the build failure you were observing was in Android Studio, then:" |
| 105 | echo ' Were you launching Android Studio by running `./studiow`?' |
| 106 | echo " Try asking a team member why Android Studio is failing but gradlew is succeeding" |
| 107 | echo "If you previously observed a build failure, then this means one of:" |
| 108 | echo " The state of your build is different than when you started your previous build" |
| 109 | echo " You could ask a team member if they've seen this error." |
| 110 | echo " The build is nondeterministic" |
| 111 | echo " If this seems likely to you, then please open a bug." |
| 112 | exit 1 |
| 113 | else |
| 114 | echo |
| 115 | echo "Reproduced build failure" |
| 116 | fi |
| 117 | |
| 118 | echo |
| 119 | echo "Stopping the Gradle Daemon and rebuilding" |
| 120 | cd "$supportRoot" |
| 121 | ./gradlew --stop || true |
| 122 | if runBuild ./gradlew --no-daemon $gradleArgs; then |
| 123 | echo |
| 124 | echo "The build passed when disabling the Gradle Daemon" |
| 125 | echo "This suggests that there is some state saved in the Gradle Daemon that is causing a failure." |
| 126 | echo "Unfortunately, this script does not know how to diagnose this further." |
| 127 | echo "You could ask a team member if they've seen this error." |
| 128 | exit 1 |
| 129 | else |
| 130 | echo |
| 131 | echo "The build failed even with the Gradle Daemon disabled." |
| 132 | echo "This may mean that there is state stored in a file somewhere, triggering the build to fail." |
| 133 | echo "We will investigate the possibility of saved state next." |
| 134 | echo |
| 135 | backupState "$tempDir/prev" |
| 136 | fi |
| 137 | |
| 138 | echo |
| 139 | echo "Checking whether a clean build passes" |
| 140 | clearState |
| 141 | backupState "$tempDir/empty" |
| 142 | successState="$tempDir/empty" |
| 143 | if runBuild ./gradlew --no-daemon $gradleArgs; then |
| 144 | echo |
| 145 | echo "The clean build passed, so we can now investigate what cached state is triggering this build to fail." |
| 146 | backupState "$tempDir/clean" |
| 147 | else |
| 148 | echo |
| 149 | echo "The clean build also failed." |
| 150 | echo "This may mean that the build is failing for everyone" |
| 151 | echo "This may mean that something about your checkout is different from others'" |
Jeff Gaston | 216c970 | 2019-05-14 17:44:16 -0400 | [diff] [blame^] | 152 | echo "You may be interested in running development/simplify-build-failure/simplify-build-failure.sh to identify the minimal set of source files required to reproduce this error" |
Jeff Gaston | cc0993d | 2019-04-02 18:02:44 -0400 | [diff] [blame] | 153 | echo "Checking the status of your checkout:" |
| 154 | checkStatus |
| 155 | exit 1 |
| 156 | fi |
| 157 | |
| 158 | echo |
| 159 | echo "Checking whether a second build passes when starting from the output of the first clean build" |
| 160 | if runBuild ./gradlew --no-daemon $gradleArgs; then |
| 161 | echo |
| 162 | echo "The next build after the clean build passed, so we can use the output of the first clean build as the successful state to compare against" |
| 163 | successState="$tempDir/clean" |
| 164 | else |
| 165 | echo |
| 166 | echo "The next build after the clean build failed." |
| 167 | echo "Although this is unexpected, we should still be able to diagnose it." |
| 168 | echo "This might be slower than normal, though, because it may require us to rebuild more things more often" |
| 169 | fi |
| 170 | |
| 171 | echo |
| 172 | echo "Next we'll double-check that after restoring the failing state, the build fails" |
| 173 | restoreState "$tempDir/prev" |
| 174 | if runBuild ./gradlew --no-daemon $gradleArgs; then |
| 175 | echo |
| 176 | echo "After restoring the saved state, the build passed." |
| 177 | echo "This might mean that there is additional state being saved somewhere else that this script does not know about" |
| 178 | echo "This might mean that the success or failure status of the build is dependent on timestamps." |
| 179 | echo "This might mean that the build is nondeterministic." |
| 180 | echo "Unfortunately, this script does not know how to diagnose this further." |
| 181 | echo "You could:" |
| 182 | echo " Ask a team member if they know where the state may be stored" |
| 183 | echo " Ask a team member if they recognize the build error" |
| 184 | exit 1 |
| 185 | else |
| 186 | echo |
| 187 | echo "After restoring the saved state, the build failed. This confirms that this script is successfully saving and restoring the relevant state" |
| 188 | fi |
| 189 | |
| 190 | # Now ask diff-filterer.py to run a binary search to determine what the relevant differences are between "$tempDir/prev" and "$tempDir/clean" |
| 191 | echo |
| 192 | echo "Binary-searching the contents of the two output directories until the relevant differences are identified." |
| 193 | echo "This may take a while." |
| 194 | echo |
Jeff Gaston | a58e308 | 2019-08-05 19:44:26 -0400 | [diff] [blame] | 195 | if runBuild "$supportRoot/development/file-utils/diff-filterer.py --assume-input-states-are-correct --work-path $tempDir $successState $tempDir/prev \"$scriptPath/impl/restore-state.sh . $workingDir && cd $supportRoot && ./gradlew --no-daemon $gradleArgs\""; then |
Jeff Gaston | cc0993d | 2019-04-02 18:02:44 -0400 | [diff] [blame] | 196 | echo |
| 197 | echo "There should be something wrong with the above file state" |
| 198 | echo "Hopefully the output from diff-filterer.py above is enough information for you to figure out what is wrong" |
| 199 | echo "If not, you could ask a team member about your original error message and see if they have any ideas" |
| 200 | else |
| 201 | echo |
| 202 | echo "Something went wrong running diff-filterer.py" |
| 203 | echo "Maybe that means the build is nondeterministic" |
| 204 | echo "Maybe that means that there's something wrong with this script ($0)" |
| 205 | fi |