blob: 5012dce3b3a170c99e0df6f25ba05de18a10a842 [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)"
Jeff Gastona58e3082019-08-05 19:44:26 -040041tempDir="$checkoutRoot/diagnose-build-failure/"
Jeff Gastoncc0993d2019-04-02 18:02:44 -040042if [ "${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'"
Jeff Gaston216c9702019-05-14 17:44:16 -0400152 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 Gastoncc0993d2019-04-02 18:02:44 -0400153 echo "Checking the status of your checkout:"
154 checkStatus
155 exit 1
156fi
157
158echo
159echo "Checking whether a second build passes when starting from the output of the first clean build"
160if 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"
164else
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"
169fi
170
171echo
172echo "Next we'll double-check that after restoring the failing state, the build fails"
173restoreState "$tempDir/prev"
174if 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
185else
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"
188fi
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"
191echo
192echo "Binary-searching the contents of the two output directories until the relevant differences are identified."
193echo "This may take a while."
194echo
Jeff Gastona58e3082019-08-05 19:44:26 -0400195if 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 Gastoncc0993d2019-04-02 18:02:44 -0400196 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"
200else
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)"
205fi