# Wendelin.core | Instructions to build & test # Copyright (C) 2014-2021 Nexedi SA and Contributors. # Kirill Smelkov <kirr@nexedi.com> # # This program is free software: you can Use, Study, Modify and Redistribute # it under the terms of the GNU General Public License version 3, or (at your # option) any later version, as published by the Free Software Foundation. # # You can also Link and Combine this program with other software covered by # the terms of any of the Free Software licenses or any of the Open Source # Initiative approved licenses and Convey the resulting work. Corresponding # source of such a combination shall include the source code for all other # software used. # # This program is distributed WITHOUT ANY WARRANTY; without even the implied # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # See COPYING file for full licensing terms. # See https://www.nexedi.com/licensing for rationale and options. all : PYTHON ?= python PYTEST ?= $(PYTHON) -m pytest PYBENCH ?= $(PYTHON) -m golang.cmd.pybench VALGRIND?= valgrind GO ?= go # use the same C compiler as python # (for example it could be `gcc -m64` on a 32bit userspace) CC := $(shell $(PYTHON) -c "from __future__ import print_function; \ import sysconfig as _; \ print(_.get_config_vars()['CC'])") ifeq ($(CC),) $(error "Cannot defermine py-CC") endif all : bigfile/_bigfile.so wcfs/wcfs ccan_config := 3rdparty/ccan/config.h bigfile/_bigfile.so : $(ccan_config) FORCE $(PYTHON) setup.py build_dso --inplace $(PYTHON) setup.py ll_build_ext --inplace wcfs/wcfs: FORCE cd wcfs && $(GO) build FORCE : # TODO add FORCE? $(ccan_config): 3rdparty/ccan/Makefile $(MAKE) -C $(@D) $(@F) # if there is no ccan/Makefile - ccan submodule has not been initialized # if there is - it is ok, as that Makefile does not need to be rebuilt 3rdparty/ccan/Makefile: @echo 'E: 3rdparty/ccan submodule not initialized' @echo 'E: please do `git submodule update --init`' @false # -*- testing -*- # XXX dup with setup.py CPPFLAGS:= -Iinclude -I3rdparty/ccan -I3rdparty/include CFLAGS := -g -Wall -D_GNU_SOURCE -std=gnu99 -fplan9-extensions \ -Wno-declaration-after-statement \ -Wno-error=declaration-after-statement \ # XXX hack ugly LOADLIBES=lib/bug.c lib/utils.c 3rdparty/ccan/ccan/tap/tap.c TESTS := $(patsubst %.c,%,$(wildcard bigfile/tests/test_*.c)) test : test.t test.go test.wcfs test.py test.fault test.asan test.tsan test.vgmem test.vghel test.vgdrd # TODO move XFAIL markers into *.c # Before calling our SIGSEGV handler, Memcheck first reports "invalid read|write" error. # A solution could be to tell memcheck via VALGRIND_MAKE_MEM_DEFINED that VMA # address space is ok to access _before_ handling pagefault. # http://valgrind.org/docs/manual/mc-manual.html#mc-manual.clientreqs XFAIL_bigfile/tests/test_virtmem.vgmemrun := y # extract what goes after RUNWITH: marker from command source, or empty if no marker runwith = $(shell grep -oP '(?<=^// RUNWITH: ).*' $(basename $1).c) # run a test, not failing if failure is expected xrun = $1 $(if $(XFAIL_$@),|| echo "($@ - expected failure)") XRUN< = $(call xrun,$(call runwith,$<) $<) LINKC = $(LINK.c) $< $(LOADLIBES) $(LDLIBS) -o $@ # tests without instrumentation test.t : $(TESTS:%=%.trun) %.trun : %.t $(XRUN<) %.t : %.c $(ccan_config) FORCE $(LINKC) # test with AddressSanitizer test.asan: $(TESTS:%=%.asanrun) %.asanrun: %.asan $(XRUN<) %.asan : CFLAGS += -fsanitize=address %.asan : %.c $(ccan_config) $(LINKC) # test with ThreadSanitizer # TSAN works only on x86_64 # (can't rely on `uname -m` - could have 32bit userspace on 64bit kernel) ifneq ($(shell $(CPP) -dM - </dev/null | grep __x86_64__),) test.tsan: $(TESTS:%=%.tsanrun) %.tsanrun: %.tsan $(XRUN<) else test.tsan: @echo "Skip $@ # ThreadSanitizer does not support \"`$(CC) -v 2>&1 | grep '^Target:'`\"" endif %.tsan : CFLAGS += -fsanitize=thread -pie -fPIC %.tsan : %.c $(ccan_config) $(LINKC) # run valgrind so errors affect exit code # TODO stop on first error # (http://stackoverflow.com/questions/16345555/is-there-a-way-to-stop-valgrind-on-the-first-error-it-finds # but it still asks interactively, whether to "run debugger") VALGRINDRUN = $(VALGRIND) --error-exitcode=1 # to track memory access on each instruction (e.g. without this reads from NULL are ignored) # XXX why =allregs-at-mem-access is not sufficient? # see "Handling of Signals" in http://valgrind.org/docs/manual/manual-core.html # without this option our SIGSEGV handler is not always called # see also: https://bugs.kde.org/show_bug.cgi?id=124035 VALGRINDRUN += --vex-iropt-register-updates=allregs-at-each-insn # like XRUN< for valgrind vgxrun = $(call xrun,$(call runwith,$2) $(VALGRINDRUN) $1 $2) # test with valgrind/memcheck test.vgmem: $(TESTS:%=%.vgmemrun) %.vgmemrun: %.t $(call vgxrun,--tool=memcheck, $<) # test with valgrind/helgrind test.vghel: $(TESTS:%=%.vghelrun) %.vghelrun: %.t $(call vgxrun,--tool=helgrind, $<) # test with valgrind/drd test.vgdrd: $(TESTS:%=%.vgdrdrun) %.vgdrdrun: %.t $(call vgxrun,--tool=drd, $<) # run python tests PYTEST_IGNORE := --ignore=3rdparty --ignore=build --ignore=t # wcfs unit-tests test.wcfs : bigfile/_bigfile.so wcfs/wcfs $(PYTEST) wcfs/ # unit/functional tests for whole wendelin.core test.py : bigfile/_bigfile.so wcfs/wcfs $(PYTEST) $(PYTEST_IGNORE) --ignore=wcfs # test.py via Valgrind (very slow) test.py.vghel: bigfile/_bigfile.so wcfs/wcfs $(call vgxrun,--tool=helgrind, $(PYTEST) $(PYTEST_IGNORE)) test.py.drd: bigfile/_bigfile.so wcfs/wcfs $(call vgxrun,--tool=drd, $(PYTEST) $(PYTEST_IGNORE)) # run go tests test.go : cd wcfs && $(GO) test -count=1 ./... # -count=1 disables tests caching # test pagefault for double/real faults - it should crash tfault := bigfile/tests/tfault # XXX FAULTS extraction fragile FAULTS := $(shell grep '{"fault.*"' $(tfault).c | sed 's/"/ /g' |awk '{print $$2}') test.fault : $(FAULTS:%=%.tfault) $(tfault).t: CFLAGS += -rdynamic # so that backtrace_symbols works %.tfault : $(tfault).t t/tfault-run $< $* $(shell grep '{"$*"' $(tfault).c | awk '{print $$NF}') # -*- benchmarking -*- BENCHV.C:= $(patsubst %.c,%,$(wildcard bigfile/tests/bench_*.c)) bench : bench.t bench.py bench.t : $(BENCHV.C:%=%.trun) bench.py: bigfile/_bigfile.so wcfs/wcfs $(PYBENCH) --count=3 --forked $(PYTEST_IGNORE)