cannam@147: #! /usr/bin/env bash cannam@147: cannam@147: set -euo pipefail cannam@147: cannam@147: doit() { cannam@147: echo "@@@@ $@" cannam@147: "$@" cannam@147: } cannam@147: cannam@147: QUICK= cannam@147: cannam@147: PARALLEL=$(nproc 2>/dev/null || echo 1) cannam@147: cannam@147: while [ $# -gt 0 ]; do cannam@147: case "$1" in cannam@147: -j* ) cannam@147: PARALLEL=${1#-j} cannam@147: ;; cannam@147: test ) cannam@147: ;; # nothing cannam@147: quick ) cannam@147: QUICK=quick cannam@147: ;; cannam@147: caffeinate ) cannam@147: # Re-run preventing sleep. cannam@147: shift cannam@147: exec caffeinate -ims $0 $@ cannam@147: ;; cannam@147: tmpdir ) cannam@147: # Clone to a temp directory. cannam@147: if [ "$#" -lt 2 ]; then cannam@147: echo "usage: $0 tmpdir NAME [COMMAND]" >&2 cannam@147: exit 1 cannam@147: fi cannam@147: DIR=/tmp/$2 cannam@147: shift 2 cannam@147: if [ -e $DIR ]; then cannam@147: if [ "${DIR/*..*}" = "" ]; then cannam@147: echo "NO DO NOT PUT .. IN THERE IT'S GOING TO GO IN /tmp AND I'M GONNA DELETE IT" >&2 cannam@147: exit 1 cannam@147: fi cannam@147: if [ ! -e "$DIR/super-test.sh" ]; then cannam@147: echo "$DIR exists and it doesn't look like one of mine." >&2 cannam@147: exit 1 cannam@147: fi cannam@147: # make distcheck leaves non-writable files when it fails, so we need to chmod to be safe. cannam@147: chmod -R +w $DIR cannam@147: rm -rf $DIR cannam@147: fi cannam@147: git clone . $DIR cannam@147: cd $DIR cannam@147: exec ./super-test.sh "$@" cannam@147: ;; cannam@147: remote ) cannam@147: if [ "$#" -lt 2 ]; then cannam@147: echo "usage: $0 remote HOST [COMMAND]" >&2 cannam@147: exit 1 cannam@147: fi cannam@147: HOST=$2 cannam@147: shift 2 cannam@147: echo "=========================================================================" cannam@147: echo "Pushing code to $HOST..." cannam@147: echo "=========================================================================" cannam@147: BRANCH=$(git rev-parse --abbrev-ref HEAD) cannam@147: ssh $HOST '(chmod -fR +w tmp-test-capnp || true) && rm -rf tmp-test-capnp && mkdir tmp-test-capnp && git init tmp-test-capnp' cannam@147: git push ssh://$HOST/~/tmp-test-capnp "$BRANCH:test" cannam@147: ssh $HOST "cd tmp-test-capnp && git checkout test" cannam@147: exec ssh $HOST "cd tmp-test-capnp && ./super-test.sh $@ && cd .. && rm -rf tmp-test-capnp" cannam@147: ;; cannam@147: compiler ) cannam@147: if [ "$#" -lt 2 ]; then cannam@147: echo "usage: $0 compiler CXX_NAME" >&2 cannam@147: exit 1 cannam@147: fi cannam@147: export CXX="$2" cannam@147: shift cannam@147: ;; cannam@147: clang ) cannam@147: export CXX=clang++ cannam@147: ;; cannam@147: gcc-4.9 ) cannam@147: export CXX=g++-4.9 cannam@147: ;; cannam@147: gcc-4.8 ) cannam@147: export CXX=g++-4.8 cannam@147: ;; cannam@147: gcc-4.7 ) cannam@147: export CXX=g++-4.7 cannam@147: ;; cannam@147: mingw ) cannam@147: if [ "$#" -ne 2 ]; then cannam@147: echo "usage: $0 mingw CROSS_HOST" >&2 cannam@147: exit 1 cannam@147: fi cannam@147: CROSS_HOST=$2 cannam@147: cannam@147: cd c++ cannam@147: test -e configure || doit autoreconf -i cannam@147: test ! -e Makefile || (echo "ERROR: Directory unclean!" >&2 && false) cannam@147: cannam@147: export WINEPATH='Z:\usr\'"$CROSS_HOST"'\lib;Z:\usr\lib\gcc\'"$CROSS_HOST"'\6.3-win32;Z:'"$PWD"'\.libs' cannam@147: cannam@147: doit ./configure --host="$CROSS_HOST" --disable-shared CXXFLAGS='-static-libgcc -static-libstdc++' cannam@147: cannam@147: doit make -j$PARALLEL check cannam@147: doit make distclean cannam@147: rm -f *-mingw.exe cannam@147: exit 0 cannam@147: ;; cannam@147: android ) cannam@147: # To install Android SDK: cannam@147: # - Download command-line tools: https://developer.android.com/studio/index.html#command-tools cannam@147: # - Run $SDK_HOME/tools/bin/sdkmanager platform-tools 'platforms;android-25' 'system-images;android-25;google_apis;armeabi-v7a' emulator 'build-tools;25.0.2' ndk-bundle cannam@147: # - Run $SDK_HOME/tools/bin/avdmanager create avd -n capnp -k 'system-images;android-25;google_apis;armeabi-v7a' -b google_apis/armeabi-v7a cannam@147: # - Run $SDK_HOME/ndk-bundle/build/tools/make_standalone_toolchain.py --arch arm --api 24 --install-dir $TOOLCHAIN_HOME cannam@147: if [ "$#" -ne 4 ]; then cannam@147: echo "usage: $0 android SDK_HOME TOOLCHAIN_HOME CROSS_HOST" >&2 cannam@147: exit 1 cannam@147: fi cannam@147: SDK_HOME=$2 cannam@147: TOOLCHAIN_HOME=$3 cannam@147: CROSS_HOST=$4 cannam@147: cannam@147: cd c++ cannam@147: test -e configure || doit autoreconf -i cannam@147: test ! -e Makefile || (echo "ERROR: Directory unclean!" >&2 && false) cannam@147: doit ./configure --disable-shared cannam@147: doit make -j$PARALLEL capnp capnpc-c++ cannam@147: cannam@147: cp capnp capnp-host cannam@147: cp capnpc-c++ capnpc-c++-host cannam@147: cannam@147: export PATH="$TOOLCHAIN_HOME/bin:$PATH" cannam@147: doit make distclean cannam@147: doit ./configure --host="$CROSS_HOST" --with-external-capnp --disable-shared CXXFLAGS='-pie -fPIE' CAPNP=./capnp-host CAPNPC_CXX=./capnpc-c++-host cannam@147: cannam@147: doit make -j$PARALLEL cannam@147: doit make -j$PARALLEL capnp-test cannam@147: cannam@147: echo "Starting emulator..." cannam@147: trap 'kill $(jobs -p)' EXIT cannam@147: # TODO(someday): Speed up with KVM? Then maybe we won't have to skip fuzz tests? cannam@147: $SDK_HOME/emulator/emulator -avd capnp -no-window & cannam@147: $SDK_HOME/platform-tools/adb 'wait-for-device' cannam@147: echo "Waiting for localhost to be resolvable..." cannam@147: doit $SDK_HOME/platform-tools/adb shell 'while ! ping -c 1 localhost > /dev/null 2>&1; do sleep 1; done' cannam@147: # TODO(cleanup): With 'adb shell' I find I cannot put files anywhere, so I'm using 'su' a cannam@147: # lot here. There is probably a better way. cannam@147: doit $SDK_HOME/platform-tools/adb shell 'su 0 tee /data/capnp-test > /dev/null' < capnp-test cannam@147: doit $SDK_HOME/platform-tools/adb shell 'su 0 chmod a+rx /data/capnp-test' cannam@147: doit $SDK_HOME/platform-tools/adb shell 'cd /data && CAPNP_SKIP_FUZZ_TEST=1 su 0 /data/capnp-test && echo ANDROID_""TESTS_PASSED' | tee android-test.log cannam@147: grep -q ANDROID_TESTS_PASSED android-test.log cannam@147: cannam@147: doit make distclean cannam@147: rm -f capnp-host capnpc-c++-host cannam@147: exit 0 cannam@147: ;; cannam@147: cmake ) cannam@147: cd c++ cannam@147: rm -rf cmake-build cannam@147: mkdir cmake-build cannam@147: cd cmake-build cannam@147: doit cmake -G "Unix Makefiles" .. cannam@147: doit make -j$PARALLEL check cannam@147: exit 0 cannam@147: ;; cannam@147: exotic ) cannam@147: echo "=========================================================================" cannam@147: echo "MinGW 64-bit" cannam@147: echo "=========================================================================" cannam@147: "$0" mingw x86_64-w64-mingw32 cannam@147: echo "=========================================================================" cannam@147: echo "MinGW 32-bit" cannam@147: echo "=========================================================================" cannam@147: "$0" mingw i686-w64-mingw32 cannam@147: echo "=========================================================================" cannam@147: echo "Android" cannam@147: echo "=========================================================================" cannam@147: "$0" android /home/kenton/android-sdk-linux /home/kenton/android-24 arm-linux-androideabi cannam@147: echo "=========================================================================" cannam@147: echo "CMake" cannam@147: echo "=========================================================================" cannam@147: "$0" cmake cannam@147: exit 0 cannam@147: ;; cannam@147: clean ) cannam@147: rm -rf tmp-staging cannam@147: cd c++ cannam@147: if [ -e Makefile ]; then cannam@147: doit make maintainer-clean cannam@147: fi cannam@147: rm -f capnproto-*.tar.gz samples/addressbook samples/addressbook.capnp.c++ \ cannam@147: samples/addressbook.capnp.h cannam@147: exit 0 cannam@147: ;; cannam@147: help ) cannam@147: echo "usage: $0 [COMMAND]" cannam@147: echo "commands:" cannam@147: echo " test Runs tests (the default)." cannam@147: echo " clang Runs tests using Clang compiler." cannam@147: echo " gcc-4.7 Runs tests using gcc-4.7." cannam@147: echo " gcc-4.8 Runs tests using gcc-4.8." cannam@147: echo " gcc-4.9 Runs tests using gcc-4.9." cannam@147: echo " remote HOST Runs tests on HOST via SSH." cannam@147: echo " mingw Cross-compiles to MinGW and runs tests using WINE." cannam@147: echo " android Cross-compiles to Android and runs tests using emulator." cannam@147: echo " clean Delete temporary files that may be left after failure." cannam@147: echo " help Prints this help text." cannam@147: exit 0 cannam@147: ;; cannam@147: * ) cannam@147: echo "unknown command: $1" >&2 cannam@147: echo "try: $0 help" >&2 cannam@147: exit 1 cannam@147: ;; cannam@147: esac cannam@147: shift cannam@147: done cannam@147: cannam@147: # Build optimized builds because they catch more problems, but also enable debugging macros. cannam@147: # Enable lots of warnings and make sure the build breaks if they fire. Disable strict-aliasing cannam@147: # because GCC warns about code that I know is OK. Disable sign-compare because I've fixed more cannam@147: # sign-compare warnings than probably all other warnings combined and I've never seen it flag a cannam@147: # real problem. Disable unused parameters because it's stupidly noisy and never a real problem. cannam@147: # Enable expensive release-gating tests. cannam@147: export CXXFLAGS="-O2 -DDEBUG -Wall -Wextra -Werror -Wno-strict-aliasing -Wno-sign-compare -Wno-unused-parameter -DCAPNP_EXPENSIVE_TESTS=1" cannam@147: cannam@147: STAGING=$PWD/tmp-staging cannam@147: cannam@147: rm -rf "$STAGING" cannam@147: mkdir "$STAGING" cannam@147: mkdir "$STAGING/bin" cannam@147: mkdir "$STAGING/lib" cannam@147: export PATH=$STAGING/bin:$PATH cannam@147: export LD_LIBRARY_PATH=$STAGING/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} cannam@147: export PKG_CONFIG_PATH=$STAGING/lib/pkgconfig cannam@147: cannam@147: if [ "$QUICK" = quick ]; then cannam@147: echo "************************** QUICK TEST ***********************************" cannam@147: fi cannam@147: cannam@147: echo "=========================================================================" cannam@147: echo "Building c++" cannam@147: echo "=========================================================================" cannam@147: cannam@147: # Apple now aliases gcc to clang, so probe to find out what compiler we're really using. cannam@147: if (${CXX:-g++} -dM -E -x c++ /dev/null 2>&1 | grep -q '__clang__'); then cannam@147: IS_CLANG=yes cannam@147: DISABLE_OPTIMIZATION_IF_GCC= cannam@147: else cannam@147: IS_CLANG=no cannam@147: DISABLE_OPTIMIZATION_IF_GCC=-O0 cannam@147: fi cannam@147: cannam@147: if [ $IS_CLANG = yes ]; then cannam@147: # Don't fail out on this ridiculous "argument unused during compilation" warning. cannam@147: export CXXFLAGS="$CXXFLAGS -Wno-error=unused-command-line-argument" cannam@147: else cannam@147: # GCC emits uninitialized warnings all over and they seem bogus. We use valgrind to test for cannam@147: # uninitialized memory usage later on. GCC 4 also emits strange bogus warnings with cannam@147: # -Wstrict-overflow, so we disable it. cannam@147: CXXFLAGS="$CXXFLAGS -Wno-maybe-uninitialized -Wno-strict-overflow" cannam@147: fi cannam@147: cannam@147: cd c++ cannam@147: doit autoreconf -i cannam@147: doit ./configure --prefix="$STAGING" cannam@147: doit make -j$PARALLEL check cannam@147: cannam@147: if [ $IS_CLANG = no ]; then cannam@147: # Verify that generated code compiles with pedantic warnings. Make sure to treat capnp headers cannam@147: # as system headers so warnings in them are ignored. cannam@147: doit ${CXX:-g++} -isystem src -std=c++11 -fno-permissive -pedantic -Wall -Wextra -Werror \ cannam@147: -c src/capnp/test.capnp.c++ -o /dev/null cannam@147: fi cannam@147: cannam@147: echo "=========================================================================" cannam@147: echo "Testing c++ install" cannam@147: echo "=========================================================================" cannam@147: cannam@147: doit make install cannam@147: cannam@147: test "x$(which capnp)" = "x$STAGING/bin/capnp" cannam@147: test "x$(which capnpc-c++)" = "x$STAGING/bin/capnpc-c++" cannam@147: cannam@147: cd samples cannam@147: cannam@147: doit capnp compile -oc++ addressbook.capnp -I"$STAGING"/include --no-standard-import cannam@147: doit ${CXX:-g++} -std=c++11 addressbook.c++ addressbook.capnp.c++ -o addressbook \ cannam@147: $CXXFLAGS $(pkg-config --cflags --libs capnp) cannam@147: echo "@@@@ ./addressbook (in various configurations)" cannam@147: ./addressbook write | ./addressbook read cannam@147: ./addressbook dwrite | ./addressbook dread cannam@147: rm addressbook addressbook.capnp.c++ addressbook.capnp.h cannam@147: cannam@147: doit capnp compile -oc++ calculator.capnp -I"$STAGING"/include --no-standard-import cannam@147: doit ${CXX:-g++} -std=c++11 calculator-client.c++ calculator.capnp.c++ -o calculator-client \ cannam@147: $CXXFLAGS $(pkg-config --cflags --libs capnp-rpc) cannam@147: doit ${CXX:-g++} -std=c++11 calculator-server.c++ calculator.capnp.c++ -o calculator-server \ cannam@147: $CXXFLAGS $(pkg-config --cflags --libs capnp-rpc) cannam@147: rm -f /tmp/capnp-calculator-example-$$ cannam@147: ./calculator-server unix:/tmp/capnp-calculator-example-$$ & cannam@147: sleep 0.1 cannam@147: ./calculator-client unix:/tmp/capnp-calculator-example-$$ cannam@147: kill %+ cannam@147: wait %+ || true cannam@147: rm calculator-client calculator-server calculator.capnp.c++ calculator.capnp.h /tmp/capnp-calculator-example-$$ cannam@147: cannam@147: cd .. cannam@147: cannam@147: if [ "$QUICK" = quick ]; then cannam@147: doit make maintainer-clean cannam@147: rm -rf "$STAGING" cannam@147: exit 0 cannam@147: fi cannam@147: cannam@147: echo "=========================================================================" cannam@147: echo "Testing --with-external-capnp" cannam@147: echo "=========================================================================" cannam@147: cannam@147: doit make distclean cannam@147: doit ./configure --prefix="$STAGING" --disable-shared \ cannam@147: --with-external-capnp CAPNP=$STAGING/bin/capnp cannam@147: doit make -j$PARALLEL check cannam@147: cannam@147: echo "=========================================================================" cannam@147: echo "Testing --disable-reflection" cannam@147: echo "=========================================================================" cannam@147: cannam@147: doit make distclean cannam@147: doit ./configure --prefix="$STAGING" --disable-shared --disable-reflection \ cannam@147: --with-external-capnp CAPNP=$STAGING/bin/capnp cannam@147: doit make -j$PARALLEL check cannam@147: doit make distclean cannam@147: cannam@147: # Test 32-bit build now while we have $STAGING available for cross-compiling. cannam@147: if [ "x`uname -m`" = "xx86_64" ]; then cannam@147: echo "=========================================================================" cannam@147: echo "Testing 32-bit build" cannam@147: echo "=========================================================================" cannam@147: cannam@147: if [[ "`uname`" =~ CYGWIN ]]; then cannam@147: # It's just not possible to run cygwin32 binaries from within cygwin64. cannam@147: cannam@147: # Build as if we are cross-compiling, using the capnp we installed to $STAGING. cannam@147: doit ./configure --prefix="$STAGING" --disable-shared --host=i686-pc-cygwin \ cannam@147: --with-external-capnp CAPNP=$STAGING/bin/capnp cannam@147: doit make -j$PARALLEL cannam@147: doit make -j$PARALLEL capnp-test.exe cannam@147: cannam@147: # Expect a cygwin32 sshd to be listening at localhost port 2222, and use it cannam@147: # to run the tests. cannam@147: doit scp -P 2222 capnp-test.exe localhost:~/tmp-capnp-test.exe cannam@147: doit ssh -p 2222 localhost './tmp-capnp-test.exe && rm tmp-capnp-test.exe' cannam@147: cannam@147: doit make distclean cannam@147: cannam@147: elif [ "x${CXX:-g++}" != "xg++-4.8" ]; then cannam@147: doit ./configure CXX="${CXX:-g++} -m32" --disable-shared cannam@147: doit make -j$PARALLEL check cannam@147: doit make distclean cannam@147: fi cannam@147: fi cannam@147: cannam@147: echo "=========================================================================" cannam@147: echo "Testing c++ uninstall" cannam@147: echo "=========================================================================" cannam@147: cannam@147: doit ./configure --prefix="$STAGING" cannam@147: doit make uninstall cannam@147: cannam@147: echo "=========================================================================" cannam@147: echo "Testing c++ dist" cannam@147: echo "=========================================================================" cannam@147: cannam@147: doit make -j$PARALLEL distcheck cannam@147: doit make distclean cannam@147: rm capnproto-*.tar.gz cannam@147: cannam@147: if [ "x`uname`" = xLinux ]; then cannam@147: echo "=========================================================================" cannam@147: echo "Testing generic Unix (no Linux-specific features)" cannam@147: echo "=========================================================================" cannam@147: cannam@147: doit ./configure --disable-shared CXXFLAGS="$CXXFLAGS -DKJ_USE_FUTEX=0 -DKJ_USE_EPOLL=0" cannam@147: doit make -j$PARALLEL check cannam@147: doit make distclean cannam@147: fi cannam@147: cannam@147: echo "=========================================================================" cannam@147: echo "Testing with -fno-rtti and -fno-exceptions" cannam@147: echo "=========================================================================" cannam@147: cannam@147: # GCC miscompiles capnpc-c++ when -fno-exceptions and -O2 are specified together. The cannam@147: # miscompilation happens in one specific inlined call site of Array::dispose(), but this method cannam@147: # is inlined in hundreds of other places without issue, so I have no idea how to narrow down the cannam@147: # bug. Clang works fine. So, for now, we disable optimizations on GCC for -fno-exceptions tests. cannam@147: cannam@147: doit ./configure --disable-shared CXXFLAGS="$CXXFLAGS -fno-rtti" cannam@147: doit make -j$PARALLEL check cannam@147: doit make distclean cannam@147: doit ./configure --disable-shared CXXFLAGS="$CXXFLAGS -fno-exceptions $DISABLE_OPTIMIZATION_IF_GCC" cannam@147: doit make -j$PARALLEL check cannam@147: doit make distclean cannam@147: doit ./configure --disable-shared CXXFLAGS="$CXXFLAGS -fno-rtti -fno-exceptions $DISABLE_OPTIMIZATION_IF_GCC" cannam@147: doit make -j$PARALLEL check cannam@147: cannam@147: # Valgrind is currently "experimental and mostly broken" on OSX and fails to run the full test cannam@147: # suite, but I have it installed because it did manage to help me track down a bug or two. Anyway, cannam@147: # skip it on OSX for now. cannam@147: if [ "x`uname`" != xDarwin ] && which valgrind > /dev/null; then cannam@147: doit make distclean cannam@147: cannam@147: echo "=========================================================================" cannam@147: echo "Testing with valgrind" cannam@147: echo "=========================================================================" cannam@147: cannam@147: doit ./configure --disable-shared CXXFLAGS="-g" cannam@147: doit make -j$PARALLEL cannam@147: doit make -j$PARALLEL capnp-test cannam@147: # Running the fuzz tests under Valgrind is a great thing to do -- but it takes cannam@147: # some 40 minutes. So, it needs to be done as a separate step of the release cannam@147: # process, perhaps along with the AFL tests. cannam@147: CAPNP_SKIP_FUZZ_TEST=1 doit valgrind --leak-check=full --track-fds=yes --error-exitcode=1 ./capnp-test cannam@147: fi cannam@147: cannam@147: doit make maintainer-clean cannam@147: cannam@147: rm -rf "$STAGING"