blob: 1b3d64eabae97b3210d98d701d4eca9d62823010 [file] [log] [blame]
Jeff Gastoncc0993d2019-04-02 18:02:44 -04001#!/bin/bash
2set -e
3set -u
4
5scriptName="$(basename $0)"
6
7function 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
26gradleArgs="$*"
27if [ "$gradleArgs" == "" ]; then
28 usage
29fi
30
Jeff Gaston63234502019-07-09 13:47:31 -040031workingDir="$(pwd)"
32if [ ! -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
36fi
37
Jeff Gastoncc0993d2019-04-02 18:02:44 -040038scriptPath="$(cd $(dirname $0) && pwd)"
39supportRoot="$(cd $scriptPath/../.. && pwd)"
40checkoutRoot="$(cd $supportRoot/../.. && pwd)"
41tempDir="/tmp/diagnose-build-failure"
42if [ "${GRADLE_USER_HOME:-}" == "" ]; then
43 GRADLE_USER_HOME="$(cd ~ && pwd)/.gradle"
44fi
45COLOR_WHITE="\e[97m"
46COLOR_GREEN="\e[32m"
47
48function checkStatusRepo() {
49 repo status
50}
51
52function checkStatusGit() {
53 git status
54 git log -1
55}
56
57function checkStatus() {
58 cd "$checkoutRoot"
59 if [ "-e" .repo ]; then
60 checkStatusRepo
61 else
62 checkStatusGit
63 fi
64}
65
66function 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
83function backupState() {
84 cd "$scriptPath"
85 backupDir="$1"
Jeff Gaston63234502019-07-09 13:47:31 -040086 ./impl/backup-state.sh "$backupDir" "$workingDir"
Jeff Gastoncc0993d2019-04-02 18:02:44 -040087}
88
89function restoreState() {
90 cd "$scriptPath"
91 backupDir="$1"
Jeff Gaston63234502019-07-09 13:47:31 -040092 ./impl/restore-state.sh "$backupDir" "$workingDir"
Jeff Gastoncc0993d2019-04-02 18:02:44 -040093}
94
95function clearState() {
96 restoreState /dev/null
97}
98
99echo
100echo "Making sure that we can reproduce the build failure"
101if 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
113else
114 echo
115 echo "Reproduced build failure"
116fi
117
118echo
119echo "Stopping the Gradle Daemon and rebuilding"
120cd "$supportRoot"
121./gradlew --stop || true
122if 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
129else
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"
136fi
137
138echo
139echo "Checking whether a clean build passes"
140clearState
141backupState "$tempDir/empty"
142successState="$tempDir/empty"
143if 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"
147else
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'"
152 echo "Checking the status of your checkout:"
153 checkStatus
154 exit 1
155fi
156
157echo
158echo "Checking whether a second build passes when starting from the output of the first clean build"
159if runBuild ./gradlew --no-daemon $gradleArgs; then
160 echo
161 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"
162 successState="$tempDir/clean"
163else
164 echo
165 echo "The next build after the clean build failed."
166 echo "Although this is unexpected, we should still be able to diagnose it."
167 echo "This might be slower than normal, though, because it may require us to rebuild more things more often"
168fi
169
170echo
171echo "Next we'll double-check that after restoring the failing state, the build fails"
172restoreState "$tempDir/prev"
173if runBuild ./gradlew --no-daemon $gradleArgs; then
174 echo
175 echo "After restoring the saved state, the build passed."
176 echo "This might mean that there is additional state being saved somewhere else that this script does not know about"
177 echo "This might mean that the success or failure status of the build is dependent on timestamps."
178 echo "This might mean that the build is nondeterministic."
179 echo "Unfortunately, this script does not know how to diagnose this further."
180 echo "You could:"
181 echo " Ask a team member if they know where the state may be stored"
182 echo " Ask a team member if they recognize the build error"
183 exit 1
184else
185 echo
186 echo "After restoring the saved state, the build failed. This confirms that this script is successfully saving and restoring the relevant state"
187fi
188
189# Now ask diff-filterer.py to run a binary search to determine what the relevant differences are between "$tempDir/prev" and "$tempDir/clean"
190echo
191echo "Binary-searching the contents of the two output directories until the relevant differences are identified."
192echo "This may take a while."
193echo
Jeff Gaston63234502019-07-09 13:47:31 -0400194if runBuild "$supportRoot/development/file-utils/diff-filterer.py --assume-no-side-effects --assume-input-states-are-correct $successState $tempDir/prev \"$scriptPath/impl/restore-state.sh . $workingDir && cd $supportRoot && ./gradlew --no-daemon $gradleArgs\""; then
Jeff Gastoncc0993d2019-04-02 18:02:44 -0400195 echo
196 echo "There should be something wrong with the above file state"
197 echo "Hopefully the output from diff-filterer.py above is enough information for you to figure out what is wrong"
198 echo "If not, you could ask a team member about your original error message and see if they have any ideas"
199else
200 echo
201 echo "Something went wrong running diff-filterer.py"
202 echo "Maybe that means the build is nondeterministic"
203 echo "Maybe that means that there's something wrong with this script ($0)"
204fi