aboutsummaryrefslogtreecommitdiff
path: root/tests/run-tests.sh
blob: 65fc43617c46091e1774682d011263f1131dd50c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
#!/bin/sh
#
# Copyright (C) 2010 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 shell script is used to run all NDK build tests in a row.
#  "Build tests" are tests that check the building features of the NDK
#  but do not run anything on target devices/emulators.
#

#  You need to define the NDK

PROGDIR=`dirname $0`
PROGDIR=`cd $PROGDIR && pwd`

# Assume that we are under tests/
# and that the samples will be under samples/ and platforms/android-N/samples/
#
ROOTDIR=`cd $PROGDIR/.. && pwd`
NDK_BUILDTOOLS_PATH=$ROOTDIR/build/tools
. $NDK_BUILDTOOLS_PATH/ndk-common.sh
. $NDK_BUILDTOOLS_PATH/prebuilt-common.sh

# Defining _NDK_TESTING_ALL_=yes to put armeabi-v7a-hard in its own libs/armeabi-v7a-hard
# directoy and tested separately from armeabi-v7a.  Some tests are now compiled with both
# APP_ABI=armeabi-v7a and APP_ABI=armeabi-v7a-hard. Without _NDK_TESTING_ALL_=yes, tests
# may fail to install due to race condition on the same libs/armeabi-v7a
if [ -z "$_NDK_TESTING_ALL_" ]; then
   _NDK_TESTING_ALL_=all
fi
export _NDK_TESTING_ALL_

# The list of tests that are too long to be part of a normal run of
# run-tests.sh. Most of these do not run properly at the moment.
LONG_TESTS="prebuild-stlport test-stlport test-gnustl-full \
test-stlport_shared-exception test-stlport_static-exception \
test-gnustl_shared-exception-full test-gnustl_static-exception-full \
test-googletest-full test-libc++-shared-full test-libc++-static-full"

#
# Parse options
#
VERBOSE=no
ABI=default
PLATFORM=""
NDK_ROOT=
JOBS=$BUILD_NUM_CPUS
find_program ADB_CMD adb
TESTABLES="samples build device awk"
FULL_TESTS=no
RUN_TESTS=
RUN_TESTS_FILTERED=
NDK_PACKAGE=
WINE=
CONTINUE_ON_BUILD_FAIL=
if [ -z "$TEST_DIR" ]; then
    TEST_DIR="/tmp/ndk-$USER/tests"
fi
if [ -z "$TARGET_TEST_SUBDIR" ]; then
    TARGET_TEST_SUBDIR="ndk-tests"
fi

while [ -n "$1" ]; do
    opt="$1"
    optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'`
    case "$opt" in
        --help|-h|-\?)
            OPTION_HELP=yes
            ;;
        --verbose)
            if [ "$VERBOSE" = "yes" ] ; then
                VERBOSE2=yes
            else
                VERBOSE=yes
            fi
            ;;
        --abi=*)
            ABI="$optarg"
            ;;
        --platform=*)
            PLATFORM="$optarg"
	    ;;
        --test-dir=*)
            TEST_DIR="$optarg"
            ;;
        --ndk=*)
            NDK_ROOT="$optarg"
            ;;
        --full)
            FULL_TESTS=yes;
            ;;
        --test=*)
            RUN_TESTS="$RUN_TESTS $optarg"
            ;;
        --test-filtered=*)
            # same as --test but apply BROKEN_RUN too. Useful for projects with tons of test some of them can't run
            RUN_TESTS="$RUN_TESTS $optarg"
            RUN_TESTS_FILTERED="yes"
            ;;
        --package=*)
            NDK_PACKAGE="$optarg"
            ;;
        -j*)
            JOBS=`expr "$opt" : '-j\(.*\)'`
            shift
            ;;
        --jobs=*)
            JOBS="$optarg"
            ;;
        --adb=*)
            ADB_CMD="$optarg"
            ;;
        --only-samples)
            TESTABLES=samples
            ;;
        --only-build)
            TESTABLES=build
            ;;
        --only-device)
            TESTABLES=device
            ;;
        --only-awk)
            TESTABLES=awk
            ;;
        --wine)
            WINE=yes
            ;;
        --continue-on-build-fail)
            CONTINUE_ON_BUILD_FAIL=yes
            ;;
        -*) # unknown options
            echo "ERROR: Unknown option '$opt', use --help for list of valid ones."
            exit 1
        ;;
        *)  # Simply record new test name
            RUN_TESTS=$RUN_TESTS" $opt"
            ;;
    esac
    shift
done

if [ "$OPTION_HELP" = "yes" ] ; then
    echo "Usage: $PROGNAME [options] [testname1 [testname2...]]"
    echo ""
    echo "Run NDK automated tests. Without any parameter, this will try to"
    echo "run all standard tests, except those are tagged broken. You can"
    echo "also select/enforce specific tests by listing their name on the"
    echo "command-line."
    echo ""
    echo "Valid options:"
    echo ""
    echo "    --help|-h|-?      Print this help"
    echo "    --verbose         Enable verbose mode (can be used several times)"
    echo "    --ndk=<path>      Path to NDK to test [$ROOTDIR]"
    echo "    --package=<path>  Path to NDK package to test"
    echo "    -j<N> --jobs=<N>  Launch parallel builds [$JOBS]"
    echo "    --abi=<name>      Only run tests for the specific ABI [$ABI]"
    echo "    --platform=<name> Force API level for testing; platform=<android-x>"
    echo "    --adb=<file>      Specify adb executable for device tests"
    echo "    --only-samples    Only rebuild samples"
    echo "    --only-build      Only rebuild build tests"
    echo "    --only-device     Only rebuild & run device tests"
    echo "    --only-awk        Only run awk tests."
    echo "    --full            Run all device tests, even very long ones."
    echo "    --wine            Build all tests with wine on Linux"
    echo ""
    echo "NOTE: You cannot use --ndk and --package at the same time."
    echo ""
    exit 0
fi

# Run a command in ADB.
#
# This is needed because "adb shell" does not return the proper status
# of the launched command, so we need to add it to the output, and grab
# it after that.
# $1: Device name
# $2: Variable name that will contain the result
# $3+: Command options
adb_var_shell_cmd ()
{
    # We need a temporary file to store the output of our command
    local ADB_SHELL_CMD_LOG RET OUT
    local DEVICE=$1
    local VARNAME=$2
    shift; shift;
    ADB_SHELL_CMD_LOG=$(mktemp -t XXXXXXXX)
    # Run the command, while storing the standard output to ADB_SHELL_CMD_LOG
    # and appending the exit code as the last line.
    if [ $VERBOSE = "yes" ] ; then
        echo "$ADB_CMD -s \"$DEVICE\" shell \"$@\""
        $ADB_CMD -s "$DEVICE" shell "$@" ";" echo \$? | sed -e 's![[:cntrl:]]!!g' | tee $ADB_SHELL_CMD_LOG
    else
        $ADB_CMD -s "$DEVICE" shell "$@" ";" echo \$? | sed -e 's![[:cntrl:]]!!g' > $ADB_SHELL_CMD_LOG
    fi
    # Get last line in log, which contains the exit code from the command
    RET=`sed -e '$!d' $ADB_SHELL_CMD_LOG`
    # Get output, which corresponds to everything except the last line
    OUT=`sed -e '$d' $ADB_SHELL_CMD_LOG`
    rm -f $ADB_SHELL_CMD_LOG
    if [ "$VARNAME" != "" ]; then
        eval $VARNAME=\"\$OUT\"
    fi
    return $RET
}

# Make a directory path on device
#
# The 'mkdir' command on the Android device does not
# support the '-p' option. This function will test
# for the existence of the parent directory and recursively
# call itself until it files a parent which exists; then
# create the requested directory.
adb_shell_mkdir ()
{
    local FULLDIR BASEDIR
    local DEVICE=$1
    local FULLDIR=$2
    local BASEDIR=`dirname $FULLDIR`

    adb_var_shell_cmd "$DEVICE" "" "ls $BASEDIR 1>/dev/null 2>&1"
    if [ $? != 0 ] ; then
        if [ $BASEDIR = "/" ] ; then
            dump "ERROR: Could not find the root (/) directory on the device!"
            exit 1
        else
            adb_shell_mkdir "$DEVICE" $BASEDIR
            adb_shell_mkdir "$DEVICE" $FULLDIR
        fi
    else
        #If the directory doesn't exist, make it
        adb_var_shell_cmd "$DEVICE" "" "ls $FULLDIR 1>/dev/null 2>&1 || mkdir $FULLDIR"
        if [ $? != 0 ] ; then
            dump "ERROR: Could not mkdir '$FULLDIR' on the device!"
            exit 1
        fi
    fi
}

# Returns 0 if a variable containing one or more items separated
# by spaces contains a given value.
# $1: variable name (e.g. FOO)
# $2: value to test
var_list_contains ()
{
    echo `var_value $1` | tr ' ' '\n' | grep -q -F -x -e "$2"
}

#
# List of stuff to actually tests
#
is_testable () {
    var_list_contains TESTABLES "$1"
}

# is_buildable returns 0 if a test should be built/run for this invocation
# $1: test path
if [ -n "$RUN_TESTS" ] ; then
    is_buildable () {
        [ -f $1/build.sh -o -f $1/jni/Android.mk ] &&
        var_list_contains RUN_TESTS "`basename $1`"
    }
elif [ "$FULL_TESTS" = "yes" ] ; then
    is_buildable () {
        [ -f $1/build.sh -o -f $1/jni/Android.mk ]
    }
else # !FULL_TESTS
    is_buildable () {
        [ -f $1/build.sh -o -f $1/jni/Android.mk ] || return 1
        ! var_list_contains LONG_TESTS "`basename $1`" || return 1
    }
fi # !FULL_TESTS


mkdir -p $TEST_DIR
setup_default_log_file "$TEST_DIR/build-tests.log"

if [ -n "$NDK_PACKAGE" ] ; then
    if [ -n "$NDK_ROOT" ] ; then
        dump "ERROR: You can't use --ndk and --package at the same time!"
        exit 1
    fi
    NDK_ROOT=/tmp/ndk-tests/install
    mkdir -p  "$NDK_ROOT" && rm -rf "$NDK_ROOT/*"
    dump "Unpacking NDK package to $NDK_ROOT"
    unpack_archive "$NDK_PACKAGE" "$NDK_ROOT"
    NDK_ROOT=`ls -d $NDK_ROOT/*`
fi

#
# Check the NDK install path.
#
if [ -n "$NDK_ROOT" ] ; then
    if [ ! -d "$NDK_ROOT" ] ; then
        dump "ERROR: Your --ndk option does not point to a directory: $NDK_ROOT"
        dump "Please use a valid path for this option."
        exit 1
    fi
    if [ ! -f "$NDK_ROOT/ndk-build" -o ! -f "$NDK_ROOT/build/tools/prebuilt-common.sh" ] ; then
        dump "ERROR: Your --ndk option does not point to a valid NDK install: $NDK_ROOT"
        dump "Please use a valid NDK install path for this option."
        exit 3
    fi
    NDK="$NDK_ROOT"
else
    NDK="$ROOTDIR"
fi

#
# Create log file
#

BUILD_DIR=$TEST_DIR/build
mkdir -p "$BUILD_DIR" && rm -rf "$BUILD_DIR/*"

#
# Add -link-native-binary to allow linking native binaries
#
if [ "$NDK_ABI_FILTER" != "${NDK_ABI_FILTER%%bc*}" ] ; then
  APP_LDFLAGS="$APP_LDFLAGS -Wl,-link-native-binary"
fi


###
### RUN AWK TESTS
###

# Run a simple awk script
# $1: awk script to run
# $2: input file
# $3: expected output file
# $4+: optional additional command-line arguments for the awk command
run_awk_test ()
{
    local SCRIPT="$1"
    local SCRIPT_NAME="`basename $SCRIPT`"
    local INPUT="$2"
    local INPUT_NAME="`basename $INPUT`"
    local EXPECTED="$3"
    local EXPECTED_NAME="`basename $EXPECTED`"
    shift; shift; shift;
    local OUTPUT="$BUILD_DIR/$EXPECTED_NAME"
    if [ "$VERBOSE2" = "yes" ]; then
        echo "### COMMAND: awk -f \"$SCRIPT\" $@ < \"$INPUT\" > \"$OUTPUT\""
    fi
    awk -f "$SCRIPT" $@ < "$INPUT" > "$OUTPUT"
    fail_panic "Can't run awk script: $SCRIPT"
    if [ "$VERBOSE2" = "yes" ]; then
        echo "OUTPUT FROM SCRIPT:"
        cat "$OUTPUT"
        echo "EXPECTED VALUES:"
        cat "$EXPECTED"
    fi
    cmp -s "$OUTPUT" "$EXPECTED"
    if [ $? = 0 ] ; then
        echo "Awk script: $SCRIPT_NAME: passed $INPUT_NAME"
        if [ "$VERBOSE2" = "yes" ]; then
            cat "$OUTPUT"
        fi
    else
        if [ "$VERBOSE" = "yes" ]; then
            run diff -burN "$EXPECTED" "$OUTPUT"
        fi
        echo "Awk script: $SCRIPT_NAME: $INPUT_NAME FAILED!!"
        rm -f "$OUTPUT"
        exit 1
    fi
}

run_awk_test_dir ()
{
    local SCRIPT_NAME="`basename \"$DIR\"`"
    local SCRIPT="$ROOTDIR/build/awk/$SCRIPT_NAME.awk"
    local INPUT
    local OUTPUT
    if [ ! -f "$SCRIPT" ]; then
        echo "Awk script: $SCRIPT_NAME: Missing script: $SCRIPT"
        continue
    fi
    for INPUT in `ls "$PROGDIR"/awk/$SCRIPT_NAME/*.in`; do
        OUTPUT=`echo $INPUT | sed 's/\.in$/.out/g'`
        if [ ! -f "$OUTPUT" ]; then
            echo "Awk script: $SCRIPT_NAME: Missing awk output file: $OUTPUT"
            continue
        fi
        run_awk_test "$SCRIPT" "$INPUT" "$OUTPUT"
    done
}

if is_testable awk; then
    AWKDIR="$ROOTDIR/build/awk"
    for DIR in `ls -d "$PROGDIR"/awk/*`; do
        run_awk_test_dir "$DIR"
    done
fi

###
###  REBUILD ALL SAMPLES FIRST
###

NDK_BUILD_FLAGS="-B"
if [ "$WINE" ]; then
    case "$NDK_HOST_32BIT" in
        1|true)
            WINE=wine12
            ;;
        *)
            WINE=wine17
            NDK_BUILD_FLAGS=""  # make.exe -B hangs in wine > 1.2.x
            if [ "$NDK_TOOLCHAIN_VERSION" != "4.4.3" ] ; then
                APP_LDFLAGS="$APP_LDFLAGS -fuse-ld=mcld" # 64-bit ld.gold can't run in any wine!
            fi
            ;;
    esac
    find_program WINE_PROG $WINE
    fail_panic "Can't locate $WINE"
fi

# $1: output bitcode path
gen_empty_bitcode() {
    TEMP_FILE=`mktemp`
    mv $TEMP_FILE ${TEMP_FILE}.c
    run $NDK/$(get_llvm_toolchain_binprefix $DEFAULT_LLVM_VERSION)/clang -shared -target le32-none-ndk -emit-llvm -o $1 ${TEMP_FILE}.c
    rm -f ${TEMP_FILE}.c
}

# $1: output archive path
gen_empty_archive() {
    run ar crs $1
}

case $ABI in
    default)  # Let the APP_ABI in jni/Application.mk decide what to build
        ;;
    armeabi|armeabi-v7a|arm64-v8a|x86|x86_64|mips|mips64|armeabi-v7a-hard)
        NDK_BUILD_FLAGS="$NDK_BUILD_FLAGS APP_ABI=$ABI"
        ;;
    *)
        if [ -n "$(filter_out "$PREBUILT_ABIS" "$ABI")" ] && [ -n "$(find_ndk_unknown_archs)" ]; then
            ABI=$(find_ndk_unknown_archs)
            NDK_BUILD_FLAGS="$NDK_BUILD_FLAGS APP_ABI=$ABI"

            # Create those temporarily files to make testing happy
            GCC_TOOLCHAIN_VERSION=`cat $NDK/toolchains/llvm-$DEFAULT_LLVM_VERSION/setup.mk | grep '^TOOLCHAIN_VERSION' | awk '{print $3'}`
            run mkdir -p $NDK/$GNUSTL_SUBDIR/$GCC_TOOLCHAIN_VERSION/libs/$ABI
            run mkdir -p $NDK/$GABIXX_SUBDIR/libs/$ABI
            run mkdir -p $NDK/$LIBPORTABLE_SUBDIR/libs/$ABI
            run gen_empty_archive $NDK/$GNUSTL_SUBDIR/$GCC_TOOLCHAIN_VERSION/libs/$ABI/libsupc++.a
            run gen_empty_archive $NDK/$GNUSTL_SUBDIR/$GCC_TOOLCHAIN_VERSION/libs/$ABI/libgnustl_static.a
            run gen_empty_bitcode $NDK/$GNUSTL_SUBDIR/$GCC_TOOLCHAIN_VERSION/libs/$ABI/libgnustl_shared.bc
            run gen_empty_archive $NDK/$GABIXX_SUBDIR/libs/$ABI/libgabi++_static.a
            run gen_empty_bitcode $NDK/$GABIXX_SUBDIR/libs/$ABI/libgabi++_shared.bc
            run cp -a $NDK/$GNUSTL_SUBDIR/$GCC_TOOLCHAIN_VERSION/libs/$(get_default_abi_for_arch arm)/include $NDK/$GNUSTL_SUBDIR/$GCC_TOOLCHAIN_VERSION/libs/$ABI
        else
            echo "ERROR: Unsupported abi value: $ABI"
            exit 1
        fi
        ;;
esac

# Force all tests to run at one API level
if [ "$PLATFORM" != "" ]; then
    NDK_BUILD_FLAGS="$NDK_BUILD_FLAGS APP_PLATFORM=$PLATFORM"
fi

# Use --verbose twice to see build commands for the tests
if [ "$VERBOSE2" = "yes" ] ; then
    NDK_BUILD_FLAGS="$NDK_BUILD_FLAGS V=1"
fi

run_ndk_build ()
{
    if [ "$WINE" ]; then
        if [ "$WINE" = "wine12" ]; then
            run $WINE cmd /c Z:$NDK/ndk-build.cmd -j$JOBS "$@" APP_LDFLAGS="$APP_LDFLAGS" APP_CFLAGS="$APP_CFLAGS"
        else
            # do "clean" instead of -B
            run $WINE cmd /c Z:$NDK/ndk-build.cmd clean
            # make.exe can't do parallel build in wine > 1.2.x
            run $WINE cmd /c Z:$NDK/ndk-build.cmd "$@" -j1 APP_LDFLAGS="$APP_LDFLAGS" APP_CFLAGS="$APP_CFLAGS"
        fi
    else
        run $NDK/ndk-build -j$JOBS "$@" APP_LDFLAGS="$APP_LDFLAGS" APP_CFLAGS="$APP_CFLAGS"
    fi
}

# get build var
# $1: project directory
# $2: var
get_build_var ()
{
    local PROJECT=$1
    local VAR=$2

    if [ -z "$GNUMAKE" ] ; then
        GNUMAKE=make
    fi
    $GNUMAKE --no-print-dir -f $NDK/build/core/build-local.mk -C $PROJECT DUMP_$VAR | tail -1
}


# check if the project is broken and shouldn't be built
# $1: project directory
# $2: optional error message
is_broken_build ()
{
    local PROJECT="$1"
    local ERRMSG="$2"

    if [ -z "$RUN_TESTS" ] ; then
        if [ -f "$PROJECT/BROKEN_BUILD" ] ; then
            if [ ! -s "$PROJECT/BROKEN_BUILD" ] ; then
                # skip all
                if [ -z "$ERRMSG" ] ; then
                    echo "Skipping `basename $PROJECT`: (build)"
                else
                    echo "Skipping $ERRMSG: `basename $PROJECT`"
                fi
                return 0
            else
                # only skip listed in file
                TARGET_TOOLCHAIN=`get_build_var $PROJECT TARGET_TOOLCHAIN`
                TARGET_TOOLCHAIN_VERSION=`echo $TARGET_TOOLCHAIN | tr '-' '\n' | tail -1`
                grep -q -e "$TARGET_TOOLCHAIN_VERSION" "$PROJECT/BROKEN_BUILD"
                if [ $? = 0 ] ; then
                    if [ -z "$ERRMSG" ] ; then
                        echo "Skipping `basename $PROJECT`: (no build for $TARGET_TOOLCHAIN_VERSION)"
                    else
                        echo "Skipping $ERRMSG: `basename $PROJECT` (no build for $TARGET_TOOLCHAIN_VERSION)"
                    fi
                    return 0
                fi
                # skip incompatible forced platform
                if [ "$PLATFORM" != "" ] ; then
                    grep -q -e "$PLATFORM" "$PROJECT/BROKEN_BUILD" || grep -q -e "android-forced" "$PROJECT/BROKEN_BUILD"
                    if [ $? = 0 ] ; then
                        if [ -z "$ERRMSG" ] ; then
                            echo "Skipping `basename $PROJECT`: (no build for $PLATFORM)"
                        else
                            echo "Skipping $ERRMSG: `basename $PROJECT` (no build for $PLATFORM)"
                        fi
                        return 0
                    fi
                fi
            fi
        fi
    fi
    return 1
}

# check if $ABI is incompatible and shouldn't be built
# $1: project directory
is_incompatible_abi ()
{
    local PROJECT="$1"
    # Basically accept all for unknown arch, even some cases may not be suitable for this way
    if [ "$ABI" != "default" -a "$ABI" != "$(find_ndk_unknown_archs)" ] ; then
        # check APP_ABI
        local APP_ABIS=`get_build_var $PROJECT APP_ABI`
        APP_ABIS=$APP_ABIS" "
        if [ "$APP_ABIS" != "${APP_ABIS%%all*}" ] ; then
        # replace "all", "all32" and "all64"
          _EXPANDED=`get_build_var $PROJECT NDK_APP_ABI_ALL_EXPANDED`
          _FRONT="${APP_ABIS%%all*}"
          _BACK="${APP_ABIS#*all}"
          APP_ABIS="${_FRONT}${_EXPANDED}${_BACK}"
          _EXPANDED=`get_build_var $PROJECT NDK_APP_ABI_ALL32_EXPANDED`
          _FRONT="${APP_ABIS%%all32*}"
          _BACK="${APP_ABIS#*all32}"
          APP_ABIS="${_FRONT}${_EXPANDED}${_BACK}"
          _EXPANDED=`get_build_var $PROJECT NDK_APP_ABI_ALL64_EXPANDED`
          _FRONT="${APP_ABIS%%all64*}"
          _BACK="${APP_ABIS#*all64}"
          APP_ABIS="${_FRONT}${_EXPANDED}${_BACK}"
        fi
        if [ "$APP_ABIS" = "${APP_ABIS%$ABI *}" ] ; then
            echo "Skipping `basename $PROJECT`: incompatible ABI, needs $APP_ABIS"
            return 0
        fi
    fi
    return 1
}

compile_on_the_fly()
{
    local DSTDIR="$1"
    local COMPILER_PKGNAME="compiler.abcc"
    if [ -z "`$ADB_CMD -s "$DEVICE" shell pm path $COMPILER_PKGNAME`" ]; then
        dump "ERROR: No abcc found for unknown arch testing"
        return 1
    fi
    run $ADB_CMD -s "$DEVICE" shell am force-stop $COMPILER_PKGNAME
    run $ADB_CMD -s "$DEVICE" shell am startservice --user 0 -a ${COMPILER_PKGNAME}.BITCODE_COMPILE_TEST -n $COMPILER_PKGNAME/.AbccService -e working_dir $DSTDIR

    old_pid="`$ADB_CMD -s "$DEVICE" shell top -n 1 | grep $COMPILER_PKGNAME | awk '{print $1}'`"
    threshold=`echo $((60*10))` # Wait at most 10 minutes for large testcases
    sleep_seconds=0
    while [ 2 -eq 2 ]; do
      if [ $sleep_seconds -gt $threshold ]; then
        pid="`$ADB_CMD -s "$DEVICE" shell top -n 1 | grep $COMPILER_PKGNAME | awk '{print $1}'`"
        if [ "$pid" = "$old_pid" ]; then
          # Too much time
          break
        fi
        old_pid="$pid"
        sleep_seconds=0
      fi
      if [ -n "`$ADB_CMD -s "$DEVICE" shell ls $DSTDIR | grep compile_result`" ]; then
        # Compile done
        break
      fi
      sleep 3
      sleep_seconds="`echo $sleep_seconds + 3 | bc`"
    done
    ret="`$ADB_CMD -s "$DEVICE" shell cat $DSTDIR/compile_result`"
    ret=`echo $ret | tr -d "\r\n"`
    if [ $sleep_seconds -gt $threshold ] || [ "$ret" != "0" ]; then
      dump "ERROR: Could not compile bitcodes for $TEST_NAME on device"
      if [ $sleep_seconds -gt $threshold ]; then
        dump "- Reason: Compile time too long"
      elif [ -n "`$ADB_CMD -s "$DEVICE" shell ls $DSTDIR | grep compile_error`" ]; then
        dump "- Reason: `$ADB_CMD -s "$DEVICE" shell cat $DSTDIR/compile_error`"
      fi
      run $ADB_CMD -s "$DEVICE" shell am force-stop $COMPILER_PKGNAME
      return 1
    fi
    run $ADB_CMD -s "$DEVICE" shell am force-stop $COMPILER_PKGNAME
    return 0
}


build_project ()
{
    local NAME=`basename $1`
    local CHECK_ABI=$2
    local DIR="$BUILD_DIR/$NAME"

    if is_broken_build $1; then
        return 0;
    fi
    if [ "$CHECK_ABI" = "yes" ] ; then
        if is_incompatible_abi $1 ; then
            return 0
        fi
    fi
    rm -rf "$DIR" && cp -r "$1" "$DIR"
    # build it
    (run cd "$DIR" && run_ndk_build $NDK_BUILD_FLAGS)
    RET=$?
    if [ -f "$1/BUILD_SHOULD_FAIL" ]; then
        if [ $RET = 0 ]; then
            echo "!!! FAILURE: BUILD SHOULD HAVE FAILED [$1]"
            if [ "$CONTINUE_ON_BUILD_FAIL" != yes ] ; then
                exit 1
            fi
        fi
        log "!!! SUCCESS: BUILD FAILED AS EXPECTED [$(basename $1)]"
        RET=0
    fi
    if [ $RET != 0 ] ; then
        echo "!!! BUILD FAILURE [$1]!!! See $NDK_LOGFILE for details or use --verbose option!"
        if [ "$CONTINUE_ON_BUILD_FAIL" != yes ] ; then
            exit 1
        fi
    fi
}

#
# Determine list of samples directories.
#
if is_testable samples; then
    if [ -f "$NDK/RELEASE.TXT" ] ; then
        # This is a release package, all samples should be under $NDK/samples
        SAMPLES_DIRS="$NDK/samples"
        if [ ! -d "$SAMPLES_DIRS" ] ; then
            dump "ERROR: Missing samples directory: $SAMPLES_DIRS"
            dump "Your NDK release installation is broken!"
            exit 1
        fi
        log "Using release NDK samples from: $SAMPLES_DIRS"
    else
        # This is a development work directory, we will take the samples
        # directly from development/ndk.
        DEVNDK_DIR=`dirname $NDK`/development/ndk
        if [ ! -d "$DEVNDK_DIR" ] ; then
            dump "ERROR: Could not find development NDK directory: $DEVNDK_DIR"
            dump "Please clone platform/development.git from android.googlesource.com"
            exit 1
        fi
        SAMPLES_DIRS="$DEVNDK_DIR/samples"
        for DIR in `ls -d $DEVNDK_DIR/platforms/android-*/samples`; do
            SAMPLES_DIRS="$SAMPLES_DIRS $DIR"
        done
        dump "Using development NDK samples from $DEVNDK_DIR"
        if [ "$VERBOSE" = "yes" ] ; then
            echo "$SAMPLES_DIRS" | tr ' ' '\n'
        fi
    fi

    #
    # Copy the samples to a temporary build directory

    build_sample ()
    {
        echo "Building NDK sample: `basename $1`"
        build_project $1 "no"
    }

    for DIR in $SAMPLES_DIRS; do
        for SUBDIR in `ls -d $DIR/*`; do
            if is_buildable $SUBDIR; then
                build_sample $SUBDIR
            fi
        done
    done
fi

###
###  BUILD PROJECTS UNDER tests/build/
###

if is_testable build; then
    build_build_test ()
    {
        local NAME="$(basename $1)"
        echo "Building NDK build test: `basename $1`"
        if [ -f $1/build.sh ]; then
            local DIR="$BUILD_DIR/$NAME"
            if [ -f "$1/jni/Android.mk" -a -f "$1/jni/Application.mk" ] ; then
                # exclude jni/Android.mk with import-module because it needs NDK_MODULE_PATH
                grep -q  "call import-module" "$1/jni/Android.mk"
                if [ $? != 0 ] ; then
                    if (is_broken_build $1 || is_incompatible_abi $1) then
                        return 0;
                    fi
                fi
            fi
            rm -rf "$DIR" && cp -r "$1" "$DIR"
            export NDK
            (cd "$DIR" && run ./build.sh -j$JOBS $NDK_BUILD_FLAGS)
            if [ $? != 0 ]; then
                echo "!!! BUILD FAILURE [$1]!!! See $NDK_LOGFILE for details or use --verbose option!"
                if [ "$CONTINUE_ON_BUILD_FAIL" != yes ] ; then
                    exit 1
                fi
            fi
        else
            build_project $1 "yes"
        fi
    }

    for DIR in `ls -d $ROOTDIR/tests/build/*`; do
        if is_buildable $DIR; then
            build_build_test $DIR
        fi
    done
fi

###
###  BUILD PROJECTS UNDER tests/device/
###

CPU_ABIS=
if is_testable device; then
    build_device_test ()
    {
        if is_broken_build $1 "broken device test build"; then
            return 0;
        fi
        echo "Building NDK device test: `basename $1`"
        build_project $1 "yes"
    }

    # $1: DEVICE
    # $2: DEVICE CPU ABI
    # $3: test
    # $4: tmp dir
    run_device_test ()
    {
        local DEVICE=$1
        local CPU_ABI=$2
        local TEST=$3
        local TEST_NAME="$(basename $TEST)"
        local SRCDIR
        local DSTDIR="$4/$TARGET_TEST_SUBDIR"
        local SRCFILE
        local DSTFILE
        local PROGRAM
        # Do not run the test if BROKEN_RUN is defined
        if [ -z "$RUN_TESTS" ]; then
            if is_broken_build $TEST "NDK device test not built"; then
                return 0
            fi
            if [ -f "$TEST/BROKEN_RUN" ] ; then
                if [ ! -s "$TEST/BROKEN_RUN" ] ; then
                    # skip all
                    dump "Skipping NDK device test run: $TEST_NAME"
                    return 0
                else
                    # skip all tests built by toolchain
                    TARGET_TOOLCHAIN=`get_build_var $TEST TARGET_TOOLCHAIN`
                    TARGET_TOOLCHAIN_VERSION=`echo $TARGET_TOOLCHAIN | tr '-' '\n' | tail -1`
                    grep -q -e "$TARGET_TOOLCHAIN_VERSION" "$TEST/BROKEN_RUN"
                    if [ $? = 0 ] ; then
                        dump "Skipping NDK device test run: $TEST_NAME (no run for binary built by $TARGET_TOOLCHAIN_VERSION)"
                        return 0
                    fi
                    # skip tests listed in file
                    SKIPPED_EXECUTABLES=`cat $TEST/BROKEN_RUN | tr '\n' ' '`
                    dump "Skipping NDK device test run: $TEST_NAME ($SKIPPED_EXECUTABLES)"
                fi
            fi
        fi
        if [ "$ABI" = "$(find_ndk_unknown_archs)" ] && [ -d "$BUILD_DIR/`basename $TEST`/libs" ]; then
            cd $BUILD_DIR/`basename $TEST`/libs && cp -a $ABI $CPU_ABI
        fi
        SRCDIR="$BUILD_DIR/`basename $TEST`/libs/$CPU_ABI"
        if [ ! -d "$SRCDIR" ] || [ -z "`ls $SRCDIR`" ]; then
            dump "Skipping NDK device test run (no $CPU_ABI binaries): $TEST_NAME"
            return 0
        fi
        # First, copy all files to the device, except for gdbserver, gdb.setup, and
        # those declared in $TEST/BROKEN_RUN
        adb_shell_mkdir "$DEVICE" $DSTDIR

        if [ "$ABI" = "$(find_ndk_unknown_archs)" ]; then # on-the-fly on-device compilation
            run $ADB_CMD -s "$DEVICE" shell rm -rf $DSTDIR/abcc_tmp
            adb_shell_mkdir "$DEVICE" $DSTDIR/abcc_tmp
            run $ADB_CMD -s "$DEVICE" shell chmod 0777 $DSTDIR/abcc_tmp
            for SRCFILE in `ls $SRCDIR`; do
                run $ADB_CMD -s "$DEVICE" push "$SRCDIR/$SRCFILE" $DSTDIR/abcc_tmp
                run $ADB_CMD -s "$DEVICE" shell chmod 0644 $DSTDIR/abcc_tmp/$SRCFILE
            done
            compile_on_the_fly $DSTDIR/abcc_tmp
            if [ $? -ne 0 ]; then
                test "$CONTINUE_ON_BUILD_FAIL" != "yes" && exit 1
                return 1
            fi
            run rm -f $SRCDIR/*
            run $ADB_CMD -s "$DEVICE" pull $DSTDIR/abcc_tmp $SRCDIR
            run rm -f $SRCDIR/compile_result
            run rm -f $SRCDIR/compile_error
            run rm -f $SRCDIR/*$(get_lib_suffix_for_abi $ABI)
            run $ADB_CMD -s "$DEVICE" shell rm -rf $DSTDIR/abcc_tmp
        fi

        for SRCFILE in `ls $SRCDIR`; do
            DSTFILE=`basename $SRCFILE`
            echo "$DSTFILE" | grep -q -e '\.so$'
            if [ $? != 0 ] ; then
                continue
            fi
            SRCPATH="$SRCDIR/$SRCFILE"
            if [ $HOST_OS = cygwin ]; then
                SRCPATH=`cygpath -m $SRCPATH`
            fi
            DSTPATH="$DSTDIR/$DSTFILE"
            run $ADB_CMD -s "$DEVICE" push "$SRCPATH" "$DSTPATH" &&
            run $ADB_CMD -s "$DEVICE" shell chmod 0755 $DSTPATH
            if [ $? != 0 ] ; then
                dump "ERROR: Could not install $SRCPATH to device $DEVICE!"
                exit 1
            fi
        done

        for SRCFILE in `ls $SRCDIR`; do
            DSTFILE=`basename $SRCFILE`
            if [ "$DSTFILE" = "gdbserver" -o "$DSTFILE" = "gdb.setup" ] ; then
                continue
            fi
            echo "$DSTFILE" | grep -q -e '\.so$'
            if [ $? = 0 ] ; then
              continue
            fi
            if [ -z "$RUN_TESTS" -o "$RUN_TESTS_FILTERED" = "yes" ]; then
                if [ -f "$TEST/BROKEN_RUN" ]; then
                    grep -q -w -e "$DSTFILE" "$TEST/BROKEN_RUN"
                    if [ $? = 0 ] ; then
                        continue
                    fi
                fi
            fi
            SRCPATH="$SRCDIR/$SRCFILE"
            if [ $HOST_OS = cygwin ]; then
                SRCPATH=`cygpath -m $SRCPATH`
            fi
            DSTPATH="$DSTDIR/$DSTFILE"
            run $ADB_CMD -s "$DEVICE" push "$SRCPATH" "$DSTPATH" &&
            run $ADB_CMD -s "$DEVICE" shell chmod 0755 $DSTPATH
            DATAPATHS=
            if [ -f "$TEST/DATA" ]; then
                if grep -q -e "$DSTFILE" "$TEST/DATA"; then
                    DATAPATHS=`grep -e "$DSTFILE" "$TEST/DATA" | awk '{print $2}'`
                    DATAPATHS=$NDK/$DATAPATHS
                    for DATA in $(ls $DATAPATHS); do
                        run $ADB_CMD -s "$DEVICE" push "$DATA" "$DSTDIR"
                    done
                fi
            fi
            if [ $? != 0 ] ; then
                dump "ERROR: Could not install $SRCPATH to device $DEVICE!"
                exit 1
            fi
            PROGRAM="`basename $DSTPATH`"
            dump "Running device test [$CPU_ABI]: $TEST_NAME (`basename $PROGRAM`)"
            adb_var_shell_cmd "$DEVICE" "" "cd $DSTDIR && LD_LIBRARY_PATH=$DSTDIR ./$PROGRAM"
            if [ $? != 0 ] ; then
                dump "   ---> TEST FAILED!!"
            fi
            adb_var_shell_cmd "$DEVICE" "" "rm -f $DSTPATH"
            if [ -n "$DATAPATHS" ]; then
                for DATA in $(ls $DATAPATHS); do
                    adb_var_shell_cmd "$DEVICE" "" "rm -f $DSTDIR/`basename $DATA`"
                done
            fi
        done
        # Cleanup
        adb_var_shell_cmd "$DEVICE" "" rm -r $DSTDIR
    }

    for DIR in `ls -d $ROOTDIR/tests/device/*`; do
        if is_buildable $DIR; then
            build_device_test $DIR
        fi
    done

    # Do we have adb and any device connected here?
    # If not, we can't run our tests.
    #
    SKIP_TESTS=no
    if [ -z "$ADB_CMD" ] ; then
        dump "WARNING: No 'adb' in your path!"
        SKIP_TESTS=yes
    else
        # Get list of online devices, turn ' ' in device into '#'
        ADB_DEVICES=`$ADB_CMD devices | grep -v offline | awk 'NR>1 {gsub(/[ \t]+device$/,""); print;}' | sed '/^$/d' | sort | tr ' ' '#'`
        ADB_DEVICES=$(echo $ADB_DEVICES | tr '\n' ' ')
        log2 "ADB online devices (sorted): $ADB_DEVICES"
        ADB_DEVCOUNT=`echo "$ADB_DEVICES" | wc -w`
        if [ "$ADB_DEVCOUNT" = "0" ]; then
            dump "WARNING: No device connected to adb!"
            SKIP_TESTS=yes
        else
            ADB_DEVICES="$ADB_DEVICES "
            if [ -n "$ANDROID_SERIAL" ] ; then
                # Expect ANDROID_SERIAL is comma-delimited of one or more devices
                ANDROID_SERIAL=$(echo "$ANDROID_SERIAL" | tr ' ' '#')  # turn ' ' into '#'
                ANDROID_SERIAL=$(commas_to_spaces $ANDROID_SERIAL)
                for SERIAL in $ANDROID_SERIAL; do
                    if [ "$ADB_DEVICES" = "${ADB_DEVICES%$SERIAL *}" ] ; then
                        dump "WARNING: Device $SERIAL cannot be found or offline!"
                        SKIP_TESTS=yes
                    fi
                done
                if [ "$SKIP_TESTS" != "yes" ] ; then
                    ADB_DEVICES="$ANDROID_SERIAL"
                fi
            fi
        fi
    fi
    if [ "$SKIP_TESTS" = "yes" ] ; then
        dump "SKIPPING RUNNING TESTS ON DEVICE!"
    else
        AT_LEAST_CPU_ABI_MATCH=
        for DEVICE in $ADB_DEVICES; do
            # undo earlier ' '-to-'#' translation
            DEVICE=$(echo "$DEVICE" | tr '#' ' ')
            # get device CPU_ABI and CPU_ABI2, each may contain list of abi, comma-delimited.
            adb_var_shell_cmd "$DEVICE" CPU_ABI1 getprop ro.product.cpu.abi
            adb_var_shell_cmd "$DEVICE" CPU_ABI2 getprop ro.product.cpu.abi2
            CPU_ABIS="$CPU_ABI1,$CPU_ABI2"
            CPU_ABIS=$(commas_to_spaces $CPU_ABIS)
            if [ -n "$_NDK_TESTING_ALL_" ]; then
                if [ "$CPU_ABI1" = "armeabi-v7a" -o "$CPU_ABI2" = "armeabi-v7a" ]; then
                    CPU_ABIS="$CPU_ABIS armeabi-v7a-hard"
                fi
            fi
            if [ "$CPU_ABIS" = " " ]; then
              # Very old cupcake-based Android devices don't have these properties
              # defined. Fortunately, they are all armeabi-based.
              CPU_ABIS=armeabi
            fi
            log "CPU_ABIS=$CPU_ABIS"
            for CPU_ABI in $CPU_ABIS; do
                if [ "$ABI" = "default" -o "$ABI" = "$CPU_ABI" -o "$ABI" = "$(find_ndk_unknown_archs)" ] ; then
                    AT_LEAST_CPU_ABI_MATCH="yes"
                    for DIR in `ls -d $ROOTDIR/tests/device/*`; do
                        if is_buildable $DIR; then
                            log "Running device test on $DEVICE [$CPU_ABI]: $DIR"
                            run_device_test "$DEVICE" "$CPU_ABI" "$DIR" /data/local/tmp
                        fi
                    done
                fi
            done
        done
        if [ "$AT_LEAST_CPU_ABI_MATCH" != "yes" ] ; then
            dump "WARNING: No device matches ABI $ABI! SKIPPING RUNNING TESTS ON DEVICE!"
        fi
    fi
fi

dump "Cleaning up..."
if [ "$ABI" = "$(find_ndk_unknown_archs)" ]; then
  # Cleanup some intermediate files for testing
  run rm -rf $NDK/$GNUSTL_SUBDIR/$GCC_TOOLCHAIN_VERSION/libs/$ABI
  run rm -rf $NDK/$GABIXX_SUBDIR/libs/$ABI
  run rm -rf $NDK/$LIBPORTABLE_SUBDIR/libs/$ABI
fi
rm -rf $BUILD_DIR
dump "Done."