Home | History | Annotate | Download | only in standalone
      1 #!/bin/sh
      2 #
      3 # Copyright (C) 2012 The Android Open Source Project
      4 #
      5 # Licensed under the Apache License, Version 2.0 (the "License");
      6 # you may not use this file except in compliance with the License.
      7 # You may obtain a copy of the License at
      8 #
      9 #      http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 # Unless required by applicable law or agreed to in writing, software
     12 # distributed under the License is distributed on an "AS IS" BASIS,
     13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 # See the License for the specific language governing permissions and
     15 # limitations under the License.
     16 #
     17 
     18 # This script is used to run a series of tests on a given standalone
     19 # toolchain. You need to define the following variables before calling it:
     20 #
     21 #   PREFIX    Full binary prefix to the toolchain binaries,
     22 #             e.g. '/path/to/toolchain/bin/arm-linux-androideabi-'
     23 #             This script will use ${PREFIX}gcc to invoke the compiler,
     24 #             ${PREFIX}ar for the archiver, etc...
     25 #
     26 #   CFLAGS    Compiler flags for C programs.
     27 #   CXXFLAGS  Compiler flags for C++ programs.
     28 #   LDFLAGS   Linker flags (passed to ${PREFIX}gcc, not ${PREFIX}ld)
     29 #
     30 
     31 PROGNAME=$(basename "$0")
     32 PROGDIR=$(dirname "$0")
     33 NDK_ROOT=$(cd "$PROGDIR/../.." && pwd)
     34 NDK_BUILDTOOLS_PATH=$NDK_ROOT/build/tools
     35 . $NDK_ROOT/build/tools/prebuilt-common.sh
     36 
     37 panic () {
     38     echo "ERROR: $@" >&2; exit 1
     39 }
     40 
     41 fail_panic () {
     42   if [ $? != 0 ]; then panic "$@"; fi
     43 }
     44 
     45 
     46 # Command-line processing
     47 #
     48 # Note: try to keep in alphabetical order, same for the --option cases below.
     49 #
     50 ABI=
     51 HELP=
     52 LIST_TESTS=
     53 NO_SYSROOT=
     54 SYSROOT=
     55 TEST_SUBDIRS=
     56 VERBOSE=1
     57 
     58 # Parse options
     59 for opt; do
     60     optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'`
     61     case $opt in
     62         --abi=*)
     63             ABI=$optarg
     64             ;;
     65         --help|-h|-?)
     66             HELP=true
     67             ;;
     68         --list)
     69             LIST_TESTS=true
     70             ;;
     71         --no-sysroot)
     72             NO_SYSROOT=true
     73             ;;
     74         --prefix=*)
     75             PREFIX=$optarg
     76             ;;
     77         --quiet|-q)
     78             VERBOSE=$(( $VERBOSE - 1 ))
     79             ;;
     80         --sysroot=*)
     81             SYSROOT=$optarg
     82             ;;
     83         --verbose|-v)
     84             VERBOSE=$(( $VERBOSE + 1 ))
     85             ;;
     86         -*)
     87             panic "Unknown option '$opt'. See --help for list of valid ones."
     88             ;;
     89         *)
     90             TEST_SUBDIRS=$TEST_SUBDIRS" "$opt
     91             ;;
     92     esac
     93 done
     94 
     95 if [ "$HELP" ]; then
     96     echo "Usage: $PROGNAME [options] [testname+]"
     97     echo ""
     98     echo "Run a set of unit tests to check that a given Android NDK toolchain works"
     99     echo "as expected. Useful to catch regressions when generating new toolchain"
    100     echo "binaries."
    101     echo ""
    102     echo "You can pass the full path to the toolchain either with the --prefix"
    103     echo "option, or by defining PREFIX in your environment before calling this script."
    104     echo "For example:"
    105     echo ""
    106     echo "  $PROGNAME --prefix=\$NDK/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/arm-linux-androideabi"
    107     echo ""
    108     echo "The prefix can also be the full-path to the \$TARGET-gcc or \$TARGET-g++ program "
    109     echo ""
    110     echo "The script will automatically use an NDK-provided sysroot, but you can specify an"
    111     echo "alternate one with the --sysroot=<path> option. You can also use --no-sysroot if"
    112     echo "the toolchain already provides its own sysroot (e.g. if it is a standalone toolchain"
    113     echo "generated with make-standalone-toolchain.sh)."
    114     echo ""
    115     echo "The target ABI is normally auto-detected from the toolchain, but you can specify an"
    116     echo "alternative one with the --abi=<name> option. This is only useful on ARM, where the"
    117     echo "default ABI is 'armeabi' targetting the ARMv5TE instruction set. If you want to check"
    118     echo "the generation of ARMv7-A machine code, use the following:"
    119     echo ""
    120     echo "  --abi=armeabi-v7a"
    121     echo ""
    122     echo "When called without any arguments, the script will run all known toolchain tests."
    123     echo "You can restrict the list of tests by passing them on the command-line. Use --list"
    124     echo "to display the list of all tests that are relevant for your current ABI."
    125     echo ""
    126     echo "More information about each test can be displayed by using --verbose."
    127     echo ""
    128     echo "Valid options:"
    129     echo ""
    130     echo "    --help|-h|-?        Print this message."
    131     echo "    --verbose|-v        Increase verbosity."
    132     echo "    --quiet|-q          Decrease verbosity."
    133     echo "    --list              List all available tests for current ABI."
    134     echo "    --prefix=<prefix>   Specify full toolchain binary prefix."
    135     echo "    --sysroot=<path>    Specify alternate sysroot."
    136     echo "    --no-sysroot        Do not use a sysroot."
    137     echo "    --abi=<name>        Specify target ABI name."
    138     echo ""
    139     exit 0
    140 fi
    141 
    142 TMPDIR=/tmp/ndk-$USER/tests/standalone
    143 mkdir -p "$TMPDIR" && rm -rf "$TMPDIR/*"
    144 
    145 BUILD_DIR=$TMPDIR/build
    146 mkdir -p "$BUILD_DIR"
    147 
    148 LOGFILE=$TMPDIR/log.txt
    149 echo -n "" > $LOGFILE
    150 
    151 if [ $VERBOSE -ge 3 ]; then
    152     run () {
    153         echo "# COMMAND: $@"
    154         "$@"
    155     }
    156 elif [ $VERBOSE -ge 2 ]; then
    157     run () {
    158         echo "# COMMAND: $@" >> $LOGFILE
    159         "$@"
    160     }
    161 else
    162     run () {
    163         echo "# COMMAND[$@]" >> $LOGFILE
    164         "$@" >> $LOGFILE 2>&1
    165     }
    166 fi
    167 
    168 if [ $VERBOSE -ge 2 ]; then
    169     run_script () {
    170         $SHELL "$@"
    171     }
    172 else
    173     run_script () {
    174         $SHELL "$@" >> $LOGFILE 2>&1
    175     }
    176 fi
    177 
    178 if [ $VERBOSE -ge 1 ]; then
    179     dump () {
    180         echo "$@"
    181     }
    182 else
    183     dump () {
    184         :  # nothing
    185     }
    186 fi
    187 
    188 if [ "$HOST_OS" = "cygwin" -o "$HOST_OS" = "windows" ] ; then
    189     NULL="NUL"
    190 else
    191     NULL="/dev/null"
    192 fi
    193 
    194 # Probe a given sub-directory and see if it contains valid test files.
    195 # $1: sub-directory path
    196 # Return: 0 on success, 1 otherwise
    197 #
    198 # This can also sets the following global variables:
    199 #   TEST_TYPE
    200 #   SCRIPT
    201 #   SOURCES
    202 #
    203 probe_test_subdir ()
    204 {
    205     local DIR="$1"
    206 
    207     TEST_TYPE=
    208     SCRIPT=
    209     SOURCES=
    210 
    211     if [ -f "$DIR/run.sh" ]; then
    212         TEST_TYPE=script
    213         SCRIPT=run.sh
    214 
    215     elif [ -f "$DIR/run-$ABI.sh" ]; then
    216         TEST_TYPE=script
    217         SCRIPT=run-$ABI.sh
    218 
    219     elif [ -f "$DIR/main.c" ]; then
    220         TEST_TYPE=c_executable
    221         SOURCES=main.c
    222 
    223     elif [ -f "$DIR/main.cpp" ]; then
    224         TEST_TYPE=cxx_executable
    225         SOURCES=main.cpp
    226 
    227     else
    228         return 1
    229     fi
    230 
    231     return 0
    232 }
    233 
    234 
    235 # Handle --list option now, then exit
    236 if [ -n "$LIST_TESTS" ]; then
    237     echo "List of available toolchain tests:"
    238     if [ -z "$ABI" ]; then
    239         ABI=armeabi
    240     fi
    241     for TEST_SUBDIR in $(cd $PROGDIR && ls -d *); do
    242         SUBDIR=$PROGDIR/$TEST_SUBDIR
    243         if probe_test_subdir "$SUBDIR"; then
    244             echo "  $TEST_SUBDIR"
    245         fi
    246     done
    247     exit 0
    248 fi
    249 
    250 if [ -z "$PREFIX" ]; then
    251     panic "Please define PREFIX in your environment, or use --prefix=<prefix> option."
    252 fi
    253 
    254 CC=
    255 CXX=
    256 CC_TARGET=
    257 if [ "$PREFIX" = "${PREFIX%clang}" ]; then
    258     # Test GCC
    259     # Remove -gcc or -g++ from prefix if any
    260     PREFIX=${PREFIX%-gcc}
    261     PREFIX=${PREFIX%-g++}
    262 
    263     # Add a trailing dash to the prefix, if there isn't any
    264     PREFIX=${PREFIX%-}-
    265 
    266     GCC=${PREFIX}gcc
    267     if [ ! -f "$GCC" ]; then
    268         panic "Missing compiler, please fix your prefix definition: $GCC"
    269     fi
    270 
    271     GCC=$(which $GCC 2>$NULL)
    272     if [ -z "$GCC" -o ! -f "$GCC" ]; then
    273         panic "Bad compiler path: ${PREFIX}gcc"
    274     fi
    275 
    276     # Remove trailing .exe if any
    277     GCC=${GCC%${HOST_EXE}}
    278 
    279     GCCDIR=$(dirname "$GCC")
    280     GCCBASE=$(basename "$GCC")
    281 
    282     GCCDIR=$(cd "$GCCDIR" && pwd)
    283     GCC=$GCCDIR/$GCCBASE
    284 
    285     PREFIX=${GCC%%gcc}
    286 
    287     CC=${PREFIX}gcc
    288     CXX=${PREFIX}g++
    289     CC_TARGET=$($GCC -v 2>&1 | tr ' ' '\n' | grep -e --target=)
    290     CC_TARGET=${CC_TARGET##--target=}
    291 else
    292     # Test Clang
    293     # Remove clang or clang++ from prefix if any
    294     PREFIX=${PREFIX%clang}
    295     PREFIX=${PREFIX%clang++}
    296 
    297     CLANG=${PREFIX}clang
    298     if [ ! -f "$CLANG" ]; then
    299         panic "Missing compiler, please fix your prefix definition: $CLANG"
    300     fi
    301 
    302     CLANGDIR=$(dirname "$CLANG")
    303     CLANGBASE=$(basename "$CLANG")
    304 
    305     CLANGDIR=$(cd "$CLANGDIR" && pwd)
    306     CLANG=$CLANGDIR/$CLANGBASE
    307 
    308     PREFIX=${CLANG%%clang}
    309 
    310     # Find *-ld in the same directory eventaully usable as ${PREFIX}-ld
    311     GNU_LD=$(cd $CLANGDIR && ls *-ld${HOST_EXE})
    312     GNU_LD=$CLANGDIR/$GNU_LD
    313     if [ ! -f "$GNU_LD" ]; then
    314         panic "Missing linker in the same directory as clang/clang++: $CLANGDIR"
    315     fi
    316 
    317     PREFIX=${GNU_LD%ld${HOST_EXE}}
    318 
    319     CC=$CLANG
    320     CXX=${CLANG%clang}clang++
    321     CC_TARGET=$($CLANG -v 2>&1 | grep Target:)
    322     CC_TARGET=${CC_TARGET##Target: }
    323 fi
    324 
    325 if [ -z "$ABI" ]; then
    326     # Auto-detect target CPU architecture
    327     dump "Auto-detected target configuration: $CC_TARGET"
    328     case $CC_TARGET in
    329         arm*-linux-androideabi)
    330             ABI=armeabi
    331             ARCH=arm
    332             ;;
    333         i686*-linux-android)
    334             ABI=x86
    335             ARCH=x86
    336             ;;
    337         mipsel*-linux-android)
    338             ABI=mips
    339             ARCH=mips
    340             ;;
    341         aarch64*-linux-android)
    342             ABI=arm64-v8a
    343             ARCH=arm64
    344             ;;
    345         x86_64*-linux-android)
    346             ABI=x86_64
    347             ARCH=x86_64
    348             ;;
    349         mips64el*-linux-android)
    350             ABI=mips64
    351             ARCH=mips64
    352             ;;
    353         *)
    354             panic "Unknown target architecture '$CC_TARGET', please use --abi=<name> to manually specify ABI."
    355     esac
    356     dump "Auto-config: --abi=$ABI"
    357 fi
    358 
    359 COMMON_FLAGS=
    360 
    361 # Ensure ABI_<abi> is defined as a compiler macro when building test programs.
    362 # as a compiler macro when building all test programs.
    363 ABI_MACRO=ABI_$(echo "$ABI" | tr '-' '_')
    364 COMMON_FLAGS=$COMMON_FLAGS" -D$ABI_MACRO=1"
    365 
    366 if [ -n "$NO_SYSROOT" ]; then
    367     SYSROOT=
    368 elif [ -n "$SYSROOT" ]; then
    369     if [ ! -d "$SYSROOT" ]; then
    370         panic "Sysroot directory does not exist: $SYSROOT"
    371     fi
    372     # Sysroot must be absolute path
    373     SYSROOT=$(cd $SYSROOT && pwd)
    374     COMMON_FLAGS=$COMMON_FLAGS" --sysroot=$SYSROOT"
    375 else
    376     # Auto-detect sysroot
    377     PLATFORM="android-"$(get_default_api_level_for_arch $ARCH)
    378     SYSROOT=$NDK_ROOT/platforms/$PLATFORM/arch-$ARCH
    379     if [ ! -d "$SYSROOT" ]; then
    380         panic "Can't find sysroot file, use --sysroot to point to valid one: $SYSROOT"
    381     fi
    382     if [ ! -f "$SYSROOT/usr/lib/libc.so" ]; then
    383         panic "Incomplete sysroot, use --sysroot to point to valid one: $SYSROOT"
    384     fi
    385     if [ "$HOST_OS" = "cygwin" ]; then
    386         SYSROOT=`cygpath -m $SYSROOT`
    387     else
    388         if [ "$HOST_OS" = "windows" -a "$OSTYPE" = "msys" ]; then
    389             # use -W specific to MSys to get windows path
    390             SYSROOT=$(cd $SYSROOT ; pwd -W)
    391         fi
    392     fi
    393     dump "Auto-config: --sysroot=$SYSROOT"
    394     COMMON_FLAGS=$COMMON_FLAGS" --sysroot=$SYSROOT"
    395 fi
    396 
    397 if [ -z "$CXXFLAGS" ]; then
    398     CXXFLAGS=$CFLAGS
    399 fi
    400 
    401 # NOTE: We need to add -fno-exceptions, otherwise some toolchains compile
    402 #        with exception support by default, and the test programs fail to
    403 #        link due to an undefined reference to __gxx_personality_v0.
    404 #
    405 #        This symbol is normally part of libsupc++ which is not available
    406 #        if you don't have the GNU libstdc++ installed into your toolchain
    407 #        directory.
    408 #
    409 #        Affects the x86 and mips toolchains, but not the ARM one.
    410 #        Not sure if we want exceptions enabled by default or not.
    411 #
    412 CXXFLAGS=$CXXFLAGS" -fno-exceptions"
    413 
    414 CFLAGS=$COMMON_FLAGS" "$CFLAGS
    415 CXXFLAGS=$COMMON_FLAGS" "$CXXFLAGS
    416 
    417 if [ -z "$TEST_SUBDIRS" ]; then
    418     TEST_SUBDIRS=$(cd $PROGDIR && ls -d *)
    419 fi
    420 
    421 COUNT=0
    422 FAILURES=0
    423 for TEST_SUBDIR in $TEST_SUBDIRS; do
    424     SUBDIR=$PROGDIR/$TEST_SUBDIR
    425 
    426     if ! probe_test_subdir "$SUBDIR"; then
    427         continue
    428     fi
    429 
    430     rm -rf "$BUILD_DIR"/* &&
    431     cp -RL "$SUBDIR"/* "$BUILD_DIR/"
    432     fail_panic "Could not copy test files to $BUILD_DIR !?"
    433 
    434     dump_n "Running $TEST_SUBDIR test... "
    435 
    436     case $TEST_TYPE in
    437         script)
    438             (
    439                 export PREFIX CC CXX CFLAGS CXXFLAGS LDFLAGS VERBOSE ABI NULL
    440                 run cd "$BUILD_DIR" && run_script $SCRIPT
    441             )
    442             RET=$?
    443             ;;
    444 
    445         c_executable)
    446             (
    447                 run cd "$BUILD_DIR" && run $CC $LDFLAGS $CFLAGS -o $NULL $SOURCES
    448             )
    449             RET=$?
    450             ;;
    451 
    452         cxx_executable)
    453             (
    454                 run cd "$BUILD_DIR" && run $CXX $LDFLAGS $CXXFLAGS -o $NULL $SOURCES
    455             )
    456             RET=$?
    457             ;;
    458     esac
    459 
    460     if [ "$RET" != 0 ]; then
    461         dump "KO"
    462         FAILURES=$(( $FAILURES + 1 ))
    463     else
    464         dump "ok"
    465     fi
    466     COUNT=$(( $COUNT + 1 ))
    467 done
    468 
    469 if [ "$FAILURES" -eq 0 ]; then
    470     dump "$COUNT/$COUNT tests passed. Success."
    471     exit 0
    472 else
    473     dump "$FAILURES tests failed out of $COUNT."
    474     exit 1
    475 fi
    476