#!/bin/bash # //===--------------------------- testit ---------------------------------===// # // # // The LLVM Compiler Infrastructure # // # // This file is distributed under the University of Illinois Open Source # // License. See LICENSE.TXT for details. # // # //===--------------------------------------------------------------------===// currentpath=`pwd` origpath=$currentpath currentdir=`basename $currentpath` while [ $currentdir != "test" ]; do if [ $currentdir = "/" ] then echo "current directory must be in or under \"test\"." exit 1 fi cd .. currentpath=`pwd` currentdir=`basename $currentpath` done cd .. LIBCXX_ROOT=`pwd`/../../llvm-libc++/libcxx cd $origpath VERBOSE=1 run () { if [ "$VERBOSE" -gt 1 ]; then echo "COMMAND: $@" fi case $VERBOSE in 0|1) # Hide command output and errors. "$@" >/dev/null 2>&1 ;; 2) # Only hide command output "$@" >/dev/null ;; *) # Show command output and errors. "$@" ;; esac } run2 () { if [ "$VERBOSE" -gt 2 ]; then echo "COMMAND: $@" fi case $VERBOSE in 0|1) # Hide command output and errors. "$@" >/dev/null 2>&1 ;; 2) # Only hide command output "$@" >/dev/null ;; *) # Show command output and errors. "$@" ;; esac } # The list of valid target abis supported by this script. VALID_ABIS="armeabi armeabi-v7a armeabi-v7a-hard x86 mips" DO_HELP= DO_STATIC= TARGET_ABI= TARGET_ARCH= if [ -n "$SHARD_TEST_TOOLCHAIN" ]; then TARGET_PATH=/data/local/tmp/libcxx-${SHARD_TEST_TOOLCHAIN}-$(dirname $PWD | sum | cut -d' ' -f1)-$(basename $PWD) else TARGET_PATH=/data/local/tmp/libcxx fi CXX= WITH_COMPILER_RT= OPTIMIZE= for OPT; do case $OPT in --help|-?) DO_HELP=true ;; --abi=*) TARGET_ABI=${OPT##--abi=} ;; --static) DO_STATIC=true ;; --shared) DO_STATIC= ;; --cxx=*) CXX=${OPT##--cxx=} ;; --verbose) VERBOSE=$(( $VERBOSE + 1 )) ;; --with-compiler-rt) WITH_COMPILER_RT=yes ;; -O*) OPTIMIZE=$OPT ;; -*) echo "Unknown option: $OPT. See --help." exit 1 ;; *) echo "This script doesn't take parameters. See --help." exit 1 ;; esac done if [ "$DO_HELP" ]; then echo \ "Usage: $(basename $0) [options] This script is used to run the libc++ test suite for Android. You will need the following things: - The prebuild libc++ libraries in your NDK install. - A prebuilt Android toolchain in your path. - The 'adb' tool in your path. - An Android device connected to ADB. The toolchain and device must match your target ABI. For example, if you use --abi=armeabi-v7a, your device must run ARMv7-A Android binaries, and arm-linux-androideabi-g++ will be used to compile all tests, unless you use --cxx=<command> to override it. Valid options: --help|-? Display this message. --abi=<name> Specify target ABI. Use --abi=list for list. --static Link against static libc++ library. --cxx=<program> Override C++ compiler/linker. --verbose Increase verbosity. " exit 0 fi # Check target ABI. if [ "$TARGET_ABI" = "list" ]; then echo "List of valid target ABIs:" for ABI in $VALID_ABIS; do printf " %s" $ABI done printf "\n" exit 0 fi if [ -z "$TARGET_ABI" ]; then echo "ERROR: Please specify a target ABI (--abi=<name>)." exit 1 fi FOUND_ABI= for ABI in $VALID_ABIS; do if [ "$ABI" = "$TARGET_ABI" ]; then FOUND_ABI=true break fi done if [ -z "$FOUND_ABI" ]; then echo "ERROR: Invalid abi '$TARGET_ABI'. Must be one of: $VALID_ABIS" exit 1 fi LIBCXX_LIBS=$(cd $LIBCXX_ROOT/.. && pwd)/libs/$TARGET_ABI for LIB in libc++_static.a libc++_shared.so; do if [ ! -f "$LIBCXX_LIBS/$LIB" ]; then echo "ERROR: Missing prebuilt library: $LIBCXX_LIBS/$LIB" echo "Please run: build/tools/build-cxx-stl.sh --stl=libc++" exit 1 fi done LIBCOMPILER_RT_LIBS=$(cd "$LIBCXX_ROOT"/../../../android/compiler-rt && pwd)/libs/$TARGET_ABI if [ "$WITH_COMPILER_RT" = "yes" ]; then for LIB in libcompiler_rt_static.a libcompiler_rt_shared.so; do if [ ! -f "$LIBCOMPILER_RT_LIBS/$LIB" ]; then echo "ERROR: Missing prebuilt library: $LIBCOMPILER_RT_LIBS/$LIB" echo "Please run: build/tools/build-compiler-rt.sh --ndk-dir=$NDK \ --src-dir=/tmp/ndk-$USER/src/llvm-3.4/compiler-rt --llvm-version=3.4" exit 1 fi done fi # Check or detect C++ toolchain. TOOLCHAIN_CFLAGS= TOOLCHAIN_LDFLAGS= THUMB_MODE="-mthumb" LIBM="-lm" if [ -z "$TOOLCHAIN_PREFIX" ]; then # Compute case $TARGET_ABI in armeabi) TOOLCHAIN_PREFIX=arm-linux-androideabi TOOLCHAIN_CFLAGS="$THUMB_MODE" TOOLCHAIN_LDFLAGS="$THUMB_MODE" ;; armeabi-v7a) TOOLCHAIN_PREFIX=arm-linux-androideabi TOOLCHAIN_CFLAGS="-march=armv7-a $THUMB_MODE -mfpu=vfpv3-d16" TOOLCHAIN_LDFLAGS="-march=armv7-a $THUMB_MODE -Wl,--fix-cortex-a8" ;; armeabi-v7a-hard) TOOLCHAIN_PREFIX=arm-linux-androideabi TOOLCHAIN_CFLAGS="-march=armv7-a $THUMB_MODE -mfpu=vfpv3-d16 -mhard-float -D_NDK_MATH_NO_SOFTFP=1" TOOLCHAIN_LDFLAGS="-march=armv7-a $THUMB_MODE -Wl,--fix-cortex-a8 -Wl,--no-warn-mismatch" LIBM="-lm_hard" ;; x86) TOOLCHAIN_PREFIX=i686-linux-android ;; mips) TOOLCHAIN_PREFIX=mipsel-linux-android ;; *) echo "ERROR: Unknown ABI '$ABI'" exit 1 ;; esac if [ -z "$CXX" ]; then CXX=$TOOLCHAIN_PREFIX-g++ fi fi REAL_CXX=$(which "$CXX" 2>/dev/null) if [ -z "$REAL_CXX" ]; then echo "ERROR: Missing C++ compiler: $CXX" exit 1 fi CC=$CXX function version_ge { input_string=$1 compare_string=$2 input_major=$(echo $input_string | cut -d\. -f 1) input_minor=$(echo $input_string | cut -d\. -f 2) compare_major=$(echo $compare_string | cut -d\. -f 1) compare_minor=$(echo $compare_string | cut -d\. -f 2) true=0 false=1 if [ "$input_major" -gt "$compare_major" ]; then return $true; fi if [ "$input_major" -lt "$compare_major" ]; then return $false; fi if [ "$input_minor" -ge "$compare_minor" ]; then return $true; fi return $false } if [ -z "$OPTIONS" ] then OPTIONS="-std=c++11 -g -funwind-tables $OPTIMIZE" # Note that some tests use assert() to check condition but -O2 defines assert() to nothing, # unless we specify -UNDEBUG to bring assert() back. # But currently adding -UNDEBUG crashes both clang3.4 and clang3.3 for test # like libcxx/test/atomics/atomics.types.generic/address.pass.cpp. Define -UNDEBUG # only when we are not using clang. ToDo if [ "$CXX" = "${CXX%%*clang++*}" ] ; then OPTIONS="$OPTIONS -UNDEBUG" fi if [ -n "$LLVM_VERSION" ]; then if version_ge "$LLVM_VERSION" "3.4"; then OPTIONS="${OPTIONS} -mllvm -arm-enable-ehabi-descriptors -mllvm -arm-enable-ehabi" fi fi fi OPTIONS="$OPTIONS $TOOLCHAIN_CFLAGS $TOOLCHAIN_LDFLAGS" OPTIONS="$OPTIONS -I$LIBCXX_ROOT/test/support" # llvm-libc++/libcxx/test/lit.cfg line #278 defineds the following for testing only on Linux OPTIONS="$OPTIONS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS" if [ -z "$ADB" ] then ADB=adb fi # Run a shell command through ADB, return its status. # Variable ERR contains output if $RET is non-zero adb_shell () { # We need a temporary file to store the output of our command local CMD_OUT RET ERR= CMD_OUT=$(mktemp /tmp/testit_android-cmdout-XXXXXX) # Run the command, while storing the standard output to CMD_OUT # and appending the exit code as the last line. if [ "$VERBOSE" -gt 2 ]; then echo "COMMAND: $ADB shell $@" fi $ADB shell "$@ ; echo \$?" | sed -e 's![[:cntrl:]]!!g' > $CMD_OUT 2>&1 # Get last line in log, which contains the exit code from the command RET=$(sed -e '$!d' $CMD_OUT) # Get output, which corresponds to everything except the last line OUT=$(sed -e '$d' $CMD_OUT) if [ "$RET" != "0" ]; then ERR=$OUT fi if [ "$VERBOSE" -gt 2 ]; then printf "%s" "$OUT" fi rm -f $CMD_OUT return $RET } # Push a given file through ADB. # $1: File path adb_push () { local FILE=$1 local FILE_BASENAME=$(basename "$FILE") run2 $ADB push $FILE $TARGET_PATH/$FILE_BASENAME 2>/dev/null } # Run a given executable through ADB. # $1: Executable path adb_run () { local EXECUTABLE=$1 local EXECUTABLE_BASENAME=$(basename "$EXECUTABLE") run2 $ADB push $EXECUTABLE $TARGET_PATH/$EXECUTABLE_BASENAME 2>/dev/null if [ "$?" != 0 ]; then return 1; fi # Retry up to 10 times if fail is due to "Text file busy" for i in 1 2 3 4 5 6 7 8 9 10; do adb_shell "cd $TARGET_PATH; LD_LIBRARY_PATH=$TARGET_PATH LIBUNWIND_PRINT_UNWINDING=1 ./$EXECUTABLE_BASENAME" if [ "$?" = "0" ]; then return 0 fi if ! $(echo $ERR | grep -iq "Text file busy"); then if [ "$i" != "1" ]; then # Dump error message to help diagnostics echo "ERR=$ERR" fi break; fi echo "Text file busy. Re-try $i" sleep 1 run2 $ADB push $EXECUTABLE $TARGET_PATH/$EXECUTABLE_BASENAME 2>/dev/null sleep 2 # try again done return 1 } adb_shell "rm -rf $TARGET_PATH" adb_shell "mkdir -p $TARGET_PATH" if [ "$DO_STATIC" ]; then # Statically link to ensure the executable can be run easily through ADB if [ "$WITH_COMPILER_RT" = "yes" ]; then LIBS="-nodefaultlibs -lc++_static -latomic -ldl $LIBM -lc -lcompiler_rt_static" else LIBS="-nodefaultlibs -latomic -ldl $LIBM -lc" fi else run2 $ADB push $LIBCXX_LIBS/libc++_shared.so $TARGET_PATH 2>/dev/null if [ $? != 0 ]; then echo "ERROR: Can't push shared libc++ to target device!" exit 1 fi if [ "$WITH_COMPILER_RT" = "yes" ]; then run2 $ADB push $LIBCOMPILER_RT_LIBS/libcompiler_rt_shared.so $TARGET_PATH 2>/dev/null if [ $? != 0 ]; then echo "ERROR: Can't push shared libcompiler_rt to target device!" exit 1 fi LIBS="-nodefaultlibs -lc++_shared -latomic -ldl $LIBM -lc -lcompiler_rt_shared" else LIBS="-nodefaultlibs -lc++_shared -latomic -ldl $LIBM -lc" fi fi case $TRIPLE in *-*-mingw* | *-*-cygwin* | *-*-win*) TEST_EXE=test.exe ;; *) TEST_EXE=a.out ;; esac TEST_EXE=/tmp/testit_android-$USER-$$-$TEST_EXE FAIL=0 PASS=0 UNIMPLEMENTED=0 IMPLEMENTED_FAIL=0 IMPLEMENTED_PASS=0 # Run tests in current directory, recursively # # Note that file path containing EQ are symlink to the existing tests whose path contain '=', # to workaround an issue in ndk-build which doesn't handle LOCAL_SRC_FILES with '='. # See tests/device/test-libc++-static-full/jni/Android.mk We need to filter out path containing # EQ such that we don't run same tests twice # # An alternative is to do "find . -type f", but this doesn't work in NDK windows package # where zip turns symlink into physical file it points to. # # We also sort the test to make the test report comparable to previous test # afunc() { fail=0 pass=0 if (ls ${TEST_PREFIX}*fail.cpp > /dev/null 2>&1) then for FILE in $(ls ${TEST_PREFIX}*fail.cpp | tr ' ' '\n' | grep -v EQ | sort); do if run $CC $OPTIONS $HEADER_INCLUDE $SOURCE_LIB $FILE $LIBS -o $TEST_EXE > /dev/null 2>&1 then rm $TEST_EXE echo "$FILE should not compile" fail=$(($fail+1)) else pass=$(($pass+1)) fi done fi if (ls ${TEST_PREFIX}*.cpp > /dev/null 2>&1) then if (ls *.dat > /dev/null 2>&1) then adb_shell "rm -f $TARGET_PATH/*.dat" for FILE in $(ls *.dat | tr ' ' '\n' | grep -v EQ | sort); do if [ "$VERBOSE" -gt 1 ]; then echo "Pushing data: " $FILE fi adb_push $FILE if [ $? != 0 ]; then echo "Failed to push file $FILE" fi done fi for FILE in $(ls ${TEST_PREFIX}*.cpp | tr ' ' '\n' | grep -v EQ | sort); do if [ "$VERBOSE" -gt 1 ]; then echo "Running test: " $FILE fi COMMAND="( cd $(pwd) && $CC $OPTIONS $HEADER_INCLUDE $SOURCE_LIB $FILE $LIBS )" if run $CC $OPTIONS $HEADER_INCLUDE $SOURCE_LIB $FILE $LIBS -o $TEST_EXE then if adb_run $TEST_EXE then rm $TEST_EXE pass=$(($pass+1)) else echo "`pwd`/$FILE failed at run time" echo "Compile line was: $COMMAND # run-time" fail=$(($fail+1)) rm $TEST_EXE fi else echo "`pwd`/$FILE failed to compile" echo "Compile line was: $COMMAND # compile-time" fail=$(($fail+1)) fi done fi if [ $fail -gt 0 ] then echo "failed $fail tests in `pwd`" IMPLEMENTED_FAIL=$(($IMPLEMENTED_FAIL+1)) fi if [ $pass -gt 0 ] then echo "passed $pass tests in `pwd`" if [ $fail -eq 0 ] then IMPLEMENTED_PASS=$((IMPLEMENTED_PASS+1)) fi fi if [ $fail -eq 0 -a $pass -eq 0 ] then echo "not implemented: `pwd`" UNIMPLEMENTED=$(($UNIMPLEMENTED+1)) fi FAIL=$(($FAIL+$fail)) PASS=$(($PASS+$pass)) for FILE in $(ls | tr ' ' '\n' | grep -v EQ | sort) do if [ -d "$FILE" ]; then cd $FILE afunc cd .. fi done } afunc echo "****************************************************" echo "Results for `pwd`:" echo "using `$CC --version`" echo "with $OPTIONS $HEADER_INCLUDE $SOURCE_LIB" echo "----------------------------------------------------" echo "sections without tests : $UNIMPLEMENTED" echo "sections with failures : $IMPLEMENTED_FAIL" echo "sections without failures: $IMPLEMENTED_PASS" echo " + ----" echo "total number of sections : $(($UNIMPLEMENTED+$IMPLEMENTED_FAIL+$IMPLEMENTED_PASS))" echo "----------------------------------------------------" echo "number of tests failed : $FAIL" echo "number of tests passed : $PASS" echo " + ----" echo "total number of tests : $(($FAIL+$PASS))" echo "****************************************************" exit $FAIL