1#!/usr/bin/env bash
2set -o pipefail
3set -e
4
5##############################################################################
6##
7##  Gradle start up script for UN*X
8##
9##############################################################################
10
11# --------- androidx specific code needed for build server. ------------------
12
13SCRIPT_PATH="$(cd $(dirname $0) && pwd -P)"
14if [ -n "$OUT_DIR" ] ; then
15    mkdir -p "$OUT_DIR"
16    OUT_DIR="$(cd $OUT_DIR && pwd -P)"
17    export TMPDIR="$OUT_DIR/tmp"
18elif [[ $SCRIPT_PATH == /google/cog/* ]] ; then
19    export OUT_DIR="$HOME/androidxout"
20else
21    CHECKOUT_ROOT="$(cd $SCRIPT_PATH/../.. && pwd -P)"
22    export OUT_DIR="$CHECKOUT_ROOT/out"
23fi
24export GRADLE_USER_HOME="$OUT_DIR/.gradle"
25
26ORG_GRADLE_JVMARGS="$(cd $SCRIPT_PATH && grep org.gradle.jvmargs gradle.properties | sed 's/^/-D/')"
27if [ -n "$DIST_DIR" ]; then
28    mkdir -p "$DIST_DIR"
29    DIST_DIR="$(cd $DIST_DIR && pwd -P)"
30
31    # tell Gradle where to put a heap dump on failure
32    ORG_GRADLE_JVMARGS="$(echo $ORG_GRADLE_JVMARGS | sed "s|$| -XX:HeapDumpPath=$DIST_DIR|")"
33
34    # We don't set a default DIST_DIR in an else clause here because Studio doesn't use gradlew
35    # and doesn't set DIST_DIR and we want gradlew and Studio to match
36fi
37
38# unset ANDROID_BUILD_TOP so that Lint doesn't think we're building the platform itself
39unset ANDROID_BUILD_TOP
40# ----------------------------------------------------------------------------
41
42# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
43
44APP_NAME="Gradle"
45APP_BASE_NAME=`basename "$0"`
46
47# Use the maximum available, or set MAX_FD != -1 to use that value.
48MAX_FD="maximum"
49
50warn ( ) {
51    echo "$*"
52}
53
54die ( ) {
55    echo
56    echo "$*"
57    echo
58    exit 1
59}
60
61# OS specific support (must be 'true' or 'false').
62cygwin=false
63msys=false
64darwin=false
65case "`uname`" in
66  CYGWIN* )
67    cygwin=true
68    ;;
69  Darwin* )
70    darwin=true
71    ;;
72  MINGW* )
73    msys=true
74    ;;
75esac
76platform_suffix="x86"
77case "$(arch)" in
78  arm64* )
79    platform_suffix="arm64"
80esac
81# Attempt to set APP_HOME
82# Resolve links: $0 may be a link
83PRG="$0"
84# Need this for relative symlinks.
85while [ -h "$PRG" ] ; do
86    ls=`ls -ld "$PRG"`
87    link=`expr "$ls" : '.*-> \(.*\)$'`
88    if expr "$link" : '/.*' > /dev/null; then
89        PRG="$link"
90    else
91        PRG=`dirname "$PRG"`"/$link"
92    fi
93done
94SAVED="`pwd`"
95cd "`dirname \"$PRG\"`/" >/dev/null
96APP_HOME="`pwd -P`"
97cd "$SAVED" >/dev/null
98
99CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
100
101# --------- androidx specific code needed for lint and java. ------------------
102
103# Pick the correct fullsdk for this OS.
104if [ $darwin == "true" ]; then
105    plat="darwin"
106else
107    plat="linux"
108fi
109
110# Tests for lint checks default to using sdk defined by this variable. This removes a lot of
111# setup from each lint module.
112export ANDROID_HOME="$APP_HOME/../../prebuilts/fullsdk-$plat"
113# override JAVA_HOME, because CI machines have it and it points to very old JDK
114export ANDROIDX_JDK17="$APP_HOME/../../prebuilts/jdk/jdk17/$plat-$platform_suffix"
115export ANDROIDX_JDK21="$APP_HOME/../../prebuilts/jdk/jdk21/$plat-$platform_suffix"
116export JAVA_HOME=$ANDROIDX_JDK21
117export STUDIO_GRADLE_JDK=$JAVA_HOME
118
119# Warn developers if they try to build top level project without the full checkout
120[ ! -d "$JAVA_HOME" ] && echo "Failed to find: $JAVA_HOME
121
122Typically, this means either:
1231. You are using the standalone AndroidX checkout, e.g. GitHub, which only supports
124   building a subset of projects. See CONTRIBUTING.md for details.
1252. You are using the repo checkout, but the last repo sync failed. Use repo status
126   to check for projects which are partially-synced, e.g. showing ***NO BRANCH***." && exit -1
127
128# ----------------------------------------------------------------------------
129
130# Determine the Java command to use to start the JVM.
131if [ -n "$JAVA_HOME" ] ; then
132    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
133        # IBM's JDK on AIX uses strange locations for the executables
134        JAVACMD="$JAVA_HOME/jre/sh/java"
135    else
136        JAVACMD="$JAVA_HOME/bin/java"
137    fi
138    if [ ! -x "$JAVACMD" ] ; then
139        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
140
141Please set the JAVA_HOME variable in your environment to match the
142location of your Java installation."
143    fi
144else
145    JAVACMD="java"
146    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
147
148Please set the JAVA_HOME variable in your environment to match the
149location of your Java installation."
150fi
151
152# Increase the maximum file descriptors if we can.
153if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
154    MAX_FD_LIMIT=`ulimit -H -n`
155    if [ $? -eq 0 ] ; then
156        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
157            MAX_FD="$MAX_FD_LIMIT"
158        fi
159        ulimit -n $MAX_FD
160        if [ $? -ne 0 ] ; then
161            warn "Could not set maximum file descriptor limit: $MAX_FD"
162        fi
163    else
164        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
165    fi
166fi
167
168# For Darwin, add options to specify how the application appears in the dock
169if $darwin; then
170    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
171fi
172
173# For Cygwin, switch paths to Windows format before running java
174if $cygwin ; then
175    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
176    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
177    JAVACMD=`cygpath --unix "$JAVACMD"`
178
179    # We build the pattern for arguments to be converted via cygpath
180    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
181    SEP=""
182    for dir in $ROOTDIRSRAW ; do
183        ROOTDIRS="$ROOTDIRS$SEP$dir"
184        SEP="|"
185    done
186    OURCYGPATTERN="(^($ROOTDIRS))"
187    # Add a user-defined pattern to the cygpath arguments
188    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
189        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
190    fi
191    # Now convert the arguments - kludge to limit ourselves to /bin/sh
192    i=0
193    for arg in "$@" ; do
194        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
195        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
196
197        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
198            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
199        else
200            eval `echo args$i`="\"$arg\""
201        fi
202        i=$((i+1))
203    done
204    case $i in
205        (0) set -- ;;
206        (1) set -- "$args0" ;;
207        (2) set -- "$args0" "$args1" ;;
208        (3) set -- "$args0" "$args1" "$args2" ;;
209        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
210        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
211        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
212        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
213        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
214        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
215    esac
216fi
217
218# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
219function splitJvmOpts() {
220    JVM_OPTS=("$@")
221}
222eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
223JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
224
225#TODO: Remove HOME_SYSTEM_PROPERTY_ARGUMENT if https://github.com/gradle/gradle/issues/11433 gets fixed
226HOME_SYSTEM_PROPERTY_ARGUMENT=""
227if [ "$GRADLE_USER_HOME" != "" ]; then
228    HOME_SYSTEM_PROPERTY_ARGUMENT="-Duser.home=$GRADLE_USER_HOME"
229fi
230
231if [[ " ${@} " =~ " --clean " ]]; then
232  cleanCaches=true
233else
234  cleanCaches=false
235fi
236
237if [[ " ${@} " =~ " --no-ci " ]]; then
238  disableCi=true
239else
240  disableCi=false
241fi
242
243# Expand some arguments
244for compact in "--ci" "--strict" "--clean" "--no-ci"; do
245  expanded=""
246  if [ "$compact" == "--ci" ]; then
247    if [ "$disableCi" == "false" ]; then
248      expanded="--strict\
249       --stacktrace\
250       -Pandroidx.summarizeStderr\
251       -Pandroidx.enableAffectedModuleDetection\
252       -Pandroidx.printTimestamps\
253       --no-watch-fs\
254       -Pandroidx.highMemory\
255       --profile"
256    fi
257  fi
258  if [ "$compact" == "--strict" ]; then
259    expanded="-Pandroidx.validateNoUnrecognizedMessages\
260     -Pandroidx.verifyUpToDate"
261    if [ "$USE_ANDROIDX_REMOTE_BUILD_CACHE" == "" -o "$USE_ANDROIDX_REMOTE_BUILD_CACHE" == "false" ]; then
262      expanded="$expanded --offline"
263    fi
264  fi
265  # if compact is something else then we parsed the argument above but
266  # still have to remove it (expanded == "") to avoid confusing Gradle
267
268  # check whether this particular compat argument was passed (and therefore needs expansion)
269  if [[ " ${@} " =~ " $compact " ]]; then
270    # Expand an individual argument
271    # Start by making a copy of our list of arguments and iterating through the copy
272    for arg in "$@"; do
273      # Remove this argument from our list of arguments.
274      # By the time we've completed this loop, we will have removed the original copy of
275      # each argument, and potentially re-added a new copy or an expansion of each.
276      shift
277      # Determine whether to expand this argument
278      if [ "$arg" == "$compact" ]; then
279        # Add the expansion to our arguments
280        set -- "$@" $expanded
281        if [ "$expanded" != "" ]; then
282          echo "gradlew expanded '$compact' into '$expanded'"
283          echo
284        fi
285        # We avoid re-adding this argument itself back into the list for two reasons:
286        # 1. This argument might not be directly understood by Gradle
287        # 2. We want to enforce that all behaviors enabled by this flag can be toggled independently,
288        # so we don't want it to be easy to inadvertently check for the presence of this flag
289        # specifically
290      else
291        # Add this argument back into our arguments
292        set -- "$@" "$arg"
293      fi
294    done
295  fi
296done
297
298# workaround for https://github.com/gradle/gradle/issues/18386
299if [[ " ${@} " =~ " --profile " ]]; then
300  mkdir -p reports
301fi
302
303raiseMemory=false
304if [[ " ${@} " =~ " -Pandroidx.highMemory " ]]; then
305    raiseMemory=true
306fi
307if [[ " ${@} " =~ " -Pandroidx.lowMemory " ]]; then
308  if [ "$raiseMemory" == "true" ]; then
309    echo "androidx.lowMemory overriding androidx.highMemory"
310    echo
311  fi
312  raiseMemory=false
313fi
314
315if [ "$raiseMemory" == "true" ]; then
316  # Set the initial heap size to match the max heap size,
317  # by replacing a string like "-Xmx1g" with one like "-Xms1g -Xmx1g"
318  MAX_MEM=32g
319  ORG_GRADLE_JVMARGS="$(echo $ORG_GRADLE_JVMARGS | sed "s/-Xmx\([^ ]*\)/-Xms$MAX_MEM -Xmx$MAX_MEM/")"
320
321  # Increase the compiler cache size: b/260643754 . Remove when updating to JDK 20 ( https://bugs.openjdk.org/browse/JDK-8295724 )
322  ORG_GRADLE_JVMARGS="$(echo $ORG_GRADLE_JVMARGS | sed "s|$| -XX:ReservedCodeCacheSize=576M|")"
323fi
324
325# check whether the user has requested profiling via yourkit
326yourkitArgPrefix="androidx.profile.yourkitAgentPath"
327yourkitAgentPath=""
328if [[ " ${@}" =~ " -P$yourkitArgPrefix" ]]; then
329  for arg in "$@"; do
330    if echo "$arg" | grep "${yourkitArgPrefix}=" >/dev/null; then
331      yourkitAgentPath="$(echo "$arg" | sed "s/-P${yourkitArgPrefix}=//")"
332    fi
333  done
334  if [ "$yourkitAgentPath" == "" ]; then
335    echo "Error: $yourkitArgPrefix must be set to the path of the YourKit Java agent" >&2
336    exit 1
337  fi
338  if [ ! -e "$yourkitAgentPath" ]; then
339    echo "Error: $yourkitAgentPath does not exist" >&2
340    exit 1
341  fi
342  # add the agent to the path
343  export _JAVA_OPTIONS="$_JAVA_OPTIONS -agentpath:$yourkitAgentPath"
344  # add arguments
345  set -- "$@" --no-daemon --rerun-tasks
346
347  # lots of blank lines because these messages are important
348  echo
349  echo
350  echo
351  echo
352  echo
353  # suggest --clean
354  if [ "$cleanCaches" == "false" ]; then
355    echo "When setting $yourkitArgPrefix you may also want to pass --clean"
356  fi
357  COLOR_YELLOW="\u001B[33m"
358  COLOR_CLEAR="\u001B[0m"
359
360  echo -e "${COLOR_YELLOW}Also be sure to start the YourKit user interface and connect to the appropriate Java process (probably the Gradle Daemon)${COLOR_CLEAR}"
361  echo
362  echo
363  echo
364  echo
365  echo
366fi
367
368if [[ " ${@} " =~ " --scan " ]]; then
369  if [[ " ${@} " =~ " --offline " ]]; then
370    echo "--scan incompatible with --offline"
371    echo "you could try --no-ci"
372    exit 1
373  fi
374fi
375
376function removeCaches() {
377  rm -rf $SCRIPT_PATH/.gradle
378  rm -rf $SCRIPT_PATH/buildSrc/.gradle
379  rm -f  $SCRIPT_PATH/local.properties
380  if [ "$GRADLE_USER_HOME" != "" ]; then
381    rm -rf "$GRADLE_USER_HOME"
382  else
383    rm -rf ~/.gradle
384  fi
385  # https://github.com/gradle/gradle/issues/18386
386  rm -rf $SCRIPT_PATH/reports
387  rm -rf $SCRIPT_PATH/build
388  rm -rf $OUT_DIR
389}
390
391# Move any preexisting build scan to make room for a new one
392# After moving a build scan several times it eventually gets deleted
393function rotateBuildScans() {
394  filePrefix="$1"
395  iPlus1="10"
396  for i in $(seq 9 -1 1); do
397    mv "${filePrefix}.${i}.zip" "${filePrefix}.${iPlus1}.zip" 2>/dev/null || true
398    iPlus1=$i
399  done
400  mv ${filePrefix}.zip "${filePrefix}.1.zip" 2>/dev/null || true
401}
402
403function runGradle() {
404  if [ "$TMPDIR" != "" ]; then
405    mkdir -p "$TMPDIR"
406    TMPDIR_ARG="-Djava.io.tmpdir=$TMPDIR"
407  fi
408
409  processOutput=false
410  if [[ " ${@} " =~ " -Pandroidx.validateNoUnrecognizedMessages " ]]; then
411    processOutput=true
412  fi
413  if [[ " ${@} " =~ " -Pandroidx.summarizeStderr " ]]; then
414    processOutput=true
415  fi
416  if [[ "${@} " =~ " -Pandroidx.printTimestamps " ]]; then
417    processOutput=true
418  fi
419  if [ "$processOutput" == "true" ]; then
420    wrapper="$SCRIPT_PATH/development/build_log_processor.sh"
421  else
422    wrapper=""
423  fi
424
425  RETURN_VALUE=0
426  set -- "$@" -Dorg.gradle.projectcachedir="$OUT_DIR/gradle-project-cache"
427  # Disabled in Studio until these errors become shown (b/268380971) or computed more quickly (https://github.com/gradle/gradle/issues/23272)
428  if [[ " ${@} " =~ " --dependency-verification=" ]]; then
429    VERIFICATION_ARGUMENT="" # already specified by caller
430  else
431    VERIFICATION_ARGUMENT=--dependency-verification=strict
432  fi
433  if $wrapper "$JAVACMD" "${JVM_OPTS[@]}" $TMPDIR_ARG -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain $HOME_SYSTEM_PROPERTY_ARGUMENT $TMPDIR_ARG $VERIFICATION_ARGUMENT "$ORG_GRADLE_JVMARGS" "$@"; then
434    RETURN_VALUE=0
435  else
436    # Print AndroidX-specific help message if build fails
437    # Have to do this build-failure detection in gradlew rather than in build.gradle
438    # so that this message still prints even if buildSrc itself fails
439    echo
440    echo For help with unexpected failures, see development/diagnose-build-failure/README.md
441    echo
442    RETURN_VALUE=1
443  fi
444
445  # If the caller specified where to save data, then also save the build scan data
446  if [ "$DIST_DIR" != "" ]; then
447    if [ "$GRADLE_USER_HOME" != "" ]; then
448      scanDir="$GRADLE_USER_HOME/build-scan-data"
449      if [ -e "$scanDir" ]; then
450        if [[ "$DISALLOW_TASK_EXECUTION" != "" ]]; then
451          zipPrefix="$DIST_DIR/scan-up-to-date"
452        else
453          zipPrefix="$DIST_DIR/scan"
454        fi
455        rotateBuildScans "$zipPrefix"
456        zipPath="${zipPrefix}.zip"
457        cd "$GRADLE_USER_HOME/build-scan-data"
458        zip -q -r "$zipPath" .
459        cd -
460      fi
461    fi
462  fi
463  return $RETURN_VALUE
464}
465
466if [ "$cleanCaches" == true ]; then
467  echo "IF ./gradlew --clean FIXES YOUR BUILD; OPEN A BUG."
468  echo "In nearly all cases, it should not be necessary to run a clean build."
469  echo
470  # one case where it is convenient to have a clean build is for double-checking that a build failure isn't due to an incremental build failure
471  # another case where it is convenient to have a clean build is for performance testing
472  # another case where it is convenient to have a clean build is when you're modifying the build and may have introduced some errors but haven't shared your changes yet (at which point you should have fixed the errors)
473
474  echo "Stopping Gradle daemons"
475  runGradle --stop || true
476  echo
477
478  backupDir=~/androidx-build-state-backup
479  ./development/diagnose-build-failure/impl/backup-state.sh "$backupDir" --move # prints that it is saving state into this dir"
480
481  echo "To restore this state later, run:"
482  echo
483  echo "  ./development/diagnose-build-failure/impl/restore-state.sh $backupDir"
484  echo
485  echo "Running Gradle"
486  echo
487fi
488
489if [[ "$DISALLOW_TASK_EXECUTION" != "" ]]; then
490  echo "Setting 'DISALLOW_TASK_EXECUTION' directly is forbidden. Did you mean -Pandroidx.verifyUpToDate ?"
491  echo "See TaskUpToDateValidator.java for more information"
492  exit 1
493fi
494
495runGradle "$@"
496# Check whether we were given the "-Pandroidx.verifyUpToDate" argument
497if [[ " ${@} " =~ " -Pandroidx.verifyUpToDate " ]]; then
498  # Re-run Gradle, and find all tasks that are unexpectly out of date
499  if ! DISALLOW_TASK_EXECUTION=true runGradle "$@" --continue; then
500    echo >&2
501    echo "TaskUpToDateValidator's second build failed. To reproduce, try running './gradlew -Pandroidx.verifyUpToDate <failing tasks>'" >&2
502    exit 1
503  fi
504fi
505