Chris@1: #!/bin/bash Chris@1: Chris@6: # Run this from the top-level vamp-build-and-test directory Chris@6: Chris@1: ## Things to test: Chris@1: ## the plugin builds! Chris@1: ## plugin loads Chris@1: ## passes vamp-plugin-tester tests Chris@1: ## does not export any unnecessary symbols Chris@1: ## has valid .cat and .n3 Chris@1: Chris@19: mydir=$(dirname "$0") Chris@19: case "$mydir" in /*);; *) mydir=$(pwd)/"$mydir";; esac Chris@19: Chris@22: do_rebuild="" Chris@14: Chris@22: usage() { Chris@22: echo Chris@22: echo "Usage: $0 [-c] [ ...]" Chris@22: echo Chris@31: echo " one of native, linux32, linux64, mingw32, mingw64, osx32, osx64" Chris@22: echo " -c build from clean" Chris@22: echo " directory to build (default is all of them)" Chris@22: echo Chris@22: echo "Platform usually should match the platform you are running this" Chris@22: echo "script on, unless you have a cross-compile toolset installed and" Chris@31: echo "this script knows how to run it. The special platform 'native'" Chris@31: echo "tries to guess the currently running platform." Chris@22: echo Chris@22: exit 2 Chris@22: } Chris@21: Chris@31: platform_arg="$1" Chris@31: Chris@31: if [ "$platform_arg" = "native" ]; then Chris@31: case `uname -a` in Chris@31: Linux*x86_64*) platform_arg=linux64;; Chris@31: Linux*) platform_arg=linux32;; Chris@31: Darwin*) platform_arg=osx64;; Chris@31: CYG*) platform_arg=mingw32;; Chris@31: MINGW*) platform_arg=mingw32;; Chris@31: esac Chris@31: fi Chris@31: Chris@31: case "$platform_arg" in Chris@22: linux32) Chris@22: platform=linux Chris@22: bits=32 Chris@22: toolprefix= Chris@22: pluginext=.so Chris@22: hostwrapper= Chris@22: hostext= Chris@38: valgrind=valgrind Chris@22: archflags= Chris@42: identpattern='ELF 32' Chris@22: ;; Chris@22: linux64) Chris@22: platform=linux Chris@22: bits=64 Chris@22: toolprefix= Chris@22: pluginext=.so Chris@22: hostwrapper= Chris@22: hostext= Chris@38: valgrind=valgrind Chris@22: archflags= Chris@42: identpattern='ELF 64' Chris@22: ;; Chris@22: mingw32) Chris@22: platform=mingw Chris@22: bits=32 Chris@22: toolprefix=i686-w64-mingw32- Chris@22: pluginext=.dll Chris@22: hostwrapper=wine Chris@22: hostext=.exe Chris@38: valgrind= Chris@22: archflags= Chris@36: identpattern='PE32.*386.*Windows' Chris@22: ;; Chris@22: mingw64) Chris@22: platform=mingw Chris@22: bits=64 Chris@22: toolprefix=x86_64-w64-mingw32- Chris@22: pluginext=.dll Chris@22: hostwrapper=wine Chris@22: hostext=.exe Chris@38: valgrind= Chris@22: archflags= Chris@36: identpattern='not known yet' ##!!! Chris@22: ;; Chris@22: osx32) Chris@22: platform=osx Chris@22: bits=32 Chris@22: toolprefix= Chris@22: pluginext=.dylib Chris@22: hostwrapper= Chris@22: hostext= Chris@38: valgrind= Chris@22: archflags="-arch i386" Chris@40: identpattern='Mach-O .*i386' Chris@22: ;; Chris@22: osx64) Chris@22: platform=osx Chris@22: bits=64 Chris@22: toolprefix= Chris@22: pluginext=.dylib Chris@22: hostwrapper= Chris@22: hostext= Chris@38: valgrind= Chris@26: # This is a difficult choice for various reasons... have to ponder Chris@26: archflags="-mmacosx-version-min=10.6 -arch x86_64 -arch i386" Chris@40: identpattern='Mach-O 64-bit .*x86_64' Chris@22: ;; Chris@22: esac; Chris@22: Chris@22: shift Chris@22: Chris@22: if [ -z "$platform" ]; then Chris@22: usage Chris@22: else Chris@22: echo "(Platform is $platform, $bits bits)" Chris@22: fi Chris@22: Chris@22: if [ t"$1" = t"-c" ]; then Chris@22: echo "(Building from clean)" Chris@22: do_rebuild=yes Chris@22: shift Chris@22: fi Chris@15: Chris@19: depincdir="$mydir"/../DEPENDENCIES/$platform$bits/include Chris@19: deplibdir="$mydir"/../DEPENDENCIES/$platform$bits/lib Chris@19: Chris@19: depincdir_generic="$mydir"/../DEPENDENCIES/generic/include Chris@1: Chris@11: plugindirs="$@" Chris@11: if [ -z "$plugindirs" ]; then Chris@11: plugindirs=$(cat .hgsub | grep -v vamp-plugin-sdk | grep -v vamp-plugin-tester | awk '{ print $1; }') Chris@11: fi Chris@11: Chris@6: reportdir="REPORTS/$platform$bits" Chris@6: mkdir -p "$reportdir" || exit 1 Chris@6: Chris@9: built="/tmp/built.$$.txt" Chris@9: testfailed="/tmp/testfailed.$$.txt" Chris@19: envcheckfailed="/tmp/envcheckfailed.$$.txt" Chris@9: notbuilt="/tmp/notbuilt.$$.txt" Chris@19: trap 'rm -f "$built" "$envcheckfailed" "$testfailed" "$notbuilt"' 0 Chris@19: touch "$built" "$envcheckfailed" "$testfailed" "$notbuilt" Chris@9: Chris@4: configure() { Chris@4: dir="$1" Chris@4: if [ -f "$dir/configure" ] ; then Chris@6: ( cd "$dir" ; ./configure ) 2>&1 | tee "$reportdir/$dir.configure.txt" Chris@4: fi Chris@4: } Chris@4: Chris@1: find_makefile() { Chris@1: dir="$1" Chris@9: for f in \ Chris@19: build/$platform$bits/Makefile.$platform$bits \ Chris@13: build/$platform/Makefile.$platform$bits \ Chris@19: build/$platform$bits/Makefile.$platform \ Chris@19: build/$platform$bits/Makefile \ Chris@19: build/Makefile.$platform$bits \ Chris@19: Makefile.$platform$bits \ Chris@13: build/$platform/Makefile.$platform \ Chris@13: build/$platform/Makefile \ Chris@13: build/Makefile.$platform \ Chris@9: Makefile.$platform \ Chris@13: Makefile ; do Chris@1: if [ -f "$dir/$f" ]; then Chris@1: echo $f Chris@1: break Chris@1: fi Chris@1: done Chris@1: } Chris@1: Chris@13: configure_maybe() { Chris@13: dir="$1" Chris@43: if [ t"$do_rebuild" = t"yes" ]; then Chris@43: configure "$dir" Chris@43: else Chris@43: mfile=$(find_makefile "$dir") Chris@43: if [ -z "$mfile" ]; then Chris@43: configure "$dir" Chris@43: fi Chris@13: fi Chris@13: } Chris@13: Chris@19: target_for() { Chris@19: dir="$1" Chris@19: if grep -q "^$dir: " METADATA/maketarget.txt ; then Chris@19: grep "^$dir: " METADATA/maketarget.txt | head -1 | sed 's/^[^:]*: //' Chris@19: fi Chris@19: } Chris@19: Chris@44: logfile_for() { Chris@44: activity="$1" Chris@44: dir="$2" Chris@44: echo "$reportdir/$dir.$activity.txt" Chris@44: } Chris@44: Chris@4: build() { Chris@4: dir="$1" Chris@44: log=$(logfile_for build "$dir") Chris@1: if configure_maybe "$dir"; then Chris@1: mfile=$(find_makefile "$dir") Chris@1: if [ -n "$mfile" ]; then Chris@19: target=$(target_for "$dir") Chris@19: TOOLPREFIX="$toolprefix" \ Chris@21: CXXFLAGS="-I${depincdir} -I${depincdir_generic} -I../vamp-plugin-sdk" \ Chris@21: LDFLAGS="-L${deplibdir} -L../vamp-plugin-sdk" \ Chris@21: ARCHFLAGS="$archflags" \ Chris@19: make -C "$dir" -f "$mfile" $target 2>&1 | \ Chris@44: tee "$log" Chris@9: return ${PIPESTATUS[0]} Chris@1: else Chris@44: echo "Failed to find a Makefile in $dir!" | tee "$log" Chris@4: return 1 Chris@1: fi Chris@1: fi Chris@4: } Chris@4: Chris@4: rebuild() { Chris@4: dir="$1" Chris@44: log=$(logfile_for build "$dir") Chris@13: if configure_maybe "$dir"; then Chris@4: mfile=$(find_makefile "$dir") Chris@4: if [ -n "$mfile" ]; then Chris@32: if make -C "$dir" -f "$mfile" clean; then Chris@32: build "$dir" Chris@32: else Chris@44: echo "Failed to 'make clean' in $dir!" | tee "$log" Chris@32: return 1 Chris@32: fi Chris@4: else Chris@44: echo "Failed to find a Makefile in $dir!" | tee "$log" Chris@4: return 1 Chris@4: fi Chris@4: fi Chris@4: } Chris@4: Chris@22: build_or_rebuild() { Chris@22: dir="$1" Chris@22: if [ -n "$do_rebuild" ]; then Chris@22: rebuild "$dir" Chris@22: else Chris@22: build "$dir" Chris@22: fi Chris@22: } Chris@22: Chris@19: have_plugin() { Chris@19: dir="$1" Chris@44: log=$(logfile_for build "$dir") Chris@19: for x in "$dir/"*"$pluginext"; do Chris@19: if [ -f "$x" ]; then Chris@36: if file "$x" | grep -q "$identpattern" ; then Chris@36: return 0 Chris@36: else Chris@44: echo "Plugin $x exists, but fails to match file type for correct platform (file type is: `file $x`, expected pattern is: $identpattern)" | tee "$log" Chris@36: return 1 Chris@36: fi Chris@19: fi Chris@19: done Chris@19: return 1 Chris@19: } Chris@19: Chris@19: is_nondeterministic() { Chris@19: plugin_id="$1" Chris@19: grep -q "^$id\$" METADATA/nondeterministic.txt Chris@19: } Chris@19: Chris@44: plugin_ids_in() { Chris@5: dir="$1" Chris@40: # can't use sed to remove \r from DOS line endings -- BSD and GNU Chris@44: # vary in how they interpret \r escape -- so we use perl for that... Chris@44: VAMP_PATH="$dir" $hostwrapper \ Chris@44: vamp-plugin-sdk/host/vamp-simple-host$hostext --list-ids | \ Chris@44: grep '^vamp:' | \ Chris@44: sed 's/^vamp://' | \ Chris@44: perl -p -e 's/\r//g' Chris@44: } Chris@44: Chris@44: run_tester() { Chris@44: ##!!! todo: timeout if the plugin takes too long and report as failure Chris@44: dir="$1" Chris@44: log=$(logfile_for test "$dir") Chris@44: ids=$(plugin_ids_in "$dir") Chris@44: cat /dev/null > "$log" Chris@6: if [ -z "$ids" ]; then Chris@6: echo Chris@44: echo "No plugins reported to test in $dir" | tee -a "$log" Chris@44: echo "$dir" >> "$testfailed" Chris@44: return 1 Chris@44: fi Chris@44: good=yes Chris@44: for id in $ids; do Chris@44: extra="" Chris@44: if is_nondeterministic "$id"; then Chris@44: extra="-n" Chris@44: fi Chris@44: echo "Running command: VAMP_PATH=\"$dir\" $hostwrapper vamp-plugin-tester/vamp-plugin-tester$hostext \"$extra\" \"$id\"" | tee -a "$log" Chris@44: if ( VAMP_PATH="$dir" $hostwrapper vamp-plugin-tester/vamp-plugin-tester$hostext "$extra" "$id" 2>&1 | tee -a "$log" ; exit ${PIPESTATUS[0]} ) ; then Chris@44: echo "OK" Chris@44: else Chris@44: echo Chris@44: echo "Tester failed for id $id: running again with valgrind (if available) and verbose for a report..." | tee -a "$log" Chris@44: VAMP_PATH="$dir" $valgrind $hostwrapper vamp-plugin-tester/vamp-plugin-tester$hostext -v "$extra" "$id" 2>&1 | tee -a "$log" Chris@44: good=no Chris@44: fi Chris@44: done Chris@44: if [ "$good" != "yes" ]; then Chris@23: echo "$dir" >> "$testfailed" Chris@6: return 1 Chris@5: else Chris@44: return 0 Chris@5: fi Chris@5: } Chris@5: Chris@44: public_symbols_in() { Chris@9: lib="$1" Chris@23: # nm -g prints global symbols in both OS/X and GNU tools, but Chris@23: # printing only global *defined* symbols is harder. In GNU it is Chris@23: # nm -g --defined-only; the OS/X docs suggest nm -gu should work, Chris@23: # but it doesn't. What I think will work with both is simply Chris@23: # grepping out the undefineds: Chris@23: "$toolprefix"nm -g "$lib" | grep -v ' U ' | awk '{ print $3; }' Chris@9: } Chris@9: Chris@44: env_test_exports() { Chris@44: dir="$1" Chris@44: log=$(logfile_for envtest "$dir") Chris@44: good=yes Chris@44: for lib in "$dir"/*"$pluginext"; do Chris@44: if [ ! -f "$lib" ]; then Chris@44: # This should only happen if the glob was not expanded at all Chris@44: echo "NOTE: no library found in $dir?" | tee -a "$log" Chris@44: good=no Chris@44: break Chris@44: fi Chris@44: echo Chris@44: echo "Testing for exported symbols in $lib..." Chris@44: if public_symbols_in "$lib" | grep -q vampGetPluginDescriptor; then Chris@44: others=`public_symbols_in "$lib" | grep -v vampGetPluginDescriptor` Chris@44: if [ -n "$others" ]; then Chris@44: count=`echo "$others" | wc -l` Chris@44: echo "ERROR: library $lib exports $count extra symbols in addition to vampGetPluginDescriptor" | tee -a "$log" Chris@44: good=no Chris@44: else Chris@44: echo "GOOD: library $lib only exports vampGetPluginDescriptor" | tee -a "$log" Chris@44: fi Chris@44: else Chris@44: echo "NOTE: found library $lib that is not a Vamp plugin library" | tee -a "$log" Chris@44: fi Chris@44: done Chris@44: [ "$good" = "yes" ] Chris@44: } Chris@44: Chris@44: env_test_stdout() { Chris@44: dir="$1" Chris@44: log=$(logfile_for envtest "$dir") Chris@44: good=yes Chris@44: ids=$(VAMP_PATH="$dir" $hostwrapper vamp-plugin-sdk/host/vamp-simple-host$hostext --list-ids); Chris@44: echo Chris@44: echo "Testing for any unwanted output to stdout..." Chris@44: for id in $ids; do Chris@44: case "$id" in Chris@44: vamp:*) ;; Chris@44: *) Chris@44: echo "ERROR: plugin in $dir prints to stdout as it runs: found text $id (should use stderr for debug logging to avoid mixing with batch output stream)" | tee -a "$log" Chris@44: good=no Chris@44: ;; Chris@44: esac Chris@44: done Chris@44: [ "$good" = "yes" ] Chris@44: } Chris@44: Chris@44: env_test_cat() { Chris@44: dir="$1" Chris@44: log=$(logfile_for envtest "$dir") Chris@44: good=yes Chris@44: first=yes Chris@44: echo Chris@44: echo "Testing some details of .cat files..." Chris@44: for catfile in "$dir"/*".cat"; do Chris@44: if [ ! -f "$catfile" ]; then Chris@44: # This should only happen if the glob was not expanded at all Chris@44: echo "ERROR: no .cat file found in $dir" | tee -a "$log" Chris@44: good=no Chris@44: break Chris@44: fi Chris@44: if [ "$first" = "yes" ]; then Chris@44: first=no Chris@44: else Chris@44: echo "NOTE: multiple .cat files found in $dir" | tee -a "$log" Chris@44: fi Chris@44: done Chris@44: if [ "$good" = "yes" ]; then Chris@44: excess=$(VAMP_PATH="$dir" $hostwrapper \ Chris@44: vamp-plugin-sdk/host/vamp-simple-host$hostext --list-ids | \ Chris@44: grep '^vamp:' | \ Chris@44: cat "$dir"/*".cat" - | \ Chris@44: sort | \ Chris@44: perl -e 'while (<>) { Chris@44: if (/::/) { Chris@44: print if (!defined $plugid or !/^$plugid/); Chris@44: } else { Chris@44: chomp; $plugid = $_; Chris@44: } Chris@44: }') Chris@44: if [ -n "$excess" ]; then Chris@44: echo "ERROR: excess definitions in .cat file? $excess" | tee -a "$log" Chris@44: good=no Chris@44: else Chris@44: echo "GOOD: no excess definitions in .cat files" | tee -a "$log" Chris@44: fi Chris@44: fi Chris@44: [ "$good" = "yes" ] Chris@44: } Chris@44: Chris@44: env_test_ttl() { Chris@44: dir="$1" Chris@44: log=$(logfile_for envtest "$dir") Chris@44: good=yes Chris@44: first=yes Chris@44: echo Chris@44: echo "Testing existence of RDF files..." Chris@44: for ttlfile in "$dir"/*.{n3,ttl}; do Chris@44: if [ ! -f "$ttlfile" ]; then Chris@44: # Because we have two different extensions, this isn't an Chris@44: # error as it is with e.g. .cat (because one or the other Chris@44: # of .n3 and .ttl almost certainly won't exist). But if we Chris@44: # drop out the bottom and first is still true, then we Chris@44: # know neither matched. Chris@44: : Chris@44: elif [ "$first" = "yes" ]; then Chris@44: first=no Chris@44: else Chris@44: echo "NOTE: multiple .ttl or .n3 files found in $dir" | tee -a "$log" Chris@44: fi Chris@44: done Chris@45: if [ "$first" = "yes" ]; then Chris@45: good=no; Chris@45: else Chris@45: echo "GOOD: found one or more .ttl or .n3 files (we don't actually test their validity or content here though)" | tee -a "$log" Chris@45: fi Chris@45: [ "$good" = "yes" ] Chris@45: } Chris@45: Chris@45: env_test_accompaniments() { Chris@45: dir="$1" Chris@45: log=$(logfile_for envtest "$dir") Chris@45: good=yes Chris@45: if ! ls -1 "$dir" | egrep -qi "^readme(.txt|)$"; then Chris@45: echo "ERROR: no README file found" | tee -a "$log" Chris@45: good=no Chris@45: fi Chris@45: if ! ls -1 "$dir" | egrep -qi "^(copying|licen[cs]e)(.txt|)$"; then Chris@45: echo "ERROR: no COPYING or LICEN[CS]E file found" | tee -a "$log" Chris@45: good=no Chris@45: fi Chris@45: if ! ls -1 "$dir" | egrep -qi "^citation(.txt|)$"; then Chris@45: echo "NOTE: no CITATION file found" | tee -a "$log" Chris@45: fi Chris@44: [ "$good" = "yes" ] Chris@44: } Chris@44: Chris@9: run_environmental_tests() { Chris@9: dir="$1" Chris@44: log=$(logfile_for envtest "$dir") Chris@44: allgood=yes Chris@44: cat /dev/null > "$log" Chris@45: for test in exports stdout cat ttl accompaniments; do Chris@44: "env_test_$test" "$dir" || allgood=no Chris@9: done Chris@44: if [ "$allgood" != "yes" ]; then Chris@19: echo "$dir" >> "$envcheckfailed" Chris@44: return 1 Chris@44: else Chris@44: return 0 Chris@19: fi Chris@9: } Chris@4: Chris@22: if ! build_or_rebuild "vamp-plugin-sdk"; then Chris@5: echo "Failed to build Vamp plugin SDK!" Chris@5: exit 1 Chris@5: fi Chris@5: Chris@26: # Ensure we can only link statically against these Chris@26: for x in vamp-hostsdk vamp-sdk; do Chris@26: for y in dylib dll so; do Chris@26: rm -f "vamp-plugin-sdk/lib$x.$y" Chris@26: rm -f "vamp-plugin-sdk/$x.$y" Chris@26: done Chris@26: done Chris@26: Chris@22: if ! build_or_rebuild "vamp-plugin-tester"; then Chris@5: echo "Failed to build Vamp plugin tester!" Chris@5: exit 1 Chris@5: fi Chris@5: Chris@11: for dir in $plugindirs ; do Chris@31: dir=${dir%/*} Chris@4: echo Chris@4: echo "Processing: $dir" Chris@31: if [ ! -d "$dir" ]; then Chris@31: echo "Directory $dir not found!" Chris@31: echo "$dir" >> "$notbuilt" Chris@31: elif build_or_rebuild "$dir"; then Chris@19: if have_plugin "$dir" ; then Chris@19: echo "$dir" >> "$built" Chris@19: run_tester "$dir" Chris@19: run_environmental_tests "$dir" Chris@19: else Chris@44: log=$(logfile_for build "$dir") Chris@44: echo "Build apparently succeeded, but no resulting plugin(s) found, or plugin(s) have wrong file type or platform" | tee -a "$log" Chris@19: echo "$dir" >> "$notbuilt" Chris@19: fi Chris@4: else Chris@6: echo "$dir" >> "$notbuilt" Chris@4: fi Chris@44: slog=$(logfile_for summary "$dir") Chris@44: cat /dev/null > "$slog" Chris@1: done Chris@1: Chris@4: echo Chris@19: echo "** Successfully built, tested, and checked:" Chris@44: cat "$built" | while read dir; do Chris@44: slog=$(logfile_for summary "$dir") Chris@44: if ! grep -q "^$dir\$" "$testfailed"; then Chris@44: if ! grep -q "^$dir\$" "$envcheckfailed"; then Chris@44: echo "$dir" Chris@44: echo "$dir: Success" >> "$slog" Chris@32: fi Chris@6: fi Chris@6: done | sort Chris@4: Chris@4: echo Chris@19: echo "** Failed tests:" Chris@44: cat "$testfailed" | sort | uniq | while read dir; do Chris@44: slog=$(logfile_for summary "$dir") Chris@44: echo "$dir" Chris@44: echo "$dir: Built successfully, but failed tests" >> "$slog" Chris@6: done Chris@5: Chris@5: echo Chris@19: echo "** Failed environmental checks:" Chris@44: cat "$envcheckfailed" | sort | uniq | while read dir; do Chris@44: slog=$(logfile_for summary "$dir") Chris@44: echo "$dir" Chris@44: echo "$dir: Built successfully, but failed environmental checks" >> "$slog" Chris@19: done Chris@19: Chris@19: echo Chris@4: echo "** Failed to build:" Chris@44: cat "$notbuilt" | sort | while read dir; do Chris@44: slog=$(logfile_for summary "$dir") Chris@44: echo "$dir" Chris@44: echo "$dir: Failed to build" >> "$slog" Chris@6: done Chris@4: Chris@4: echo