From b378cbdf6a21441c0a83bfcd684ce4eac59e447b Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Fri, 9 Aug 2024 01:45:14 -0400 Subject: [PATCH] WIP --- tamer/Makefile.am | 2 +- tamer/conf.sh.in | 69 ++++++++++++++++++++++++++++++++- tamer/configure.ac | 43 ++++++++++++++++++++- tamer/tests/test-cfg-prog-fns | 72 +++++++++++++++++++++++++++++++++++ tamer/tests/xmli/test-xmli | 15 +++----- 5 files changed, 188 insertions(+), 13 deletions(-) create mode 100755 tamer/tests/test-cfg-prog-fns diff --git a/tamer/Makefile.am b/tamer/Makefile.am index be459f97..ee3a6a02 100644 --- a/tamer/Makefile.am +++ b/tamer/Makefile.am @@ -74,7 +74,7 @@ check-docgen: build-aux/asg-ontviz >/dev/null .PHONY: check-system -check-system: bin +check-system: bin conf.sh tests/run-tests .PHONY: check-lint diff --git a/tamer/conf.sh.in b/tamer/conf.sh.in index d0fa492b..300725d6 100644 --- a/tamer/conf.sh.in +++ b/tamer/conf.sh.in @@ -1,3 +1,5 @@ +#!/bin/bash + # This configuration file is populated by `configure` and is intended to be # sourced by shell scripts. @@ -62,4 +64,69 @@ fi declare -r TAMER_PATH_TAMEC="$TAMER_PATH_BIN/tamec" declare -r TAMER_PATH_TAMELD="$TAMER_PATH_BIN/tameld" -declare -r P_XMLLINT="@XMLLINT@" +# Miscellaneous programs used by shell scripts. +# +# Note that, just because these programs were populated by a configure +# script, that does not mean that they actually exist. In particular, users +# may arbitrarily override these programs during configuration. +# +# To mitigate this issue, we define functions that serve _in place of_ these +# programs. This provides two major benefits: it allows us to display a +# helpful error when the program cannot be found, and it allows shell +# scripts to simply pretend that those programs exist in the environment +# without having to worry about an extra layer of abstraction. +# +# The most notable downside to this approach is that it is not obvious by +# looking at a shell script whether we have considered that a program may be +# missing, but this ought to be caught by a comprehensive test suite. +awk() { __try-run-cfg-prog "@AWK@" "$@"; } +sed() { __try-run-cfg-prog "@SED@" "$@"; } +grep() { __try-run-cfg-prog "@GREP@" "$@"; } +xmllint() { __try-run-cfg-prog "@XMLLINT@" "$@"; } +dot() { __try-run-cfg-prog "@DOT@" "$@"; } +diff() { __try-run-cfg-prog "@DIFF@" "$@"; } +find() { __try-run-cfg-prog "@FIND@" "$@"; } +xargs() { __try-run-cfg-prog "@XARGS@" "$@"; } +# 'time' is a builtin, so we need a different name +time-cmd() { __try-run-cfg-prog "@TIME@" "$@"; } + +# Exit code when bash is unable to locate the requested command. +declare -ri EX_CMD_NOT_FOUND=127 + +# Attempt to run a program that is configurable via the `configure` script. +# If the program cannot be found, then display a helpful error message that +# directs the user to the configure script. +__try-run-cfg-prog() { + local -r prog="${1?Missing program name}" + shift + + # The name of the wrapper command is assumed to be the name of the + # function that called us (the above functions). + local -r name="${FUNCNAME[1]}" + + local -i ex=0 + + command "$prog" "$@" || { + ex=$? + + if [ "$ex" == "$EX_CMD_NOT_FOUND" ]; then + # The goal is to notify the user of a few things: + # 1. That they should consult 'configure'; + # 2. The name of the command that we _tried_ to invoke; and + # 3. The name that is usually used to invoke this command. + # + # That is: '$prog' and '$name' may differ if someone ran + # ./configure DIFF=foo + # which would try to invoke 'foo' for 'diff'. + cat >&2 <. +# +# Assert that, for each AC_CHECK_PROGS in 'configure.ac', there is a +# corresponding shell function defined for use in shell scripts. +# # + +set -euo pipefail + +declare -r mypath=$(dirname "$0") + +# The functions will be defined in here. +. "$mypath/../conf.sh" + +declare -r root="$mypath/.." + +# These commands appear in configure.ac as AC_CHECK_PROGS, but they are not +# invoked via shell scripts and so should be ignored here. +declare -ra excluded_cmds=(cargo rustc) + +expected-fns() { + # note that some programs are exempt (since they are not invoked via shell + # scripts) and 'time' requires renaming because it is a builtin + awk -F'[][ (,]' '/AC_CHECK_PROGS/{print $5}' "$root/configure.ac" \ + | sans-excluded \ + | sed 's/^time$/time-cmd/' +} + +sans-excluded() { + grep -vF "$( IFS=$'\n'; echo "${excluded_cmds[*]}" )" +} + +main() { + local -i ex=0 + + while read prog; do + printf "checking for shell function %-15s" "'$prog'..." + + test "$(type -t "$prog")" == function || { + echo '[FAIL]' + ex=1 + continue + } + + echo '[ OK ]' + done < <( expected-fns ) + + if [ "$ex" -ne 0 ]; then + echo 'fail: every AC_CHECK_PROGS in configure.ac must appear as a function' + echo ' in conf.sh.in'. + fi + + return "$ex" +} + +main "$@" + diff --git a/tamer/tests/xmli/test-xmli b/tamer/tests/xmli/test-xmli index 5ef36fbb..f02f9e7b 100755 --- a/tamer/tests/xmli/test-xmli +++ b/tamer/tests/xmli/test-xmli @@ -5,12 +5,9 @@ set -euo pipefail -mypath=$(dirname "$0") +declare -r mypath=$(dirname "$0") . "$mypath/../../conf.sh" -# Performing this check within `<()` below won't cause a failure. -: "${P_XMLLINT?}" # conf.sh - run-test() { local name="${1?Missing test name}" local dir="${2?Missing dir}" @@ -48,7 +45,7 @@ timed-tamec() { objty=xmlo-experimental fi - command time -f "%F/%Rfault %I/%Oio %Mrss %c/%wctx \n%C" -o "$dir/time.log" \ + time-cmd -f "%F/%Rfault %I/%Oio %Mrss %c/%wctx \n%C" -o "$dir/time.log" \ "${TAMER_PATH_TAMEC?}" -o "$dir/$out" --emit "$objty" "$dir/$in" \ &> "$dir/tamec-$out.log" \ || ret=$? @@ -86,8 +83,8 @@ test-derive-from-src() { timed-tamec "$dir" src.xml out.xmli || return - diff <("$P_XMLLINT" --format "$dir/expected.xml" || echo 'ERR expected.xml') \ - <("$P_XMLLINT" --format "$dir/out.xmli" || echo 'ERR out.xmli') \ + diff <(xmllint --format "$dir/expected.xml" || echo 'ERR expected.xml') \ + <(xmllint --format "$dir/out.xmli" || echo 'ERR out.xmli') \ &> "$dir/diff.log" } @@ -111,8 +108,8 @@ test-fixpoint() { timed-tamec "$dir" out.xmli out-2.xmli || return - diff <("$P_XMLLINT" --format "$dir/expected.xml" || echo 'ERR expected.xml') \ - <("$P_XMLLINT" --format "$dir/out-2.xmli" || echo 'ERR out.xmli') \ + diff <(xmllint --format "$dir/expected.xml" || echo 'ERR expected.xml') \ + <(xmllint --format "$dir/out-2.xmli" || echo 'ERR out.xmli') \ &> "$dir/diff.log" }