1#!/bin/bash
2
3# This script is used to rebuild all emulator binaries from sources
4# and package them for easier distribution.
5
6set -e
7export LANG=C
8export LC_ALL=C
9
10PROGDIR=$(dirname "$0")
11PROGNAME=$(basename "$0")
12
13panic () {
14  echo "ERROR: $@"
15  exit 1
16}
17
18VERBOSE=1
19
20# Dump a message if VERBOSE is greater or equal to $1
21# $1: level
22# $2+: Message to print.
23dump_n () {
24    local LEVEL=$1
25    shift
26    if [ "$VERBOSE" -ge "$LEVEL" ]; then
27        printf "%s\n" "$@"
28    fi
29}
30
31# Dump a message at VERBOSE level 1 (the default one).
32dump () {
33    dump_n 1 "$@"
34}
35
36# Dump a message at VERBOSE level 2 (if --verbose was used).
37log () {
38    dump_n 2 "$@"
39}
40
41# Run a command, dump its output depending on VERBOSE level, i.e.:
42#  0 -> Don't display anything.
43#  1 -> Display error messages only.
44#  2 -> Display the command, its output and error.
45run () {
46    case $VERBOSE in
47        0)
48            "$@" >/dev/null 2>&1
49            ;;
50        1)
51            "$@" >/dev/null
52            ;;
53        *)
54            echo "COMMAND: $@"
55            "$@"
56            ;;
57    esac
58}
59
60# Same as 'run', but slightly more quiet:
61#  0 or 1 -> Don't display anything
62#  2      -> Diplay the command, its output and error.
63run2 () {
64    case $VERBOSE in
65        0|1)
66            "$@" >/dev/null 2>&1
67            ;;
68        2)
69            echo "COMMAND: $@"
70            "$@" >/dev/null 2>&1
71            ;;
72        *)
73            echo "COMMAND: $@"
74            "$@"
75            ;;
76    esac
77}
78
79# $1: Source directory.
80# $2: Destination directory.
81# $3: List of files to copy, relative to $1 (if empty, all files will be copied).
82copy_directory_files () {
83  local SRCDIR DSTDIR FILES
84  SRCDIR=$1
85  DSTDIR=$2
86  shift; shift;
87  FILES="$@"
88
89  mkdir -p "$DSTDIR" || panic "Cannot create destination directory: $DSTDIR"
90  (cd $SRCDIR && tar cf - $FILES) | (cd $DSTDIR && tar xf -)
91}
92
93# $1: Source directory (must be a git checkout).
94# $2: Destination directory.
95copy_directory_git_files () {
96  local SRCDIR DSTDIR FILES
97  SRCDIR=$1
98  DSTDIR=$2
99  log "Copying git sources from $SRCDIR to $DSTDIR"
100  # The list of names can contain spaces, so put them in a file to avoid
101  # any issues.
102  TMP_FILE_LIST=$(mktemp)
103  (cd $SRCDIR && git ls-files) > $TMP_FILE_LIST
104  mkdir -p "$DSTDIR" || panic "Cannot create destination directory: $DSTDIR"
105  (cd $SRCDIR && tar cf - -T $TMP_FILE_LIST) | (cd $DSTDIR && tar xf -)
106  rm -f $TMP_FILE_LIST
107}
108
109# Convert a comma-separated list into a space-separated one.
110commas_to_spaces () {
111  printf "%s" "$@" | tr ',' ' '
112}
113
114# Rebuild Darwin binaries remotely through SSH
115# $1: Host name.
116# $2: Source package file.
117build_darwin_binaries_on () {
118  local HOST PKG_FILE PKG_FILE_BASENAME DST_DIR TARFLAGS
119  HOST=$1
120  PKG_FILE=$2
121
122  # The package file is ....../something-darwin.tar.bz2
123  # And should unpack to a single directory named 'something/'
124  # so extract the prefix from the package name.
125  PKG_FILE_BASENAME=$(basename "$PKG_FILE")
126  PKG_FILE_PREFIX=${PKG_FILE_BASENAME%%-sources.tar.bz2}
127  if [ "$PKG_FILE_PREFIX" = "$PKG_FILE_BASENAME" ]; then
128    # Sanity check.
129    panic "Can't get package prefix from $PKG_FILE_BASENAME"
130  fi
131
132  # Where to do the work on the remote host.
133  DST_DIR=/tmp/android-emulator-build
134
135  if [ "$VERBOSE" -ge 3 ]; then
136    TARFLAGS="v"
137  fi
138  dump "Copying sources to Darwin host: $HOST"
139  run ssh $HOST "mkdir -p $DST_DIR && rm -rf $DST_DIR/$PKG_FILE_BASENAME"
140  cat "$PKG_FILE" | ssh $HOST "cd $DST_DIR && tar x${TARGFLAGS}f -"
141
142  dump "Rebuilding Darwin binaries remotely."
143  run ssh $HOST "bash -l -c \"cd $DST_DIR/$PKG_FILE_PREFIX/qemu && ./android-rebuild.sh $REBUILD_FLAGS\"" ||
144        panic "Can't rebuild binaries on Darwin, use --verbose to see why!"
145
146  dump "Retrieving Darwin binaries from: $HOST"
147  rm -rf objs/*
148  run scp $HOST:$DST_DIR/$PKG_FILE_PREFIX/qemu/objs/emulator* objs/
149  run scp -r $HOST:$DST_DIR/$PKG_FILE_PREFIX/qemu/objs/lib objs/lib
150  # TODO(digit): Retrieve PC BIOS files.
151  run ssh $HOST rm -rf $DST_DIR/$PKG_FILE_PREFIX
152}
153
154# Extract the git commit SHA1 of a given directory, and put its value
155# in a destination variable. If the target directory is not the root
156# of a git checkout, abort.
157# $1: Destination variable name.
158# $2: Git directory.
159# Example:   extract_commit_description GTEST_DESC "$GTEST_DIR"
160extract_git_commit_description () {
161    local VARNAME GIT_DIR SHA1
162    VARNAME=$1
163    GIT_DIR=$2
164    # Extract the commit description, then escape (') characters in it.
165    SHA1=$(cd $GIT_DIR && git log --oneline -1 .) || \
166        panic "Not a Git directory: $GIT_DIR"
167
168    SHA1=$(printf "%s" "$SHA1" | sed -e "s/'/\\'/g")
169    eval $VARNAME=\'$SHA1\'
170}
171
172# Defaults.
173DEFAULT_REVISION=$(date +%Y%m%d)
174DEFAULT_PKG_PREFIX=android-emulator
175DEFAULT_PKG_DIR=/tmp
176DEFAULT_DARWIN_SSH=$ANDROID_EMULATOR_DARWIN_SSH
177
178case $(uname -s) in
179    Linux)
180        DEFAULT_SYSTEMS="linux,windows"
181        HOST_SYSTEM=linux
182        ;;
183    Darwin)
184        DEFAULT_SYSTEMS="darwin"
185        HOST_SYSTEM=darwin
186        ;;
187    *)
188        panic "Unsupported system! This can only run on Linux and Darwin."
189esac
190
191# Command-line parsing.
192DO_HELP=
193OPT_COPY_PREBUILTS=
194OPT_DARWIN_SSH=
195OPT_PKG_DIR=
196OPT_PKG_PREFIX=
197OPT_REVISION=
198OPT_SOURCES=
199OPT_SYSTEM=
200
201for OPT; do
202    case $OPT in
203        --help|-?)
204            DO_HELP=true
205            ;;
206        --copy-prebuilts=*)
207            OPT_COPY_PREBUILTS=${OPT##--copy-prebuilts=}
208            ;;
209        --darwin-ssh=*)
210            OPT_DARWIN_SSH=${OPT##--darwin-ssh=}
211            ;;
212        --package-dir=*)
213            OPT_PKG_DIR=${OPT##--package-dir=}
214            ;;
215        --package-prefix=*)
216            OPT_PKG_PREFIX=${OPT##--package-prefix=}
217            ;;
218        --quiet)
219            VERBOSE=$(( $VERBOSE - 1 ))
220            ;;
221        --sources)
222            OPT_SOURCES=true
223            ;;
224        --revision=*)
225            OPT_REVISION=${OPT##--revision=}
226            ;;
227        --system=*)
228            OPT_SYSTEM=${OPT##--system=}
229            ;;
230        --verbose)
231            VERBOSE=$(( $VERBOSE + 1 ))
232            ;;
233        -*)
234            panic "Unsupported option '$OPT', see --help."
235            ;;
236        *)
237            panic "Unsupported parameter '$OPT', see --help."
238    esac
239done
240
241if [ "$DO_HELP" ]; then
242    cat <<EOF
243Usage: $PROGNAME [options]
244
245Rebuild the emulator binaries from source and package them into tarballs
246for easier distribution.
247
248New packages are placed by default at $DEFAULT_PKG_DIR
249Use --package-dir=<path> to use another output directory.
250
251Packages names are prefixed with $DEFAULT_PKG_PREFIX-<revision>, where
252the <revision> is the current ISO date by default. You can use
253--package-prefix=<prefix> and --revision=<revision> to change this.
254
255Binary packages will include the OpenGLES emulation libraries if they can
256be found in your current workspace, not otherwise.
257
258Use --sources option to also generate a source tarball.
259
260Use --darwin-ssh=<host> to build perform a remote build of the Darwin
261binaries on a remote host through ssh. Note that this forces --sources
262as well. You can also define ANDROID_EMULATOR_DARWIN_SSH in your
263environment to setup a default value for this option.
264
265Use --copy-prebuilts=<path> to specify the path of an AOSP workspace/checkout,
266and to copy 64-bit prebuilt binaries to <path>/prebuilts/android-emulator/
267for both Linux and Darwin platforms. This option requires the use of
268--darwin-ssh=<host> or ANDROID_EMULATOR_DARWIN_SSH to build the Darwin
269binaries.
270
271Valid options (defaults are inside brackets):
272    --help | -?           Print this message.
273    --package-dir=<path>  Change package output directory [$DEFAULT_PKG_DIR].
274    --revision=<name>     Change revision [$DEFAULT_REVISION].
275    --sources             Also create sources package.
276    --system=<list>       Specify host system list [$DEFAULT_SYSTEMS].
277    --copy-prebuilts=<path>  Copy 64-bit Linux and Darwin binaries to
278                             <path>/prebuilts/android-emulator/
279
280EOF
281    exit 0
282fi
283
284if [ "$OPT_PKG_PREFIX" ]; then
285    PKG_PREFIX=$OPT_PKG_PREFIX
286else
287    PKG_PREFIX=$DEFAULT_PKG_PREFIX
288    log "Auto-config: --package-prefix=$PKG_PREFIX"
289fi
290
291if [ "$OPT_REVISION" ]; then
292    PKG_REVISION=$OPT_REVISION
293else
294    PKG_REVISION=$DEFAULT_REVISION
295    log "Auto-config: --revision=$PKG_REVISION"
296fi
297
298if [ "$OPT_PKG_DIR" ]; then
299    PKG_DIR=$OPT_PKG_DIR
300    mkdir -p "$PKG_DIR" || panic "Can't create directory: $PKG_DIR"
301else
302    PKG_DIR=$DEFAULT_PKG_DIR
303    log "Auto-config: --package-dir=$PKG_DIR"
304fi
305
306if [ "$OPT_SYSTEM" ]; then
307    SYSTEMS=$(commas_to_spaces $OPT_SYSTEM)
308else
309    SYSTEMS=$(commas_to_spaces $DEFAULT_SYSTEMS)
310    log "Auto-config: --system=$SYSTEMS"
311fi
312
313if [ -z "$OPT_DARWIN_SSH" ]; then
314  DARWIN_SSH=$DEFAULT_DARWIN_SSH
315  if [ "$DARWIN_SSH" ]; then
316    log "Auto-config: --darwin-ssh=$DARWIN_SSH  (from environment)."
317  fi
318else
319  DARWIN_SSH=$OPT_DARWIN_SSH
320fi
321
322if [ "$DARWIN_SSH" ]; then
323    if [ -z "$OPT_SOURCES" ]; then
324        OPT_SOURCES=true
325        log "Auto-config: --sources  (remote Darwin build)."
326    fi
327    SYSTEMS="$SYSTEMS darwin"
328fi
329
330if [ "$OPT_COPY_PREBUILTS" ]; then
331    if [ -z "$DARWIN_SSH" ]; then
332        panic "The --copy-prebuilts=<dir> option requires --darwin-ssh=<host>."
333    fi
334    TARGET_AOSP=$OPT_COPY_PREBUILTS
335    if [ ! -f "$TARGET_AOSP/build/envsetup.sh" ]; then
336        panic "Not an AOSP checkout / workspace: $TARGET_AOSP"
337    fi
338    TARGET_PREBUILTS_DIR=$TARGET_AOSP/prebuilts/android-emulator
339    mkdir -p "$TARGET_PREBUILTS_DIR"
340fi
341
342case $VERBOSE in
343  0|1)
344    REBUILD_FLAGS=""
345    ;;
346  2)
347    REBUILD_FLAGS="--verbose"
348    ;;
349  *)
350    REBUILD_FLAGS="--verbose --verbose"
351    ;;
352esac
353
354# Remove duplicates.
355SYSTEMS=$(echo "$SYSTEMS" | tr ' ' '\n' | sort -u | tr '\n' ' ')
356log "Building for the following systems: $SYSTEMS"
357
358# Default build directory.
359TEMP_BUILD_DIR=/tmp/$USER-qemu-package-binaries
360
361# Ensure the build directory is removed when the script exits or is
362# interrupted.
363clean_exit () {
364    if [ -n "$TEMP_BUILD_DIR" -a -d "$TEMP_BUILD_DIR" ]; then
365        rm -rf "$TEMP_BUILD_DIR"
366    fi
367    exit $?
368}
369
370trap "clean_exit 0" EXIT
371trap "clean_exit \$?" QUIT HUP INT
372
373# Do some sanity checks to verify that the current source directory
374# doesn't have unchecked files and other bad things lingering.
375
376# Assume this script is under distrib/
377QEMU_DIR=$(cd "$PROGDIR"/.. && pwd -P)
378log "Found emulator directory: $QEMU_DIR"
379
380cd $QEMU_DIR
381if [ ! -d "$QEMU_DIR"/.git ]; then
382    panic "This directory must be a checkout of \$AOSP/platform/external/qemu!"
383fi
384UNCHECKED_FILES=$(git ls-files -o -x objs/ -x images/emulator_icon.o)
385if [ "$UNCHECKED_FILES" ]; then
386    echo "ERROR: There are unchecked files in the current directory!"
387    echo "Please remove them:"
388    echo "$UNCHECKED_FILES"
389    exit 1
390fi
391
392extract_git_commit_description QEMU_GIT_COMMIT "$QEMU_DIR"
393GTEST_DIR=$(dirname $QEMU_DIR)/gtest
394if [ ! -d "$GTEST_DIR" ]; then
395  panic "Cannot find GoogleTest source directory: $GTEST_DIR"
396fi
397log "Found GoogleTest directory: $GTEST_DIR"
398extract_git_commit_description GTEST_GIT_COMMIT "$GTEST_DIR"
399
400EMUGL_DIR=$QEMU_DIR/../../sdk/emulator/opengl
401if [ ! -d "$EMUGL_DIR" ]; then
402  panic "Cannot find GPU emulation source directory: $EMUGL_DIR"
403fi
404log "Found GPU emulation directory: $EMUGL_DIR"
405extract_git_commit_description EMUGL_GIT_COMMIT "$EMUGL_DIR"
406
407SOURCES_PKG_FILE=
408if [ "$OPT_SOURCES" ]; then
409    BUILD_DIR=$TEMP_BUILD_DIR/sources/$PKG_PREFIX-$PKG_REVISION
410    PKG_NAME="$PKG_REVISION-sources"
411    dump "[$PKG_NAME] Copying GoogleTest source files."
412    copy_directory_git_files "$GTEST_DIR" "$BUILD_DIR"/gtest
413
414    dump "[$PKG_NAME] Copying Emulator source files."
415    copy_directory_git_files "$QEMU_DIR" "$BUILD_DIR"/qemu
416
417    dump "[$PKG_NAME] Copying GPU emulation library sources."
418    copy_directory_git_files "$EMUGL_DIR" "$BUILD_DIR"/opengl
419
420    dump "[$PKG_NAME] Generating README file."
421    cat > "$BUILD_DIR"/README <<EOF
422This directory contains the sources of the Android emulator.
423Use './rebuild.sh' to rebuild the binaries from scratch.
424EOF
425
426    dump "[$PKG_NAME] Generating rebuild script."
427    cat > "$BUILD_DIR"/rebuild.sh <<EOF
428#!/bin/sh
429
430# Auto-generated script used to rebuild the Android emulator binaries
431# from sources. Note that this does not include the GLES emulation
432# libraries.
433
434cd \$(dirname "\$0") &&
435(cd qemu && ./android-rebuild.sh --ignore-audio) &&
436mkdir -p bin/ &&
437cp -rfp qemu/objs/emulator* bin/ &&
438echo "Emulator binaries are under \$(pwd -P)/bin/"
439echo "IMPORTANT: The GLES emulation libraries must be copied to:"
440echo "    \$(pwd -P)/bin/lib"
441EOF
442
443    chmod +x "$BUILD_DIR"/rebuild.sh
444
445    PKG_FILE=$PKG_DIR/$PKG_PREFIX-$PKG_REVISION-sources.tar.bz2
446    SOURCES_PKG_FILE=$PKG_FILE
447    dump "[$PKG_NAME] Creating tarball..."
448    (run cd "$BUILD_DIR"/.. && run tar cjf "$PKG_FILE" $PKG_PREFIX-$PKG_REVISION)
449fi
450
451for SYSTEM in $SYSTEMS; do
452    PKG_NAME="$PKG_REVISION-$SYSTEM"
453    dump "[$PKG_NAME] Rebuilding binaries from sources."
454    run cd $QEMU_DIR
455    case $SYSTEM in
456        $HOST_SYSTEM)
457            run ./android-rebuild.sh $REBUILD_FLAGS || panic "Use ./android-rebuild.sh to see why."
458            ;;
459        darwin)
460            if [ -z "$DARWIN_SSH" ]; then
461                # You can only rebuild Darwin binaries on non-Darwin systems
462                # by using --darwin-ssh=<host>.
463                panic "You can only rebuild Darwin binaries with --darwin-ssh"
464            fi
465            if [ -z "$SOURCES_PKG_FILE" ]; then
466                panic "You must use --sources to build Darwin binaries through ssh"
467            fi
468            build_darwin_binaries_on "$DARWIN_SSH" "$SOURCES_PKG_FILE"
469            ;;
470        windows)
471            if [ "$HOST_SYSTEM" != "linux" ]; then
472                panic "Windows binaries can only be rebuilt on Linux!"
473            fi
474            run ./android-rebuild.sh --mingw $REBUILD_FLAGS || panic "Use ./android-rebuild.sh --mingw to see why."
475            ;;
476        *)
477            panic "Can't rebuild $SYSTEM binaries on $HOST_SYSTEM for now!"
478            ;;
479    esac
480
481    dump "[$PKG_NAME] Copying emulator binaries."
482    TEMP_PKG_DIR=$TEMP_BUILD_DIR/$SYSTEM/$PKG_PREFIX-$PKG_REVISION
483    run mkdir -p "$TEMP_PKG_DIR"/tools
484
485    run cp -p objs/emulator* "$TEMP_PKG_DIR"/tools
486    if [ -d "objs/lib" ]; then
487        dump "[$PKG_NAME] Copying GLES emulation libraries."
488        run mkdir -p "$TEMP_PKG_DIR"/tools/lib
489        run2 cp -rp objs/lib/* "$TEMP_PKG_DIR"/tools/lib/
490    fi
491
492    dump "[$PKG_NAME] Creating README file."
493    cat > $TEMP_PKG_DIR/README <<EOF
494This directory contains Android emulator binaries. You can use them directly
495by defining ANDROID_SDK_ROOT in your environment, then call tools/emulator
496with the usual set of options.
497
498To install them directly into your SDK, copy them with:
499
500    cp -r tools/* \$ANDROID_SDK_ROOT/tools/
501EOF
502
503    dump "[$PKG_NAME] Copying license files."
504    mkdir -p "$TEMP_PKG_DIR"/licenses/
505    cp COPYING COPYING.LIB "$TEMP_PKG_DIR"/licenses/
506
507    dump "[$PKG_NAME] Creating tarball."
508    PKG_FILE=$PKG_DIR/$PKG_PREFIX-$PKG_REVISION-$SYSTEM.tar.bz2
509    (run cd "$TEMP_BUILD_DIR"/$SYSTEM && run tar cjf $PKG_FILE $PKG_PREFIX-$PKG_REVISION)
510done
511
512if [ "$OPT_COPY_PREBUILTS" ]; then
513    for SYSTEM in linux darwin; do
514        SRC_DIR="$TEMP_BUILD_DIR"/$SYSTEM/$PKG_PREFIX-$PKG_REVISION
515        DST_DIR=$TARGET_PREBUILTS_DIR/$SYSTEM-x86_64
516        dump "[$SYSTEM-x86_64] Copying emulator binaries into $DST_DIR"
517        run mkdir -p "$DST_DIR" || panic "Could not create directory: $DST_DIR"
518        case $SYSTEM in
519            linux) DLLEXT=.so;;
520            darwin) DLLEXT=.dylib;;
521            *) panic "Unsupported prebuilt system: $SYSTEM";;
522        esac
523        FILES="emulator"
524        for ARCH in arm x86 mips; do
525            FILES="$FILES emulator64-$ARCH"
526        done
527        for LIB in OpenglRender EGL_translator GLES_CM_translator GLES_V2_translator; do
528            FILES="$FILES lib/lib64$LIB$DLLEXT"
529        done
530        (run cd "$SRC_DIR/tools" && tar cf - $FILES) | (cd $DST_DIR && tar xf -) ||
531                panic "Could not copy binaries to $DST_DIR"
532    done
533    cat > $TARGET_PREBUILTS_DIR/README <<EOF
534This directory contains prebuilt emulator binaries that were generated by
535running the following command on a 64-bit Linux machine:
536
537  external/qemu/distrib/package-release.sh \\
538      --darwin-ssh=<host> \\
539      --copy-prebuilts=<path>
540
541Where <host> is the host name of a Darwin machine, and <path> is the root
542path of this AOSP repo workspace.
543
544Below is the list of specific commits for each input directory used:
545
546external/gtest       $GTEST_GIT_COMMIT
547external/qemu        $QEMU_GIT_COMMIT
548sdk/emulator/opengl  $EMUGL_GIT_COMMIT
549
550EOF
551fi
552
553dump "Done. See $PKG_DIR"
554ls -lh "$PKG_DIR"/$PKG_PREFIX-$PKG_REVISION*
555