ocaml
Version:
OCaml compiler packaged for esy
321 lines (283 loc) • 11.1 kB
Plain Text
#**************************************************************************
#* *
#* OCaml *
#* *
#* Xavier Clerc, SED, INRIA Rocquencourt *
#* *
#* Copyright 2010 Institut National de Recherche en Informatique et *
#* en Automatique. *
#* *
#* All rights reserved. This file is distributed under the terms of *
#* the GNU Lesser General Public License version 2.1, with the *
#* special exception on linking described in the file LICENSE. *
#* *
#**************************************************************************
.NOTPARALLEL:
BASEDIR := $(shell pwd)
ifneq "$(words |$(BASEDIR)|)" "1"
$(error The Testsuite does not handle spaces\
in the ocaml working directory path: $(BASEDIR))
endif
ROOTDIR = ..
include $(ROOTDIR)/Makefile.common
ifeq "$(UNIX_OR_WIN32)" "win32"
CYGPATH=cygpath -m
# Ensure that the test runners definitely use Cygwin's sort and not the
# Windows sort command
SORT=/usr/bin/sort
else
CYGPATH=echo
SORT=sort
endif
BASEDIR_HOST := $(shell $(CYGPATH) "$(BASEDIR)")
ROOTDIR_HOST := $(BASEDIR_HOST)/$(ROOTDIR)
OCAMLTESTDIR = $(BASEDIR_HOST)/$(DIR)/_ocamltest
failstamp := failure.stamp
TESTLOG ?= _log
ocamltest_directory := ../ocamltest
ocamltest_program := $(or \
$(wildcard $(ocamltest_directory)/ocamltest.opt$(EXE)),\
$(wildcard $(ocamltest_directory)/ocamltest$(EXE)))
ifeq "$(UNIX_OR_WIN32)" "unix"
ifeq "$(SYSTEM)" "cygwin"
find := /usr/bin/find
else # Non-cygwin Unix
find := find
endif
# $(IFS_LINE) for Unix simply clears IFS, so entire lines are read
IFS_LINE = IFS=''
else # Windows
find := /usr/bin/find
# On Windows, ocamltest will produce Windows line-endings (\r\n) and the final
# \r is kept by the shell. This can either be stripped with tr -d '\r' but we
# can avoid the additional process by instead setting IFS in the while loop to
# be the CR character (i.e. treat \r as a field delimiter).
# The dance with $(CR_CHAR) is because POSIX doesn't provide a way to write \r
# in a string.
export CR_CHAR := $(shell printf "\r")
IFS_LINE = IFS="$$CR_CHAR"
endif
ifeq "$(ocamltest_program)" ""
ocamltest = $(error ocamltest not found in $(ocamltest_directory))
else
ocamltest := SORT=$(SORT) MAKE=$(MAKE) $(ocamltest_program)
endif
# PROMOTE is only meant to be used internally in recursive calls;
# users should call the 'promote' target explicitly.
PROMOTE =
ifeq "$(PROMOTE)" ""
OCAMLTEST_PROMOTE_FLAG =
else
OCAMLTEST_PROMOTE_FLAG = -promote
endif
# KEEP_TEST_DIR_ON_SUCCESS should be set by the user (to a non-empty value)
# if they want to pass the -keep-test-dir-on-success option to ocamltest,
# to preserve test data of successful tests.
KEEP_TEST_DIR_ON_SUCCESS ?=
ifeq "$(KEEP_TEST_DIR_ON_SUCCESS)" ""
OCAMLTEST_KEEP_TEST_DIR_ON_SUCCESS_FLAG =
else
OCAMLTEST_KEEP_TEST_DIR_ON_SUCCESS_FLAG = -keep-test-dir-on-success
endif
TIMEOUT ?= 600 # 10 minutes
# SHOW_TIMINGS should be set by the user (to a non-empty value) if they want
# the timings for each test file to be included in the log
SHOW_TIMINGS ?=
OCAMLTEST_SHOW_TIMINGS_FLAG =
ifneq "$(SHOW_TIMINGS)" ""
ifeq "$(lib_unix)" "true"
OCAMLTEST_SHOW_TIMINGS_FLAG = -show-timings
endif
endif
OCAMLTESTFLAGS = \
-timeout $(TIMEOUT) \
$(OCAMLTEST_PROMOTE_FLAG) \
$(OCAMLTEST_KEEP_TEST_DIR_ON_SUCCESS_FLAG) \
$(OCAMLTEST_SHOW_TIMINGS_FLAG)
# Make sure USE_RUNTIME is defined
USE_RUNTIME ?=
ifneq ($(USE_RUNTIME),)
# Check USE_RUNTIME value
ifeq ($(findstring $(USE_RUNTIME),d i),)
$(error If set, USE_RUNTIME must be equal to "d" (debug runtime) \
or "i" (instrumented runtime))
endif
# When using the debug or instrumented runtime,
# set the runtime's verbosity to 0 by default
export OCAMLRUNPARAM?=v=0
endif
default:
@echo "Available targets:"
@echo " all launch all tests"
@echo " all-foo launch all tests beginning with foo"
@echo " parallel launch all tests using GNU parallel"
@echo " parallel-foo launch all tests beginning with foo using \
GNU parallel"
@echo " one (TEST|DIR|LIST)=x launch the specified tests ..."
@echo " promote (TEST|DIR|LIST)=x promote the output for the specified \
tests ..."
@echo " ... TEST=f ... the single test in the file f"
@echo " ... DIR=d ... the tests located in the directory d"
@echo " ... LIST=f ... the tests listed in the file f (one \
per line)"
@echo " clean delete generated files"
@echo " report print the report for the last execution"
@echo
@echo "all*, parallel* and list can automatically re-run failed test"
@echo "directories if MAX_TESTSUITE_DIR_RETRIES permits"
@echo "(default value = $(MAX_TESTSUITE_DIR_RETRIES))"
@echo
@echo "Set the environment variable USE_RUNTIME to \"d\" or \"i\" to run"
@echo "the tests with the debug or the instrumented runtime."
all:
@$(MAKE) --no-print-directory new-without-report
@$(MAKE) --no-print-directory report
new-without-report:
@rm -f $(failstamp)
@($(ocamltest) -find-test-dirs tests | while $(IFS_LINE) read -r dir; do \
echo Running tests from \'$$dir\' ... ; \
$(MAKE) exec-ocamltest DIR="$$dir" \
OCAMLTESTENV=""; \
done || echo outer loop >> $(failstamp)) 2>&1 | tee $(TESTLOG)
@$(MAKE) check-failstamp
check-failstamp:
@if [ -f $(failstamp) ]; then \
echo 'Unexpected error in the test infrastructure:'; \
cat $(failstamp); \
rm $(failstamp); \
exit 1; \
fi
all-%:
@for dir in tests/$**; do \
$(MAKE) --no-print-directory exec-one DIR=$$dir; \
done 2>&1 | tee $(TESTLOG)
@$(MAKE) report
# The targets below use GNU parallel to parallelize tests
# 'make all' and 'make parallel' should be equivalent
#
# parallel uses specific logic to make sure the output of the commands
# run in parallel are not mangled. By default, it will reproduce
# the output of each completed command atomically, in order of completion.
#
# With the --keep-order option, we ask it to save the completed output
# and replay them in invocation order instead. In theory this costs
# a tiny bit of performance, but I could not measure any difference.
# In theory again, the reporting logic works fine with test outputs
# coming in in arbitrary order (so we should not need --keep-order),
# but keeping the output deterministic is guaranteed to make
# someone's life easier at least once in the future.
#
# Finally, note that the command we run has a 2>&1 redirection, as
# in the other make targets. If we removed the quoting around
# "$(MAKE) ... 2>&1", the rediction would apply to the complete output
# of parallel, and have a slightly different behavior: by default parallel
# cleanly separates the stdout and stderr output of each completed command,
# printing stderr first then stdout second (for each command).
# I chose to keep the previous behavior exactly unchanged,
# but the demangling separation is arguably nicer behavior that we might
# want to implement at the exec-one level to also have it in the 'all' target.
# If make has been invoked with "-j n", pass this on to GNU parallel. parallel
# does not support -j without an argument, hence the double-filter. Note that
# GNU make normalises -j in $(MAKEFLAGS) so it will either be -j alone or -jn
# (i.e. with no space).
J_ARGUMENT = $(filter-out -j,$(filter -j%,$(MAKEFLAGS)))
parallel-%:
@echo | parallel >/dev/null 2>/dev/null \
|| (echo "Unable to run the GNU parallel tool;";\
echo "You should install it before using the parallel* targets.";\
exit 1)
@echo | parallel --gnu --no-notice >/dev/null 2>/dev/null \
|| (echo "Your 'parallel' tool seems incompatible with GNU parallel.";\
echo "This target requires GNU parallel.";\
exit 1)
@for dir in tests/$**; do echo $$dir; done \
| parallel --gnu --no-notice --keep-order $(J_ARGUMENT) \
"$(MAKE) --no-print-directory exec-one DIR={} 2>&1" \
| tee $(TESTLOG)
@$(MAKE) report
parallel: parallel-*
list:
@if [ -z "$(FILE)" ]; \
then echo "No value set for variable 'FILE'."; \
exit 1; \
fi
@$(MAKE) --no-print-directory one LIST="$(FILE)"
one:
@case "$(words $(DIR) $(LIST) $(TEST))" in \
0) echo 'No value set for variable DIR, LIST or TEST'>&2; exit 1;; \
1) exit 0;; \
*) echo 'Please specify exactly one of DIR, LIST or TEST'>&2; exit 1;; \
esac
@if [ -n '$(DIR)' ] && [ ! -d '$(DIR)' ]; then \
echo "Directory '$(DIR)' does not exist."; exit 1; \
fi
@if [ -n '$(TEST)' ] && [ ! -e '$(TEST)' ]; then \
echo "Test '$(TEST)' does not exist."; exit 1; \
fi
@if [ -n '$(LIST)' ] && [ ! -e '$(LIST)' ]; then \
echo "File '$(LIST)' does not exist."; exit 1; \
fi
@if [ -n '$(DIR)' ] ; then \
$(MAKE) --no-print-directory exec-one DIR=$(DIR) \
2>&1 | tee $(TESTLOG).one ; \
fi
@if [ -n '$(TEST)' ] ; then \
TERM=dumb $(OCAMLTESTENV) $(ocamltest) $(OCAMLTESTFLAGS) $(TEST) \
2>&1 | tee $(TESTLOG).one; fi
@if [ -n '$(LIST)' ] ; then \
while IFS='' read -r LINE; do \
$(MAKE) --no-print-directory exec-one DIR=$$LINE ; \
done < $$LIST 2>&1 | tee $(TESTLOG).one ; \
fi
@$(MAKE) check-failstamp
@$(MAKE) TESTLOG=$(TESTLOG).one report
exec-one:
@$(ocamltest) -find-test-dirs $(DIR) | while $(IFS_LINE) read -r dir; do \
echo "Running tests from '$$dir' ..."; \
$(MAKE) exec-ocamltest DIR="$$dir" \
OCAMLTESTENV="OCAMLTESTDIR=$(OCAMLTESTDIR)"; \
done
exec-ocamltest:
@if [ -z "$(DIR)" ]; then exit 1; fi
@if [ ! -d "$(DIR)" ]; then exit 1; fi
@($(ocamltest) -list-tests $(DIR) | while $(IFS_LINE) read -r testfile; do \
TERM=dumb $(OCAMLTESTENV) \
$(ocamltest) $(OCAMLTESTFLAGS) "$(DIR)/$$testfile" || \
echo " ... testing '$$testfile' => unexpected error"; \
done) || echo directory "$(DIR)" >>$(failstamp)
clean-one:
@if [ ! -f $(DIR)/Makefile ]; then \
for dir in $(DIR)/*; do \
if [ -d $$dir ]; then \
$(MAKE) clean-one DIR=$$dir; \
fi; \
done; \
else \
cd $(DIR) && $(MAKE) TERM=dumb BASEDIR=$(BASEDIR) clean; \
fi
promote:
@$(MAKE) one PROMOTE=true
clean:
find . -name '*_ocamltest*' | xargs rm -rf
rm -f $(failstamp)
distclean: clean
rm -f _log*
report:
@if [ ! -f $(TESTLOG) ]; then echo "No $(TESTLOG) file."; exit 1; fi
@$(AWK) -f ./summarize.awk < $(TESTLOG)