mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-16 18:03:36 +00:00
The predicates that read a Mercury source, interface or optimization file
used to return four things:
- the name of the file (for callers who specified only a module name),
- the timestamp, if requested and available,
- a parse tree, and
- a representation of any errors (sets of error categories, and error_specs).
However, these four things were not independent. Some combinations of their
values were not allowed, but (a) there was no documentation of what these
combinations were, and (b) code that processed these four things had to be
prepared to handle illegal as well as legal combinations.
This diff makes these predicates return only one result, which contains
- all four of the above things, when the file could be opened, but
- only the file name and a representation of the error if the file
could not be opened,
- only the file name and a representation of *no* errors, if the caller
asked the predicate to read the file only if its timestamp did not match
a specified value, and it does match that value.
We use a somewhat modified version of an existing type, have_read_module,
for this. It is modified both by including information that its users
now need that they did not need before, and shortening the names of its
function symbols, which now occur in many more places than before.
compiler/read_modules.m:
Make the change to the output arguments described above.
Making this change requires having the affected predicates deal with
the case where we either cannot find or cannot open the relevant file.
(The two are effectively the same thing in this module, since we search
by attempting to open.) Passing that task off to parse_modules.m
was always inelegant.
Simplify the have_read_module_map type by making the key always
be a module_name. We deleted the only use of have_read_module_maps
with another kind of key a while ago.
Delete some no-longer-needed predicates.
compiler/parse_module.m:
Delete the code dealing with the absence of a file stream to parse.
Replace code that used to construct dummy parse trees with code
that simply returns *no* parse tree if the file could be opened
but could not be read, or if its timestamp indicated that reading it
would have been redundant.
Delete some utility predicates moved to parse_error.m, so that
read_modules.m could also use them.
Fix comment rot.
compiler/parse_error.m:
Add some utility predicates for use by read_modules.m and parse_module.m.
compiler/deps_map.m:
Conform to the changes above.
Document the dubious effects of an old design decision.
Fix a misleading predicate name.
Fix comment rot.
compiler/grab_modules.m:
Conform to the changes above.
Some predicates used to return parse trees that could be dummies.
Change them to return just the parts of the parse tree that the
caller was interested in, which was usually a tiny part, and which
can be constructed trivially even when we don't have a parse tree.
Delete an unneeded type.
compiler/recompilation.check.m:
Conform to the changes above.
Represent the operations we need to test version numbers in interface files
as a typeclass, and rewrite the checking operation in terms of that
typeclass, with one instance for each kind of interface file.
Move some repeated code into a predicate.
Shorten some long names.
compiler/mercury_compile_main.m:
Conform to the changes above.
Break up a large predicate.
compiler/generate_dep_d_files.m:
compiler/make.module_dep_file.m:
compiler/write_module_interface_files.m:
Conform to the changes above.
compiler/prog_item.m:
Delete the parse_tree_some_int type. The change in recompilation.check.m,
deleted its last few uses there, and allowed the deletion of the last
uses in read_modules.m.
tests/recompilation/two_module_debug:
Extend this script to help deal with problems in all stages of the
execution of a test case, since that was required while debugging
this diff.
Document which parts of this script correspond to which parts of
two_module_test.
tests/recompilation/test_functions:
tests/recompilation/two_module_test:
Simplify the logic of the main test function, by testing a condition
once instead of three times.
Specify whether a recompilation test is expected to succeed or fail
directly, using alternatives that model a bespoke type, instead
of alternatives that mimic a boolean answer to a question, which
does not help readers who don't remember the question.
Always put shell variable names in braces.
Note a problem that makes one of the shell functions ineffective.
tests/recompilation/Mmakefile:
Tell two_module_test what to do using its updated interface.
217 lines
7.4 KiB
Bash
Executable File
217 lines
7.4 KiB
Bash
Executable File
#!/bin/sh
|
|
# vim: ts=4 sw=4 expandtab ft=sh
|
|
#
|
|
# A version of two_module_test that should be more useful for debugging,
|
|
# because instead of hiding the action behind shell functions, it exposes
|
|
# the details of each step. This allows a Mercury developer to (temporarily)
|
|
# modify this script to observe not just the final result of a call to a shell
|
|
# function, but also its intermediate results, e.g. by copying files
|
|
# suspected to be constructed incorrectly to safe locations before later steps
|
|
# overwrite them. It also allows running some compiler invocations under mdb.
|
|
# When you suspect, or know, that a step is screwing up, this can be extremely
|
|
# helpful. Smart recompilation is NOT idempotent; executing even part of
|
|
# a compiler invocation (until e.g. a compiler abort) can, and often will,
|
|
# change the contents of files in a way that will cause later, seeming
|
|
# identical compiler invocations to take a different execution path.
|
|
# By rerunning the whole test process from the start, recreating all
|
|
# the relevant files from scratch each time, this script can sidestep
|
|
# that problem.
|
|
#
|
|
# This script also has some limitations compared with two_module_test.
|
|
# The main one is that it covers only grades that target C.
|
|
# It also handles the <mmake_test 2 should_fail> block differently
|
|
# than two_module_test.
|
|
|
|
# It is easier to look up the contents of e.g. .used files
|
|
# if they are in the current directory.
|
|
/bin/rm -fr Mercury
|
|
|
|
test_prog="add_type_re"
|
|
expected_mmake_result=should_fail
|
|
module_1="${test_prog}"
|
|
module_2="${test_prog}_2"
|
|
modules="${module_1} ${module_2}"
|
|
|
|
tested_compiler="/home/zs/ws/ws26/compiler/mercury_compile"
|
|
MERCURY_COMPILER="${tested_compiler}"
|
|
export MERCURY_COMPILER
|
|
|
|
grade_opts="--grade hlc.gc"
|
|
dir_opts="--flags ../TESTS_FLAGS"
|
|
opt_opts="--no-intermodule-optimization"
|
|
smart_opts="--smart-recompilation --find-all-recompilation-reasons"
|
|
std_opts="${grade_opts} ${dir_opts} ${opt_opts} ${smart_opts}"
|
|
|
|
echo "Testing ${test_prog}"
|
|
|
|
echo "block <test_module ${module_1} ${module_2}>"
|
|
rm -f "${module_1}.m"
|
|
cp "${module_1}.m.1" "${module_1}.m"
|
|
chmod -w "${module_1}.m"
|
|
rm -f "${module_2}.m"
|
|
cp "${module_2}.m.1" "${module_2}.m"
|
|
chmod -w "${module_2}.m"
|
|
rm -f "${module_1}.res"
|
|
touch "${module_1}.res"
|
|
sleep 1
|
|
|
|
echo "block <mmake_depend>"
|
|
mmc --generate-dependencies ${std_opts} ${module_1} > ${module_1}.dep_err 2>&1
|
|
|
|
echo "block <mmake_test 1 should_succeed>"
|
|
mmc --make-short-interface ${std_opts} ${module_2}
|
|
mmc --make-interface ${std_opts} ${module_2}
|
|
mmc --make-interface ${std_opts} ${module_1}
|
|
mmc --compile-to-c ${std_opts} ${module_1} > ${module_1}.err 2>&1
|
|
mmc --compile-to-c ${std_opts} ${module_2} > ${module_2}.err 2>&1
|
|
mgnuc ${grade_opts} -- -c ${module_1}.c -o ${module_1}.o
|
|
mgnuc ${grade_opts} -- -c ${module_2}.c -o ${module_2}.o
|
|
ml ${grade_opts} -- -o ${module_1} ${module_1}_init.o \
|
|
${module_1}.o ${module_2}.o
|
|
case "$?" in
|
|
0)
|
|
;;
|
|
*)
|
|
echo "exiting due to failure of <mmake_test 1 should_succeed>"
|
|
exit 1
|
|
;;
|
|
esac
|
|
exp_file="${module_1}.exp.1"
|
|
res_file="${module_1}.out"
|
|
./${module_1} > "${res_file}"
|
|
# We assume ${exp_file} exists.
|
|
if diff ${DIFF_OPTS-"-c"} "${exp_file}" "${res_file}" >> "${module_1}.res"
|
|
then
|
|
true
|
|
else
|
|
echo "** Error in mmake_test 1 should_succeed: ${exp_file} and ${res_file} differ."
|
|
cat "${module_1}.res"
|
|
exit 1
|
|
fi
|
|
|
|
echo "block <update_module ${module_2} 2>"
|
|
sleep 1
|
|
rm -f "${module_2}.m"
|
|
cp "${module_2}.m.2" "${module_2}.m"
|
|
chmod -w "${module_2}.m"
|
|
sleep 1
|
|
|
|
case "${expected_mmake_result}" in
|
|
should_succeed)
|
|
echo "block <mmake_test 2 should_succeed>"
|
|
mmc --make-short-interface ${std_opts} ${module_2}
|
|
mmc --make-interface ${std_opts} ${module_2}
|
|
mmc --make-interface ${std_opts} ${module_1}
|
|
# mdb mmc --compile-to-c ${std_opts} ${module_1}
|
|
mmc --compile-to-c ${std_opts} ${module_1} \
|
|
> ${module_1}.err 2>&1
|
|
echo --- ${module_1}.err ---
|
|
cat ${module_1}.err
|
|
echo status = $?
|
|
exit 0
|
|
mmc --compile-to-c ${std_opts} ${module_2} \
|
|
> ${module_2}.err 2>&1
|
|
mgnuc ${grade_opts} -- -c ${module_1}.c -o ${module_1}.o
|
|
mgnuc ${grade_opts} -- -c ${module_2}.c -o ${module_2}.o
|
|
ml ${grade_opts} -- -o ${module_1} ${module_1}_init.o \
|
|
${module_1}.o ${module_2}.o
|
|
case "$?" in
|
|
0)
|
|
;;
|
|
*)
|
|
echo "Error: <mmake_test 2 should_succeed> has failed"
|
|
exit 1
|
|
;;
|
|
esac
|
|
exp_file="${module_1}.exp.2"
|
|
res_file="${module_1}.out"
|
|
./${module_1} > "${res_file}"
|
|
# We assume ${exp_file} exists.
|
|
if diff ${DIFF_OPTS-"-c"} "${exp_file}" "${res_file}" \
|
|
>> "${module_1}.res"
|
|
then
|
|
true
|
|
else
|
|
echo "** Error in mmake_test 2 should_succeed: ${exp_file} and ${res_file} differ."
|
|
cat "${module_1}.res"
|
|
exit 1
|
|
fi
|
|
;;
|
|
should_fail)
|
|
echo "block <mmake_test 2 should_fail>"
|
|
> "${module_1}.should_fail"
|
|
( \
|
|
mmc --make-short-interface ${std_opts} ${module_2}; \
|
|
if test "$?" != 0; \
|
|
then \
|
|
echo "failure" >> "${module_1}.should_fail"; \
|
|
fi; \
|
|
mmc --make-interface ${std_opts} ${module_2}; \
|
|
if test "$?" != 0; \
|
|
then \
|
|
echo "failure" >> "${module_1}.should_fail"; \
|
|
fi; \
|
|
mmc --make-interface ${std_opts} ${module_1}; \
|
|
if test "$?" != 0; \
|
|
then \
|
|
echo "failure" >> "${module_1}.should_fail"; \
|
|
fi; \
|
|
mmc --compile-to-c ${std_opts} ${module_1} \
|
|
> ${module_1}.err 2>&1; \
|
|
if test "$?" != 0; \
|
|
then \
|
|
echo "failure" >> "${module_1}.should_fail"; \
|
|
fi; \
|
|
mmc --compile-to-c ${std_opts} ${module_2} \
|
|
> ${module_2}.err 2>&1; \
|
|
if test "$?" != 0; \
|
|
then \
|
|
echo "failure" >> "${module_1}.should_fail"; \
|
|
fi; \
|
|
) > "${module_1}.failing_mail_output"
|
|
if test ! -s "${module_1}.should_fail)"
|
|
then
|
|
echo "Error: <mmake_test 2 should_fail> has succeeded"
|
|
exit 1
|
|
fi
|
|
;;
|
|
*)
|
|
echo "** Error: bad expected_mmake_result ${expected_mmake_result}"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
echo "block <check_err_file ${module_1} 2>"
|
|
sed -e '/has CHANGED/d' -e 's/Mercury\/.*\///g' "${module_1}.err" \
|
|
> "${module_1}.err2"
|
|
mv "${module_1}.err2" "${module_1}.err"
|
|
exp_file="${module_1}.err_exp.2"
|
|
res_file="${module_1}.err"
|
|
# We assume ${exp_file} exists.
|
|
if diff ${DIFF_OPTS-"-c"} "${exp_file}" "${res_file}" >> "${module_1}.res"
|
|
then
|
|
true
|
|
else
|
|
echo "** Error in check_err_file ${module_1} 2: ${exp_file} and ${res_file} differ."
|
|
cat "${module_1}.res"
|
|
exit 1
|
|
fi
|
|
|
|
echo "block <check_err_file ${module_2} 2>"
|
|
sed -e '/has CHANGED/d' -e 's/Mercury\/.*\///g' "${module_2}.err" \
|
|
> "${module_2}.err2"
|
|
mv "${module_2}.err2" "${module_2}.err"
|
|
exp_file="${module_2}.err_exp.2"
|
|
res_file="${module_2}.err"
|
|
# We assume ${exp_file} exists.
|
|
if diff ${DIFF_OPTS-"-c"} "${exp_file}" "${res_file}" >> "${module_2}.res"
|
|
then
|
|
true
|
|
else
|
|
echo "** Error in check_err_file ${module_2} 2: ${exp_file} and ${res_file} differ."
|
|
cat "${module_2}.res"
|
|
exit 1
|
|
fi
|
|
|
|
exit 0
|