Home | History | Annotate | Download | only in android
      1 # Copyright (c) 2012 Google Inc.
      2 # All rights reserved.
      3 #
      4 # Redistribution and use in source and binary forms, with or without
      5 # modification, are permitted provided that the following conditions are
      6 # met:
      7 #
      8 #     * Redistributions of source code must retain the above copyright
      9 # notice, this list of conditions and the following disclaimer.
     10 #     * Redistributions in binary form must reproduce the above
     11 # copyright notice, this list of conditions and the following disclaimer
     12 # in the documentation and/or other materials provided with the
     13 # distribution.
     14 #     * Neither the name of Google Inc. nor the names of its
     15 # contributors may be used to endorse or promote products derived from
     16 # this software without specific prior written permission.
     17 #
     18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 # Collection of common shell functions for 'run-checks.sh' et 'test-shell.sh'
     31 
     32 # All internal variables and functions use an underscore as a prefix
     33 # (e.g. _VERBOSE, _ALL_CLEANUPS, etc..).
     34 
     35 # Sanitize the environment
     36 export LANG=C
     37 export LC_ALL=C
     38 
     39 if [ "$BASH_VERSION" ]; then
     40   set -o posix
     41 fi
     42 
     43 # Utility functions
     44 
     45 _ALL_CLEANUPS=
     46 
     47 # Register a function to be called when the script exits, even in case of
     48 # Ctrl-C, logout, etc.
     49 # $1: function name.
     50 atexit () {
     51   if [ -z "$_ALL_CLEANUPS" ]; then
     52     _ALL_CLEANUPS=$1
     53     # Ensure a clean exit when the script is:
     54     #  - Exiting normally (EXIT)
     55     #  - Interrupted by Ctrl-C (INT)
     56     #  - Interrupted by log out (HUP)
     57     #  - Being asked to quit nicely (TERM)
     58     #  - Being asked to quit and dump core (QUIT)
     59     trap "_exit_cleanups \$?" EXIT INT HUP QUIT TERM
     60   else
     61     _ALL_CLEANUPS="$_ALL_CLEANUPS $1"
     62   fi
     63 }
     64 
     65 # Called on exit if at least one function was registered with atexit
     66 # $1: final exit status code
     67 _exit_cleanups () {
     68   local CLEANUP CLEANUPS
     69   # Ignore calls to atexit during cleanups
     70   CLEANUPS=$_ALL_CLEANUPS
     71   _ALL_CLEANUPS=
     72   for CLEANUP in $CLEANUPS; do
     73     ($CLEANUP)
     74   done
     75   exit "$@"
     76 }
     77 
     78 
     79 
     80 
     81 # Dump a panic message then exit.
     82 # $1+: message
     83 panic () {
     84   echo "ERROR: $@" >&2
     85   exit 1
     86 }
     87 
     88 # If the previous command failed, dump a panic message then exit.
     89 # $1+: message.
     90 fail_panic () {
     91   if [ $? != 0 ]; then
     92     panic "$@"
     93   fi;
     94 }
     95 
     96 _VERBOSE=0
     97 
     98 # Increase verbosity for dump/log/run/run2 functions
     99 increase_verbosity () {
    100   _VERBOSE=$(( $_VERBOSE + 1 ))
    101 }
    102 
    103 # Decrease verbosity
    104 decrease_verbosity () {
    105   _VERBOSE=$(( $_VERBOSE - 1 ))
    106 }
    107 
    108 # Returns success iff verbosity level is higher than a specific value
    109 # $1: verbosity level
    110 verbosity_is_higher_than () {
    111   [ "$_VERBOSE" -gt "$1" ]
    112 }
    113 
    114 # Returns success iff verbosity level is lower than a specific value
    115 # $1: verbosity level
    116 verbosity_is_lower_than () {
    117   [ "$_VERBOSE" -le "$1" ]
    118 }
    119 
    120 # Dump message to stdout, unless verbosity is < 0, i.e. --quiet was called
    121 # $1+: message
    122 dump () {
    123   if [ "$_VERBOSE" -ge 0 ]; then
    124     printf "%s\n" "$*"
    125   fi
    126 }
    127 
    128 # If --verbose was used, dump a message to stdout.
    129 # $1+: message
    130 log () {
    131   if [ "$_VERBOSE" -ge 1 ]; then
    132     printf "%s\n" "$*"
    133   fi
    134 }
    135 
    136 _RUN_LOG=
    137 
    138 # Set a run log file that can be used to collect the output of commands that
    139 # are not displayed.
    140 set_run_log () {
    141   _RUN_LOG=$1
    142 }
    143 
    144 # Run a command. Output depends on $_VERBOSE:
    145 #   $_VERBOSE <= 0:  Run command, store output into the run log
    146 #   $_VERBOSE >= 1:  Dump command, run it, output goest to stdout
    147 # Note: Ideally, the command's output would go to the run log for $_VERBOSE >= 1
    148 #       but the 'tee' tool doesn't preserve the status code of its input pipe
    149 #       in case of error.
    150 run () {
    151   local LOGILE
    152   if [ "$_RUN_LOG" ]; then
    153     LOGFILE=$_RUN_LOG
    154   else
    155     LOGFILE=/dev/null
    156   fi
    157 
    158   if [ "$_VERBOSE" -ge 1 ]; then
    159     echo "COMMAND: $@"
    160     "$@"
    161   else
    162     "$@" >>$LOGFILE 2>&1
    163   fi
    164 }
    165 
    166 # Same as run(), but only dump command output for $_VERBOSE >= 2
    167 run2 () {
    168   local LOGILE
    169   if [ "$_RUN_LOG" ]; then
    170     LOGFILE=$_RUN_LOG
    171   else
    172     LOGFILE=/dev/null
    173   fi
    174 
    175   if [ "$_VERBOSE" -ge 1 ]; then
    176     echo "COMMAND: $@"
    177   fi
    178   if [ "$_VERBOSE" -ge 2 ]; then
    179     "$@"
    180   else
    181     "$@" >>$LOGFILE 2>&1
    182   fi
    183 }
    184 
    185 # Extract number of cores to speed up the builds
    186 # Out: number of CPU cores
    187 get_core_count () {
    188   case $(uname -s) in
    189     Linux)
    190       grep -c -e '^processor' /proc/cpuinfo
    191       ;;
    192     Darwin)
    193       sysctl -n hw.ncpu
    194       ;;
    195     CYGWIN*|*_NT-*)
    196       echo $NUMBER_OF_PROCESSORS
    197       ;;
    198     *)
    199       echo 1
    200       ;;
    201   esac
    202 }
    203 
    204 
    205 # Check for the Android ADB program.
    206 #
    207 # On success, return nothing, but updates internal variables so later calls to
    208 # adb_shell, adb_push, etc.. will work. You can get the path to the ADB program
    209 # with adb_get_program if needed.
    210 #
    211 # On failure, returns 1, and updates the internal adb error message, which can
    212 # be retrieved with adb_get_error.
    213 #
    214 # $1: optional ADB program path.
    215 # Return: success or failure.
    216 _ADB=
    217 _ADB_STATUS=
    218 _ADB_ERROR=
    219 
    220 adb_check () {
    221   # First, try to find the executable in the path, or the SDK install dir.
    222   _ADB=$1
    223   if [ -z "$_ADB" ]; then
    224     _ADB=$(which adb 2>/dev/null)
    225     if [ -z "$_ADB" -a "$ANDROID_SDK_ROOT" ]; then
    226       _ADB=$ANDROID_SDK_ROOT/platform-tools/adb
    227       if [ ! -f "$_ADB" ]; then
    228         _ADB=
    229       fi
    230     fi
    231     if [ -z "$_ADB" ]; then
    232       _ADB_STATUS=1
    233       _ADB_ERROR="The Android 'adb' tool is not in your path."
    234       return 1
    235     fi
    236   fi
    237 
    238   log "Found ADB program: $_ADB"
    239 
    240   # Check that it works correctly
    241   local ADB_VERSION
    242   ADB_VERSION=$("$_ADB" version 2>/dev/null)
    243   case $ADB_VERSION in
    244     "Android Debug Bridge "*) # Pass
    245       log "Found ADB version: $ADB_VERSION"
    246       ;;
    247     *) # Fail
    248       _ADB_ERROR="Your ADB binary reports a bad version ($ADB_VERSION): $_ADB"
    249       _ADB_STATUS=1
    250       return 1
    251   esac
    252 
    253   _ADB_STATUS=0
    254   return 0
    255 }
    256 
    257 
    258 # Return the path to the Android ADB program, if correctly detected.
    259 # On failure, return the empty string.
    260 # Out: ADB program path (or empty on failure)
    261 # Return: success or failure.
    262 adb_get_program () {
    263   # Return cached value as soon as possible.
    264   if [ -z "$_ADB_STATUS" ]; then
    265     adb_check $1
    266   fi
    267   echo "$_ADB"
    268   return $_ADB_STATUS
    269 }
    270 
    271 # Return the error corresponding to the last ADB function failure.
    272 adb_get_error () {
    273   echo "$_ADB_ERROR"
    274 }
    275 
    276 # Check that there is one device connected through ADB.
    277 # In case of failure, use adb_get_error to know why this failed.
    278 # $1: Optional adb program path
    279 # Return: success or failure.
    280 _ADB_DEVICE=
    281 _ADB_DEVICE_STATUS=
    282 adb_check_device () {
    283   if [ "$_ADB_DEVICE_STATUS" ]; then
    284     return $_ADB_DEVICE_STATUS
    285   fi
    286 
    287   # Check for ADB.
    288   if ! adb_check $1; then
    289     _ADB_DEVICE_STATUS=$_ADB_STATUS
    290     return 1
    291   fi
    292 
    293   local ADB_DEVICES NUM_DEVICES FINGERPRINT
    294 
    295   # Count the number of connected devices.
    296   ADB_DEVICES=$("$_ADB" devices 2>/dev/null | awk '$2 == "device" { print $1; }')
    297   NUM_DEVICES=$(echo "$ADB_DEVICES" | wc -l)
    298   case $NUM_DEVICES in
    299     0)
    300       _ADB_ERROR="No Android device connected. Please connect one to your machine."
    301       _ADB_DEVICE_STATUS=1
    302       return 1
    303       ;;
    304     1) # Pass
    305       # Ensure the same device will be called in later adb_shell calls.
    306       export ANDROID_SERIAL=$ADB_DEVICES
    307       ;;
    308     *) # 2 or more devices.
    309       if [ "$ANDROID_SERIAL" ]; then
    310         ADB_DEVICES=$ANDROID_SERIAL
    311         NUM_DEVICES=1
    312       else
    313         _ADB_ERROR="More than one Android device connected. \
    314 Please define ANDROID_SERIAL in your environment"
    315         _ADB_DEVICE_STATUS=1
    316         return 1
    317       fi
    318       ;;
    319   esac
    320 
    321   _ADB_DEVICE_STATUS=0
    322   _ADB_DEVICE=$ADB_DEVICES
    323 
    324   FINGERPRINT=$(adb_shell getprop ro.build.fingerprint)
    325   log "Using ADB device: $ANDROID_SERIAL ($FINGERPRINT)"
    326   return 0
    327 }
    328 
    329 # The 'adb shell' command is pretty hopeless, try to make sense of it by:
    330 #   1/ Removing trailing \r from line endings.
    331 #   2/ Ensuring the function returns the command's status code.
    332 #
    333 # $1+: Command
    334 # Out: command output (stdout + stderr combined)
    335 # Return: command exit status
    336 adb_shell () {
    337   local RET ADB_LOG
    338   # Check for ADB device.
    339   adb_check_device || return 1
    340   ADB_LOG=$(mktemp "${TMPDIR:-/tmp}/adb-XXXXXXXX")
    341   "$_ADB" shell "$@" ";" echo \$? > "$ADB_LOG" 2>&1
    342   sed -i -e 's![[:cntrl:]]!!g' "$ADB_LOG"  # Remove \r.
    343   RET=$(sed -e '$!d' "$ADB_LOG")           # Last line contains status code.
    344   sed -e '$d' "$ADB_LOG"                   # Print everything except last line.
    345   rm -f "$ADB_LOG"
    346   return $RET
    347 }
    348 
    349 # Push a file to a device.
    350 # $1: source file path
    351 # $2: device target file path
    352 # Return: success or failure.
    353 adb_push () {
    354   adb_check_device || return 1
    355   run "$_ADB" push "$1" "$2"
    356 }
    357 
    358 # Pull a file from a device
    359 # $1: device file path
    360 # $2: target host file path
    361 # Return: success or failure.
    362 adb_pull () {
    363   adb_check_device || return 1
    364   run "$_ADB" pull "$1" "$2"
    365 }
    366 
    367 # Same as adb_push, but will panic if the operations didn't succeed.
    368 adb_install () {
    369   adb_push "$@"
    370   fail_panic "Failed to install $1 to the Android device at $2"
    371 }
    372 
    373