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