#!/bin/sh # # Copyright (C) 2012 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This script is used to run a series of tests on a given standalone # toolchain. You need to define the following variables before calling it: # # PREFIX Full binary prefix to the toolchain binaries, # e.g. '/path/to/toolchain/bin/arm-linux-androideabi-' # This script will use ${PREFIX}gcc to invoke the compiler, # ${PREFIX}ar for the archiver, etc... # # CFLAGS Compiler flags for C programs. # CXXFLAGS Compiler flags for C++ programs. # LDFLAGS Linker flags (passed to ${PREFIX}gcc, not ${PREFIX}ld) # PROGNAME=$(basename "$0") PROGDIR=$(dirname "$0") NDK_ROOT=$(cd "$PROGDIR/../.." && pwd) NDK_BUILDTOOLS_PATH=$NDK_ROOT/build/tools . $NDK_ROOT/build/tools/prebuilt-common.sh panic () { echo "ERROR: $@" >&2; exit 1 } fail_panic () { if [ $? != 0 ]; then panic "$@"; fi } # Command-line processing # # Note: try to keep in alphabetical order, same for the --option cases below. # ABI= HELP= LIST_TESTS= NO_SYSROOT= SYSROOT= TEST_SUBDIRS= VERBOSE=1 # Parse options for opt; do optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'` case $opt in --abi=*) ABI=$optarg ;; --help|-h|-?) HELP=true ;; --list) LIST_TESTS=true ;; --no-sysroot) NO_SYSROOT=true ;; --prefix=*) PREFIX=$optarg ;; --quiet|-q) VERBOSE=$(( $VERBOSE - 1 )) ;; --sysroot=*) SYSROOT=$optarg ;; --verbose|-v) VERBOSE=$(( $VERBOSE + 1 )) ;; -*) panic "Unknown option '$opt'. See --help for list of valid ones." ;; *) TEST_SUBDIRS=$TEST_SUBDIRS" "$opt ;; esac done if [ "$HELP" ]; then echo "Usage: $PROGNAME [options] [testname+]" echo "" echo "Run a set of unit tests to check that a given Android NDK toolchain works" echo "as expected. Useful to catch regressions when generating new toolchain" echo "binaries." echo "" echo "You can pass the full path to the toolchain either with the --prefix" echo "option, or by defining PREFIX in your environment before calling this script." echo "For example:" echo "" echo " $PROGNAME --prefix=\$NDK/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/arm-linux-androideabi" echo "" echo "The prefix can also be the full-path to the \$TARGET-gcc or \$TARGET-g++ program " echo "" echo "The script will automatically use an NDK-provided sysroot, but you can specify an" echo "alternate one with the --sysroot=<path> option. You can also use --no-sysroot if" echo "the toolchain already provides its own sysroot (e.g. if it is a standalone toolchain" echo "generated with make-standalone-toolchain.sh)." echo "" echo "The target ABI is normally auto-detected from the toolchain, but you can specify an" echo "alternative one with the --abi=<name> option. This is only useful on ARM, where the" echo "default ABI is 'armeabi' targetting the ARMv5TE instruction set. If you want to check" echo "the generation of ARMv7-A machine code, use the following:" echo "" echo " --abi=armeabi-v7a" echo "" echo "When called without any arguments, the script will run all known toolchain tests." echo "You can restrict the list of tests by passing them on the command-line. Use --list" echo "to display the list of all tests that are relevant for your current ABI." echo "" echo "More information about each test can be displayed by using --verbose." echo "" echo "Valid options:" echo "" echo " --help|-h|-? Print this message." echo " --verbose|-v Increase verbosity." echo " --quiet|-q Decrease verbosity." echo " --list List all available tests for current ABI." echo " --prefix=<prefix> Specify full toolchain binary prefix." echo " --sysroot=<path> Specify alternate sysroot." echo " --no-sysroot Do not use a sysroot." echo " --abi=<name> Specify target ABI name." echo "" exit 0 fi TMPDIR=/tmp/ndk-$USER/tests/standalone mkdir -p "$TMPDIR" && rm -rf "$TMPDIR/*" BUILD_DIR=$TMPDIR/build mkdir -p "$BUILD_DIR" LOGFILE=$TMPDIR/log.txt echo -n "" > $LOGFILE if [ $VERBOSE -ge 3 ]; then run () { echo "# COMMAND: $@" "$@" } elif [ $VERBOSE -ge 2 ]; then run () { echo "# COMMAND: $@" >> $LOGFILE "$@" } else run () { echo "# COMMAND[$@]" >> $LOGFILE "$@" >> $LOGFILE 2>&1 } fi if [ $VERBOSE -ge 2 ]; then run_script () { $SHELL "$@" } else run_script () { $SHELL "$@" >> $LOGFILE 2>&1 } fi if [ $VERBOSE -ge 1 ]; then dump () { echo "$@" } else dump () { : # nothing } fi if [ "$HOST_OS" = "cygwin" -o "$HOST_OS" = "windows" ] ; then NULL="NUL" else NULL="/dev/null" fi # Probe a given sub-directory and see if it contains valid test files. # $1: sub-directory path # Return: 0 on success, 1 otherwise # # This can also sets the following global variables: # TEST_TYPE # SCRIPT # SOURCES # probe_test_subdir () { local DIR="$1" TEST_TYPE= SCRIPT= SOURCES= if [ -f "$DIR/run.sh" ]; then TEST_TYPE=script SCRIPT=run.sh elif [ -f "$DIR/run-$ABI.sh" ]; then TEST_TYPE=script SCRIPT=run-$ABI.sh elif [ -f "$DIR/main.c" ]; then TEST_TYPE=c_executable SOURCES=main.c elif [ -f "$DIR/main.cpp" ]; then TEST_TYPE=cxx_executable SOURCES=main.cpp else return 1 fi return 0 } # Handle --list option now, then exit if [ -n "$LIST_TESTS" ]; then echo "List of available toolchain tests:" if [ -z "$ABI" ]; then ABI=armeabi fi for TEST_SUBDIR in $(cd $PROGDIR && ls -d *); do SUBDIR=$PROGDIR/$TEST_SUBDIR if probe_test_subdir "$SUBDIR"; then echo " $TEST_SUBDIR" fi done exit 0 fi if [ -z "$PREFIX" ]; then panic "Please define PREFIX in your environment, or use --prefix=<prefix> option." fi CC= CXX= CC_TARGET= if [ "$PREFIX" = "${PREFIX%clang}" ]; then # Test GCC # Remove -gcc or -g++ from prefix if any PREFIX=${PREFIX%-gcc} PREFIX=${PREFIX%-g++} # Add a trailing dash to the prefix, if there isn't any PREFIX=${PREFIX%-}- GCC=${PREFIX}gcc if [ ! -f "$GCC" ]; then panic "Missing compiler, please fix your prefix definition: $GCC" fi GCC=$(which $GCC 2>$NULL) if [ -z "$GCC" -o ! -f "$GCC" ]; then panic "Bad compiler path: ${PREFIX}gcc" fi # Remove trailing .exe if any GCC=${GCC%${HOST_EXE}} GCCDIR=$(dirname "$GCC") GCCBASE=$(basename "$GCC") GCCDIR=$(cd "$GCCDIR" && pwd) GCC=$GCCDIR/$GCCBASE PREFIX=${GCC%%gcc} CC=${PREFIX}gcc CXX=${PREFIX}g++ CC_TARGET=$($GCC -v 2>&1 | tr ' ' '\n' | grep -e --target=) CC_TARGET=${CC_TARGET##--target=} else # Test Clang # Remove clang or clang++ from prefix if any PREFIX=${PREFIX%clang} PREFIX=${PREFIX%clang++} CLANG=${PREFIX}clang if [ ! -f "$CLANG" ]; then panic "Missing compiler, please fix your prefix definition: $CLANG" fi CLANGDIR=$(dirname "$CLANG") CLANGBASE=$(basename "$CLANG") CLANGDIR=$(cd "$CLANGDIR" && pwd) CLANG=$CLANGDIR/$CLANGBASE PREFIX=${CLANG%%clang} # Find *-ld in the same directory eventaully usable as ${PREFIX}-ld GNU_LD=$(cd $CLANGDIR && ls *-ld${HOST_EXE}) GNU_LD=$CLANGDIR/$GNU_LD if [ ! -f "$GNU_LD" ]; then panic "Missing linker in the same directory as clang/clang++: $CLANGDIR" fi PREFIX=${GNU_LD%ld${HOST_EXE}} CC=$CLANG CXX=${CLANG%clang}clang++ CC_TARGET=$($CLANG -v 2>&1 | grep Target:) CC_TARGET=${CC_TARGET##Target: } fi if [ -z "$ABI" ]; then # Auto-detect target CPU architecture dump "Auto-detected target configuration: $CC_TARGET" case $CC_TARGET in arm*-linux-androideabi) ABI=armeabi ARCH=arm ;; i686*-linux-android) ABI=x86 ARCH=x86 ;; mipsel*-linux-android) ABI=mips ARCH=mips ;; aarch64*-linux-android) ABI=arm64-v8a ARCH=arm64 ;; x86_64*-linux-android) ABI=x86_64 ARCH=x86_64 ;; mips64el*-linux-android) ABI=mips64 ARCH=mips64 ;; *) panic "Unknown target architecture '$CC_TARGET', please use --abi=<name> to manually specify ABI." esac dump "Auto-config: --abi=$ABI" fi COMMON_FLAGS= # Ensure ABI_<abi> is defined as a compiler macro when building test programs. # as a compiler macro when building all test programs. ABI_MACRO=ABI_$(echo "$ABI" | tr '-' '_') COMMON_FLAGS=$COMMON_FLAGS" -D$ABI_MACRO=1" if [ -n "$NO_SYSROOT" ]; then SYSROOT= elif [ -n "$SYSROOT" ]; then if [ ! -d "$SYSROOT" ]; then panic "Sysroot directory does not exist: $SYSROOT" fi # Sysroot must be absolute path SYSROOT=$(cd $SYSROOT && pwd) COMMON_FLAGS=$COMMON_FLAGS" --sysroot=$SYSROOT" else # Auto-detect sysroot PLATFORM="android-"$(get_default_api_level_for_arch $ARCH) SYSROOT=$NDK_ROOT/platforms/$PLATFORM/arch-$ARCH if [ ! -d "$SYSROOT" ]; then panic "Can't find sysroot file, use --sysroot to point to valid one: $SYSROOT" fi if [ ! -f "$SYSROOT/usr/lib/libc.so" ]; then panic "Incomplete sysroot, use --sysroot to point to valid one: $SYSROOT" fi if [ "$HOST_OS" = "cygwin" ]; then SYSROOT=`cygpath -m $SYSROOT` else if [ "$HOST_OS" = "windows" -a "$OSTYPE" = "msys" ]; then # use -W specific to MSys to get windows path SYSROOT=$(cd $SYSROOT ; pwd -W) fi fi dump "Auto-config: --sysroot=$SYSROOT" COMMON_FLAGS=$COMMON_FLAGS" --sysroot=$SYSROOT" fi if [ -z "$CXXFLAGS" ]; then CXXFLAGS=$CFLAGS fi # NOTE: We need to add -fno-exceptions, otherwise some toolchains compile # with exception support by default, and the test programs fail to # link due to an undefined reference to __gxx_personality_v0. # # This symbol is normally part of libsupc++ which is not available # if you don't have the GNU libstdc++ installed into your toolchain # directory. # # Affects the x86 and mips toolchains, but not the ARM one. # Not sure if we want exceptions enabled by default or not. # CXXFLAGS=$CXXFLAGS" -fno-exceptions" CFLAGS=$COMMON_FLAGS" "$CFLAGS CXXFLAGS=$COMMON_FLAGS" "$CXXFLAGS if [ -z "$TEST_SUBDIRS" ]; then TEST_SUBDIRS=$(cd $PROGDIR && ls -d *) fi COUNT=0 FAILURES=0 for TEST_SUBDIR in $TEST_SUBDIRS; do SUBDIR=$PROGDIR/$TEST_SUBDIR if ! probe_test_subdir "$SUBDIR"; then continue fi rm -rf "$BUILD_DIR"/* && cp -RL "$SUBDIR"/* "$BUILD_DIR/" fail_panic "Could not copy test files to $BUILD_DIR !?" dump_n "Running $TEST_SUBDIR test... " case $TEST_TYPE in script) ( export PREFIX CC CXX CFLAGS CXXFLAGS LDFLAGS VERBOSE ABI NULL run cd "$BUILD_DIR" && run_script $SCRIPT ) RET=$? ;; c_executable) ( run cd "$BUILD_DIR" && run $CC $LDFLAGS $CFLAGS -o $NULL $SOURCES ) RET=$? ;; cxx_executable) ( run cd "$BUILD_DIR" && run $CXX $LDFLAGS $CXXFLAGS -o $NULL $SOURCES ) RET=$? ;; esac if [ "$RET" != 0 ]; then dump "KO" FAILURES=$(( $FAILURES + 1 )) else dump "ok" fi COUNT=$(( $COUNT + 1 )) done if [ "$FAILURES" -eq 0 ]; then dump "$COUNT/$COUNT tests passed. Success." exit 0 else dump "$FAILURES tests failed out of $COUNT." exit 1 fi