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