mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-22 12:53:47 +00:00
401 lines
12 KiB
Makefile
401 lines
12 KiB
Makefile
#-----------------------------------------------------------------------------#
|
|
# vim: ts=8 sw=8 noexpandtab ft=make
|
|
#-----------------------------------------------------------------------------#
|
|
# Mmake.common - shared Mmake variables and rules for the test directories.
|
|
#
|
|
# The including Mmakefile must set the following variables:
|
|
# THIS_DIR - the name of the directory containing tests
|
|
# (e.g. benchmarks, hard_coded/typeclasses)
|
|
# TESTS_DIR - the directory containing this file.
|
|
# SUBDIRS - the names of any subdirectories containing tests.
|
|
# PROGS - the names of the main modules of the test programs.
|
|
# TESTS- the names of the tests. For each test there must be targets
|
|
# test.depend, test.runtest and test.realclean, unless the test ends
|
|
# in `-nodepend', in which case the `-nodepend' suffix will be stripped
|
|
# off and the test.depend target is not required.
|
|
#
|
|
# If the variable ERROR_FILE is set, only the tests which failed in
|
|
# the test run which produced the specified error log file will be run.
|
|
#
|
|
#-----------------------------------------------------------------------------#
|
|
|
|
MAIN_TARGET = runtests
|
|
|
|
# Set up to test a particular workspace.
|
|
ifdef WORKSPACE
|
|
|
|
# Make sure we don't get the installed versions of the libraries.
|
|
LINK_STATIC = yes
|
|
include $(WORKSPACE)/Mmake.workspace
|
|
|
|
# Some tests only work if the workspace was compiled with `--use-subdirs'.
|
|
ifneq ($(shell [ -d $(WORKSPACE)/library/Mercury ] || echo no_subdir),no_subdir)
|
|
WORKSPACE_HAS_SUBDIRS = true
|
|
endif
|
|
|
|
endif
|
|
|
|
# Note: Mmake lets you override MCFLAGS for a particular file by setting
|
|
# MCFLAGS-foo. Similarly, you can override GRADEFLAGS for a particular
|
|
# file by setting both GRADEFLAGS-foo.
|
|
|
|
ifndef DIFF_OPTS
|
|
DIFF_OPTS=-c
|
|
endif
|
|
|
|
# Override this with `RUN_RECOMPILATION_TESTS=no' if your compiler
|
|
# cannot run smart recompilation. This is only necessary for compilers
|
|
# built in deep profiling grades, because smart recompilation uses
|
|
# exception handling, which does not yet work with deep profiling.
|
|
RUN_RECOMPILATION_TESTS=yes
|
|
|
|
# The Java interpreter.
|
|
JAVA=java
|
|
|
|
# We avoid picking up parameter settings from $HOME/.mdbrc that could cause
|
|
# spurious differences between the outputs of the debugger test cases
|
|
# and their expected outputs.
|
|
|
|
# We suppress the printing of the banner, because different workspaces
|
|
# may get different version numbers printed in it. This would otherwise be
|
|
# the source of irrelevant difference between the actual and expected outputs.
|
|
|
|
MDB = HOME=/nonexistent MERCURY_SUPPRESS_MDB_BANNER=yes \
|
|
MERCURY_DEBUGGER_INIT=$(TESTS_DIR)/mdbrc mdb
|
|
|
|
MDB_NOINIT = HOME=/nonexistent MERCURY_SUPPRESS_MDB_BANNER=yes \
|
|
MERCURY_DEBUGGER_INIT="" mdb
|
|
|
|
# Debugger test cases can standardize the reported event numbers and call
|
|
# sequence numbers by using $(MDB_STD) instead of $(MDB) on the command line.
|
|
|
|
MDB_STD = MERCURY_OPTIONS="$$MERCURY_OPTIONS -de" $(MDB)
|
|
|
|
MDB_STD_NOINIT = MERCURY_OPTIONS="$$MERCURY_OPTIONS -de" $(MDB_NOINIT)
|
|
|
|
PARAMS_MSG = in grade $(GRADE)
|
|
|
|
include $(TESTS_DIR)/DEFNS_FOR_TESTS
|
|
-include $(TESTS_DIR)/Mmake.params
|
|
|
|
MCFLAGS += --flags $(TESTS_DIR)/TESTS_FLAGS
|
|
ifdef WORKSPACE_FLAGS
|
|
MCFLAGS += --flags $(TESTS_DIR)/WS_FLAGS
|
|
endif
|
|
|
|
# Avoid trying to make this file with `mmc --make' if it doesn't exist.
|
|
$(TESTS_DIR)/Mmake.params: ;
|
|
|
|
#-----------------------------------------------------------------------------#
|
|
|
|
.PRECIOUS: %.res %.err_res %.out %.err
|
|
|
|
# If there is a `.inp' file, then we pipe that in as the command's input.
|
|
# Then we run the command, with stdout and stderr both redirected to the
|
|
# `.out' file. Finally if the command fails (returns non-zero exit status),
|
|
# we print out the contents of the `.out' file. We use `grep . $@ /dev/null'
|
|
# to print out the contents, because that precedes each line of output with
|
|
# the filename, which is helpful when running a parallel make.
|
|
%.out: %
|
|
{ [ -f $*.inp ] && cat $*.inp; } | ./$< > $@ 2>&1 || \
|
|
{ grep . $@ /dev/null; exit 1; }
|
|
|
|
# For some test cases, there is more than one valid output.
|
|
# We try matching the output with the `.exp' file, and if that doesn't succeed,
|
|
# and there are `.exp2', `.exp3', `.exp4' or `.exp5' files, then we try
|
|
# matching against those too. If none succeed, we put the shortest of the diffs
|
|
# into the `.res' file.
|
|
|
|
%.res: %.exp %.out
|
|
@echo "Comparing $*.out with $*.exp*,"
|
|
@echo " results in $@"
|
|
@-rm -f $@ $*.res[1-5]
|
|
@{ diff $(DIFF_OPTS) $*.exp $*.out > $*.res1 && \
|
|
echo "Matched $*.exp" && \
|
|
cp $*.res1 $@; } || \
|
|
{ test -f $*.exp2 && \
|
|
diff $(DIFF_OPTS) $*.exp2 $*.out > $*.res2 && \
|
|
echo "Matched $*.exp2" && \
|
|
cp $*.res2 $@; } || \
|
|
{ test -f $*.exp3 && \
|
|
diff $(DIFF_OPTS) $*.exp3 $*.out > $*.res3 && \
|
|
echo "Matched $*.exp3" && \
|
|
cp $*.res3 $@; } || \
|
|
{ test -f $*.exp4 && \
|
|
diff $(DIFF_OPTS) $*.exp4 $*.out > $*.res4 && \
|
|
echo "Matched $*.exp4" && \
|
|
cp $*.res4 $@; } || \
|
|
{ test -f $*.exp5 && \
|
|
diff $(DIFF_OPTS) $*.exp5 $*.out > $*.res5 && \
|
|
echo "Matched $*.exp5" && \
|
|
cp $*.res5 $@; } || \
|
|
{ shortest=`wc -l $*.res[1-5] | grep -v total | sort -n | \
|
|
head -1 | awk '{ print $$2; }' `; \
|
|
echo "** $*.out did not match the expected output"; \
|
|
echo "** (closest match was $$shortest)"; \
|
|
cp $$shortest $@; \
|
|
cat $@; \
|
|
exit 1; }
|
|
|
|
# For some test cases, there is more than one valid output.
|
|
# We try matching the output with the `.err_exp' file, and if that doesn't
|
|
# succeed, and there are `.err_exp2', `.err_exp3', `.err_exp4' or `.err_exp5'
|
|
# files, then we try matching against those too. If none succeed, we put
|
|
# the shortest of the diffs into the `.err_res' file.
|
|
|
|
%.err_res: %.err %.err_exp
|
|
@echo "Comparing $*.err with $*.err_exp*,"
|
|
@echo " results in $@"
|
|
@-rm -f $@ $*.err_res[1-5]
|
|
@{ diff $(DIFF_OPTS) $*.err_exp $*.err > $*.err_res1 && \
|
|
echo "Matched $*.err_exp" && \
|
|
cp $*.err_res1 $@; } || \
|
|
{ test -f $*.err_exp2 && \
|
|
diff $(DIFF_OPTS) $*.err_exp2 $*.err > $*.err_res2 && \
|
|
echo "Matched $*.err_exp2" && \
|
|
cp $*.err_res2 $@; } || \
|
|
{ test -f $*.err_exp3 && \
|
|
diff $(DIFF_OPTS) $*.err_exp3 $*.err > $*.err_res3 && \
|
|
echo "Matched $*.err_exp3" && \
|
|
cp $*.err_res3 $@; } || \
|
|
{ test -f $*.err_exp4 && \
|
|
diff $(DIFF_OPTS) $*.err_exp4 $*.err > $*.err_res4 && \
|
|
echo "Matched $*.err_exp4" && \
|
|
cp $*.err_res4 $@; } || \
|
|
{ test -f $*.err_exp5 && \
|
|
diff $(DIFF_OPTS) $*.err_exp5 $*.err > $*.err_res5 && \
|
|
echo "Matched $*.err_exp5" && \
|
|
cp $*.err_res5 $@; } || \
|
|
{ shortest=`wc -l $*.err_res[1-5] | grep -v total | sort -n | \
|
|
head -1 | awk '{ print $$2; }' `; \
|
|
echo "** $*.err did not match the expected error output"; \
|
|
echo "** (closest match was $$shortest)"; \
|
|
cp $$shortest $@; \
|
|
cat $@; \
|
|
exit 1; }
|
|
|
|
#-----------------------------------------------------------------------------#
|
|
|
|
MERCURY_MAIN_MODULES = $(PROGS)
|
|
|
|
ERROR_OUTPUT_FILE = runtests.errs
|
|
|
|
realclean_local: clean_logs clean_errors clean_mdbrc
|
|
|
|
clean_local: clean_out clean_res clean_zip clean_trace_counts clean_mdbrc_in
|
|
|
|
clean_mdbrc:
|
|
-rm -f mdbrc
|
|
|
|
clean_mdbrc_in:
|
|
-rm -f mdbrc.in
|
|
|
|
# XXX what is this target for??
|
|
clean_mc: clean_c clean_o clean_out clean_res
|
|
|
|
clean_out:
|
|
rm -f *.out
|
|
|
|
clean_res:
|
|
rm -f *.res* *.err_res*
|
|
|
|
clean_logs:
|
|
rm -f *.log
|
|
|
|
clean_errors:
|
|
rm -f $(ERROR_OUTPUT_FILE) FAILED_TESTS
|
|
|
|
clean_trace_counts:
|
|
rm -f *.pass1 *.pass2 *.pass3 *.fail
|
|
|
|
# Remove gzipped executables for tests which have failed.
|
|
ifeq ($(TESTS),)
|
|
clean_zip: ;
|
|
else
|
|
clean_zip:
|
|
for test in $(TESTS); do \
|
|
prog=`basename $$test -nodepend`; \
|
|
rm -f $$prog.gz $$prog.exe.gz; \
|
|
done
|
|
endif
|
|
|
|
# The `foo' targets make recursively make `foo' in all subdirectories.
|
|
# before making `foo_local' in the current directory.
|
|
|
|
.PHONY: runtests runtests_local runtests_subdirs
|
|
.PHONY: check check_local check_subdirs
|
|
.PHONY: realclean_subdirs clean_subdirs
|
|
|
|
realclean: realclean_subdirs
|
|
clean: clean_subdirs
|
|
all: runtests
|
|
|
|
check: runtests
|
|
check_local: runtests_local
|
|
check_subdirs: runtests_subdirs
|
|
|
|
# We want to run all the tests, even if some fail, so run the tests
|
|
# using `mmake -k'.
|
|
|
|
runtests:
|
|
+@if mmake -k runtests_local runtests_subdirs; then \
|
|
rm -f $(ERROR_OUTPUT_FILE); \
|
|
else \
|
|
for subdir in $(SUBDIRS) x; do \
|
|
if [ -f $$subdir/$(ERROR_OUTPUT_FILE) ]; then \
|
|
cat $$subdir/$(ERROR_OUTPUT_FILE) \
|
|
>> $(ERROR_OUTPUT_FILE); \
|
|
fi; \
|
|
if [ -f $$subdir/FAILED_TESTS ]; then \
|
|
sed "s@^@$$subdir/@" $$subdir/FAILED_TESTS \
|
|
>> FAILED_TESTS; \
|
|
fi; \
|
|
done; \
|
|
echo SOME TESTS FAILED: see FAILED_TESTS and $(ERROR_OUTPUT_FILE); \
|
|
exit 1; \
|
|
fi
|
|
|
|
# If the variable ERROR_FILE is set, only the tests which failed in
|
|
# the test run which produced the specified error log file will be run.
|
|
|
|
ifdef ERROR_FILE
|
|
ifndef TESTS_TO_RUN
|
|
TESTS_TO_RUN := $(shell awk '/^FAILED TEST/ { print $$3 }' $(ERROR_FILE))
|
|
export TESTS_TO_RUN
|
|
endif
|
|
endif
|
|
|
|
ifdef SPECIFIED_TESTS
|
|
THIS_DIR_TESTS_TO_RUN := $(SPECIFIED_TESTS)
|
|
else
|
|
ifdef FAILED_TESTS_ONLY
|
|
# FAILED_TESTS contains the names of failed tests from subdirectories
|
|
# as well as this directory. We want to run the failed tests from this
|
|
# directory only.
|
|
THIS_DIR_TESTS_TO_RUN := $(shell egrep -v / FAILED_TESTS)
|
|
else
|
|
ifdef TESTS_TO_RUN
|
|
THIS_DIR_TESTS_TO_RUN := $(shell echo $(patsubst $(THIS_DIR)/%,%,\
|
|
$(filter $(THIS_DIR)/%,$(TESTS_TO_RUN))) \
|
|
| tr ' ' '\n' | grep -v /)
|
|
else
|
|
THIS_DIR_TESTS_TO_RUN := $(strip $(TESTS))
|
|
endif
|
|
endif
|
|
endif
|
|
|
|
ifeq ($(THIS_DIR_TESTS_TO_RUN),)
|
|
|
|
runtests_local: ;
|
|
|
|
else
|
|
|
|
# Run multiple rm commands at once to speed up `mmake realclean' on
|
|
# slow NFS filesystems.
|
|
# XXX We used to do this, but if the tests are being run with parallel make,
|
|
# it results a huge number of processes being run because make's jobserver
|
|
# mechanism is disabled if `-j' is specified on the command line of a sub-make.
|
|
# RM_JFACTOR=-j10
|
|
RM_JFACTOR=
|
|
|
|
# Clean up after any previous test run.
|
|
.PHONY: start_runtests_local
|
|
start_runtests_local:
|
|
@echo STARTING tests in $(THIS_DIR) $(PARAMS_MSG) at `date`
|
|
@echo cleaning up the directory before the tests
|
|
+@if ls -lt | head -2 | egrep CLEAN > /dev/null 2>&1; then \
|
|
rm -f CLEAN > /dev/null 2>&1; \
|
|
else \
|
|
rm -f CLEAN > /dev/null 2>&1; \
|
|
mmake $(RM_JFACTOR) realclean_local > /dev/null 2>&1; \
|
|
rm -f *.d *.dep *.int *.int2 *.int3 > /dev/null 2>&1; \
|
|
rm -f *.date *.date3 *.opt *.optdate > /dev/null 2>&1; \
|
|
rm -f *.trans_opt *.trans_opt_date > /dev/null 2>&1; \
|
|
rm -f .mercury_trace_counts* > /dev/null 2>&1; \
|
|
rm -f .date* > /dev/null 2>&1; \
|
|
fi
|
|
@> FAILED_TESTS
|
|
|
|
# Run a single test, cleaning up if it succeeds, or producing a log file
|
|
# and gzipping the executable if the test fails.
|
|
|
|
.PHONY: %.log
|
|
%.log: start_runtests_local
|
|
+@test_full=$*; \
|
|
test=$(*:%-nodepend=%); \
|
|
params_msg="$(PARAMS_MSG)"; \
|
|
this_dir="$(THIS_DIR)"; \
|
|
tests_dir="$(TESTS_DIR)"; \
|
|
export test_full; \
|
|
export test; \
|
|
export params_msg; \
|
|
export this_dir; \
|
|
export tests_dir; \
|
|
$(TESTS_DIR)/run_one_test
|
|
|
|
# XXX In some test directories, several test cases depend on the same target,
|
|
# so the tests cannot be run in parallel, for two reasons, First, because
|
|
# the different invocations of mmake for different tests above may all attempt
|
|
# to create the target at the same time, and second, the invocation of
|
|
# `mmake $$test.realclean' after a test could be run before other users
|
|
# of the shared target are finished with it.
|
|
#
|
|
# Test directories which have such shared targets should set MAYBE_J1 to -j1.
|
|
# Other test directories should set it to the empty string.
|
|
|
|
TEST_LOGS = $(THIS_DIR_TESTS_TO_RUN:%=%.log)
|
|
runtests_local:
|
|
+@ \
|
|
mmake -k $(MAYBE_J1) $(TEST_LOGS); \
|
|
touch DUMMY.log; \
|
|
cat *.log > $(ERROR_OUTPUT_FILE); \
|
|
rm -f DUMMY.log; \
|
|
if [ ! -s $(ERROR_OUTPUT_FILE) ]; then \
|
|
echo "PASSED ALL TESTS in $(THIS_DIR) $(PARAMS_MSG)"; \
|
|
echo cleaning up the directory after the tests; \
|
|
mmake $(RM_JFACTOR) realclean_local > /dev/null 2>&1; \
|
|
rm core > /dev/null 2>&1; \
|
|
touch CLEAN; \
|
|
else \
|
|
echo "FAILED tests in $(THIS_DIR) $(PARAMS_MSG)"; \
|
|
exit 1; \
|
|
fi
|
|
|
|
endif # THIS_DIR_TESTS_TO_RUN != ""
|
|
|
|
ifeq ($(SUBDIRS),)
|
|
|
|
runtests_subdirs realclean_subdirs clean_subdirs : ;
|
|
|
|
else
|
|
|
|
RUNTESTS_IN_SUBDIRS=$(SUBDIRS:%=runtests_in_%)
|
|
runtests_subdirs: $(RUNTESTS_IN_SUBDIRS)
|
|
|
|
.PHONY: $(RUNTESTS_IN_SUBDIRS)
|
|
$(RUNTESTS_IN_SUBDIRS): runtests_in_%:
|
|
+if test -d $* ; then \
|
|
cd $* && mmake runtests ; \
|
|
else \
|
|
echo Could not run tests in directory $* ; \
|
|
echo Could not run tests in directory $* >> NOMAKE_DIRS ; \
|
|
fi
|
|
|
|
realclean_subdirs:
|
|
+succeeded=true; \
|
|
for dir in $(SUBDIRS); do \
|
|
(cd $$dir && mmake realclean) || succeeded=false; \
|
|
done
|
|
case $$succeeded in false) exit 1 ;; esac
|
|
|
|
clean_subdirs:
|
|
+succeeded=true; \
|
|
for dir in $(SUBDIRS); do \
|
|
(cd $$dir && mmake clean) || succeeded=false; \
|
|
done; \
|
|
case $$succeeded in false) exit 1 ;; esac
|
|
|
|
endif # SUBDIRS != ""
|
|
|
|
#-----------------------------------------------------------------------------#
|