#!/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