[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
PATCH: add dejagnu(1) multi-launcher
From: |
Jacob Bachmeyer |
Subject: |
PATCH: add dejagnu(1) multi-launcher |
Date: |
Tue, 18 Dec 2018 01:01:02 -0600 |
User-agent: |
Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.22) Gecko/20090807 MultiZilla/1.8.3.4e SeaMonkey/1.1.17 Mnenhy/0.7.6.0 |
This patch is the long-promised dejagnu(1) multi-launcher, although only
the launcher itself, and the first command to make it useful will be in
a later patch.
I had previously mentioned some files that were part of this work that
have been renamed between then and now. Specifically,
"testsuite/lib/dejagnu.exp" is now "testsuite/lib/launcher.exp" and the
entire "testsuite/dejagnu.launcher/" directory has been renamed to
"testsuite/launcher.all/" after I decided that, since the launcher is
run using a LAUNCHER variable rather than a DEJAGNU variable (obvious
potential for confusion with that bad choice that was avoided from the
start), using "--tool launcher" to test the launcher is more appropriate
than using "--tool dejagnu" even though the launcher is installed under
the "dejagnu" name.
Shellcheck gives one minor complaint in dejagnu as written; I have
chosen to ignore that complaint, since the $(grep ...) on line 130 is:
(1) extracting the version number (which is unlikely to contain spaces)
from runtest.exp and (2) substituted into an "echo" command, where
word-splitting does not matter. I have chosen to ignore this warning,
following the advice of the GNU Coding Standards not to make the program
ugly to placate a static analyzer. Emacs highlights that code much
better and more readably without the quotes anyway.
----
NEWS item:
?. A shell command "dejagnu" is added as a place to hang various auxiliary
commands not directly involved with running tests. The "runtest"
command will remain for that purpose for the forseeable future.
----
ChangeLog entries:
* Makefile.am (EXTRA_DIST): Add "dejagnu" launcher script.
(bin_SCRIPTS): Likewise.
(TESTSUITE_FILES): Add testsuite for same.
(DEJATOOL): Add "launcher" to list of tools to test.
(dist_man_MANS): Add man page for "dejagnu" launcher.
* doc/dejagnu.texi (Running other DejaGnu commands): New chapter.
(Invoking dejagnu): New node for dejagnu(1) launcher script.
* doc/dejagnu.1: New man page.
* dejagnu: New script.
* testsuite/launcher.all/command.exp: New file.
* testsuite/launcher.all/command/commands/bar-baz.awk: New file.
* testsuite/launcher.all/command/commands/bar.awk: New file.
* testsuite/launcher.all/command/commands/bar.sh: New file.
* testsuite/launcher.all/command/commands/baz-quux.gawk: New file.
* testsuite/launcher.all/command/commands/foo.sh: New file.
* testsuite/launcher.all/command/commands/foo.tcl: New file.
* testsuite/launcher.all/interp.exp: New file.
* testsuite/launcher.all/verbose.exp: New file.
* testsuite/lib/launcher.exp: New file.
----
patch:
----
diff --git a/dejagnu b/dejagnu
new file mode 100755
index 0000000..774ff81
--- /dev/null
+++ b/dejagnu
@@ -0,0 +1,454 @@
+#!/bin/sh
+#
+# Copyright (C) 2018 Free Software Foundation, Inc.
+#
+# This file is part of DejaGnu.
+#
+# DejaGnu is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# DejaGnu is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with DejaGnu; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+
+# Portions from runtest Copyright (C) 1992-2016 Free Software Foundation, Inc.
+
+# This script was written by Jacob Bachmeyer. Portions of this script are
+# adapted from the existing runtest script originally written by Rob Savoye.
+
+# This script finds an implementation for the command given, finds the
+# proper interpreter, and then dispatches the command. This script can
+# either be run with a command name as the first (few) argument(s), via a
+# link from the command name, or some combination of those.
+
+# shellcheck disable=SC2209
+# The shellcheck tool complains about assigning certain constant strings to
+# variables. In this script, the intended meaning is obvious in context.
+
+# ##help
+# #Usage: dejagnu <command> [--help|options...]
+# #Usage: dejagnu --help
+# #Usage: dejagnu --version
+# # --help Print help text
+# # --version Print DejaGnu version
+# ##end
+
+# list of extensions supported for commands in priority order
+readonly Variants="gawk awk tcl exp bash sh"
+
+## Recognize options
+
+# For testing and development
+override_ext=
+if test x"$1" = x--DGTimpl ; then
+ override_ext=$2
+ shift 2
+fi
+
+want_help=false
+want_version=false
+verbose=0
+for a in "$@"; do
+ case $a in
+ --help) want_help=true ;;
+ -v|--v|-verbose|--verbose) verbose=$((verbose + 1)) ;;
+ -V|--V|-version|--version) want_version=true ;;
+ esac
+done
+
+if expr "$verbose" \> 0 > /dev/null ; then
+ echo Verbose level is $verbose
+fi
+
+## Get the file name of this script and deduce @address@hidden
+
+bindir="$(echo "$0" | sed -e 's@/[^/]*$@@')"
+if expr "$verbose" \> 0 > /dev/null ; then
+ echo Running launcher from "$bindir"
+fi
+
+## Find the commands.
+
+# If running from source tree, they are in ./commands/ relative to this script.
+# If installed, they are in @datadir@/dejagnu/commands/ on the system.
+
+# This makes the same assumption as in runtest that one of these holds:
+#
+# @datadir@ == @bindir@/../share
+# @datadir@ == @bindir@/../../share
+# @datadir@ == /usr/share
+# @datadir@ == /usr/local/share
+
+if test -n "$DEJAGNULIBS" ; then
+ commdir="${DEJAGNULIBS}/commands"
+ datadir="${DEJAGNULIBS}"
+elif test -d "${bindir}/commands" && test -f "${bindir}/runtest.exp" ; then
+ if expr "$verbose" \> 1 > /dev/null ; then
+ echo Running from source directory
+ fi
+ commdir="${bindir}/commands"
+ datadir="${bindir}"
+else
+ commdir=
+ for i in \
+ "$(echo "$bindir" | sed -e 's@/[^/]*$@/share/dejagnu@')" \
+ "$(echo "$bindir" | sed -e 's@/[^/]*/[^/]*$@/share/dejagnu@')" \
+ /usr/share/dejagnu /usr/local/share/dejagnu
+ do
+ if expr "$verbose" \> 1 > /dev/null ; then
+ echo Probing directory "$i"/commands
+ fi
+ if test -d "$i"/commands ; then
+ commdir="$i"/commands
+ datadir="$i"
+ break
+ fi
+ done
+fi
+
+if test -z "${commdir}" ; then
+ echo ERROR: could not find command directory.
+ exit 2
+fi
+
+if expr "$verbose" \> 0 > /dev/null ; then
+ echo Looking for commands in "$commdir"
+fi
+
+## Get the name of the requested command.
+
+# Are we just looking for version information?
+if $want_version ; then
+ echo 'dejagnu auxiliary launcher (DejaGnu)' \
+ $(grep '^set frame_version' "${datadir}/runtest.exp" \
+ | sed 's/^[^0-9]*//')
+ exit 0
+fi
+
+# Remove any leading autoconf platform prefix and the "dejagnu" prefix.
+command="$(basename "$0" | sed -e 's/^.*-\?dejagnu-\?//')"
+commext=
+
+while true
+do
+ if test -z "${command}" ; then
+ if expr "$1" : - > /dev/null ; then
+ break
+ fi
+ command="$1"
+ shift
+ fi
+ if expr "$verbose" \> 2 > /dev/null ; then
+ echo Looking for "${commdir}/${command}.*"
+ fi
+ for ext in ${Variants}
+ do
+ if test -f "${commdir}/${command}.$ext" ; then
+ commext="$commext $ext"
+ fi
+ done
+ if expr "$1" : - > /dev/null ; then
+ break
+ fi
+ if test -n "$commext" ; then
+ break
+ fi
+ if test -n "$1" ; then
+ command="${command}-$1"
+ shift
+ else
+ break
+ fi
+done
+
+if test -z "$commext" && test -n "$command" ; then
+ echo ERROR: could not resolve command "$command"
+ exit 2
+fi
+
+if expr "$verbose" \> 0 > /dev/null ; then
+ if test -n "$command"; then
+ if expr "$verbose" \> 1 > /dev/null ; then
+ echo Found subcommand "$command" with variants: "$commext"
+ else
+ echo Found subcommand "$command"
+ fi
+ else
+ echo Running nothing.
+ fi
+fi
+
+## Find interpreters.
+
+# Awk and GNU awk
+if test -n "$AWK" ; then
+ awkbin="$AWK"
+elif test -x "${bindir}/awk" ; then
+ awkbin="${bindir}/awk"
+else
+ awkbin=awk
+fi
+if test -n "$GAWK" ; then
+ gawkbin="$GAWK"
+elif test -x "${bindir}/gawk" ; then
+ gawkbin="${bindir}/gawk"
+else
+ gawkbin=gawk
+fi
+if command -v "$awkbin" > /dev/null 2>&1 ; then
+ have_awk=true
+else
+ have_awk=false
+fi
+if command -v "$gawkbin" > /dev/null 2>&1 ; then
+ have_gawk=true
+else
+ have_gawk=false
+fi
+# substitute GNU awk for awk if needed
+if $have_gawk ; then
+ if $have_awk ; then : ; else
+ have_awk=$have_gawk
+ awkbin=$gawkbin
+ fi
+fi
+# is "awk" actually GNU Awk?
+if $have_awk ; then
+ if "$awkbin" --version | sed 1q | grep -qi 'GNU Awk' ; then
+ have_gawk_as_awk=true
+ else
+ have_gawk_as_awk=false
+ fi
+fi
+if expr "$verbose" \> 2 > /dev/null ; then
+ if $have_awk ; then
+ echo Awk interpreter is "$awkbin"
+ else
+ echo Awk interpreter was not found
+ fi
+ if $have_gawk ; then
+ echo GNU Awk interpreter is "$gawkbin"
+ else
+ echo GNU Awk interpreter was not found
+ fi
+fi
+
+
+# Bash
+if test -n "$BASH" ; then
+ bashbin="$BASH"
+elif test -x "${bindir}/bash" ; then
+ bashbin="${bindir}/bash"
+elif test -x /bin/bash ; then
+ bashbin=/bin/bash
+else
+ bashbin=bash
+fi
+if command -v "$bashbin" > /dev/null 2>&1 ; then
+ have_bash=true
+else
+ have_bash=false
+fi
+if expr "$verbose" \> 2 > /dev/null ; then
+ if $have_bash ; then
+ echo Bash interpreter is "$bashbin"
+ else
+ echo Bash interpreter was not found
+ fi
+fi
+
+# Bourne shell
+# This script is running, therefore we have a Bourne shell.
+have_sh=true
+
+# Expect
+# DejaGnu configure bails out if Expect is not available, but this script
+# can be run from the source directory without first running configure.
+if test -n "$EXPECT" ; then
+ expectbin="$EXPECT"
+elif test -x "${bindir}/expect" ; then
+ expectbin="${bindir}/expect"
+else
+ expectbin=expect
+fi
+if command -v "$expectbin" > /dev/null 2>&1 ; then
+ have_expect=true
+else
+ have_expect=false
+fi
+if expr "$verbose" \> 2 > /dev/null ; then
+ if $have_expect ; then
+ echo Expect interpreter is "$expectbin"
+ else
+ echo Expect interpreter was not found
+ fi
+fi
+
+# Tcl
+if test -n "$TCLSH" ; then
+ tclbin="$TCLSH"
+elif test -x "${bindir}/tclsh" ; then
+ tclbin="${bindir}/tclsh"
+else
+ tclbin=tclsh
+fi
+# substitute expect if needed
+if command -v "$tclbin" > /dev/null 2>&1 ; then :
+elif command -v "$expectbin" > /dev/null 2>&1 ; then tclbin="$expectbin"
+fi
+if command -v "$tclbin" > /dev/null 2>&1 ; then
+ have_tcl=true
+else
+ have_tcl=false
+fi
+if expr "$verbose" \> 2 > /dev/null ; then
+ if $have_tcl ; then
+ echo Tcl interpreter is "$tclbin"
+ else
+ echo Tcl interpreter was not found
+ fi
+fi
+
+## Select a variant.
+
+if test -n "$override_ext" ; then
+ selected_ext="$override_ext"
+else
+ selected_ext=
+ for v in $commext
+ do
+ case $v in
+ awk)
+ if $have_awk ; then
+ selected_ext=awk
+ break
+ fi
+ ;;
+ bash)
+ if $have_bash ; then
+ selected_ext=bash
+ break
+ fi
+ ;;
+ exp)
+ if $have_expect ; then
+ selected_ext=exp
+ break
+ fi
+ ;;
+ gawk)
+ if $have_gawk ; then
+ selected_ext=gawk
+ break
+ fi
+ ;;
+ tcl)
+ if $have_tcl ; then
+ selected_ext=tcl
+ break
+ fi
+ ;;
+ sh)
+ selected_ext=sh
+ break
+ ;;
+ *)
+ echo ERROR: '(select-variant)' unrecognized variant "$v"
+ ;;
+ esac
+ done
+ if test -z "$selected_ext" && test -n "$command" ; then
+ echo ERROR: no variant of "$command" was selected
+ exit 2
+ fi
+fi
+
+if test -n "$command" && expr "$verbose" \> 0 > /dev/null ; then
+ if test -n "$override_ext" ; then
+ echo Selected variant "$selected_ext" by override
+ else
+ echo Selected variant "$selected_ext"
+ fi
+fi
+
+## Dispatch to the selected command.
+
+# Are we just looking for a usage message?
+if $want_help ; then
+ if test -z "$command" ; then
+ # want help on the launcher itself
+ help_file=$0
+ else
+ help_file="${commdir}/${command}.${selected_ext}"
+ fi
+ if test ! -r "$help_file" ; then
+ echo ERROR: file "'$help_file'" is not readable
+ exit 2
+ fi
+ if grep -q '#help' "$help_file" \
+ && grep -q '#end' "$help_file"; then : ; else
+ echo ERROR: file "'$help_file'" does not contain a help message
+ exit 2
+ fi
+ help_prefix_pat=$(grep '#help' "$help_file" \
+ | sed 's/#help.*$//;1q' | tr '[:print:][:blank:]' .)
+ if expr "$verbose" \> 1 > /dev/null ; then
+ echo Extracting help from "'$help_file'" with prefix
"'$help_prefix_pat'"
+ fi
+ sed -n < "$help_file" \
+ -e '1,/#help/d' \
+ -e '/^'"$help_prefix_pat"'#end/q' \
+ -e 's/^'"$help_prefix_pat"'//;p'
+ exit 0
+fi
+
+if test -z "$command" ; then
+ if test -n "$override_ext" ; then
+ case $selected_ext in
+ awk) $have_awk; exit $? ;;
+ bash) $have_bash; exit $? ;;
+ exp) $have_expect; exit $? ;;
+ gawk) $have_gawk; exit $? ;;
+ tcl) $have_tcl; exit $? ;;
+ sh) $have_sh; exit $? ;;
+ *) exit 2 ;;
+ esac
+ else
+ echo ERROR: no command given
+ exit 2
+ fi
+fi
+
+case $selected_ext in
+ awk)
+ if $have_gawk_as_awk ; then
+ exec "$awkbin" --posix -f "${commdir}/${command}.awk" -- ${1+"$@"}
+ else
+ exec "$awkbin" -f "${commdir}/${command}.awk" -- ${1+"$@"}
+ fi
+ ;;
+ bash) exec "$bashbin" -- "${commdir}/${command}.bash" ${1+"$@"} ;;
+ exp) exec "$expectbin" -- "${commdir}/${command}.exp" ${1+"$@"} ;;
+ gawk) exec "$gawkbin" -f "${commdir}/${command}.gawk" -- ${1+"$@"} ;;
+ tcl) exec "$tclbin" "${commdir}/${command}.tcl" ${1+"$@"} ;;
+ sh) exec "${commdir}/${command}.sh" ${1+"$@"} ;;
+ echo)
+ echo command: "${command}"
+ echo args: ${1+"$@"}
+ exit 0
+ ;;
+ *)
+ echo ERROR: '(run-variant)' unrecognized variant "$selected_ext"
+ exit 2
+ ;;
+esac
+
+
+#EOF
diff --git a/doc/dejagnu.1 b/doc/dejagnu.1
new file mode 100644
index 0000000..70facb0
--- /dev/null
+++ b/doc/dejagnu.1
@@ -0,0 +1,141 @@
+.\" Copyright (C) 2018 Free Software Foundation, Inc
+.\" You may distribute this file under the terms of the GNU Free
+.\" Documentation License.
+.Dd December 17, 2018
+.Os GNU
+.Dt DEJAGNU 1 URM
+.Sh NAME
+.Nm dejagnu
+.Nd DejaGnu auxiliary command launcher
+.Sh SYNOPSIS
+.Nm dejagnu
+.Ao Ar command Ac
+.Op Fl -help \*(Ba Ar options...
+.Nm
+.Fl -help
+.Nm
+.Fl -version
+.Sh DESCRIPTION
+The
+.Nm
+command finds a script that implements the requested
+.Ar command ,
+selects from multiple implementations if available
+according to a fixed internal list, and executes the command.
+.Sh OPTIONS
+.Bl -tag -width ".Fl -version"
+.It Fl -help
+Print a help message instead of running a command.
+If no command is given, prints brief usage for
+.Nm
+itself.
+.It Fl V , -version
+Print a version banner for the launcher itself including the version of
DejaGnu.
+Any command given is ignored.
+.It Fl v , -verbose
+Emit additional output describing the operation of the
+.Nm
+launcher itself.
+This option is also passed on to the invoked command.
+.El
+.Pp
+All arguments after the command name are passed to the invoked command.
+.Sh ENVIRONMENT
+.Bl -tag -width ".Ev DEJAGNULIBS"
+.It Ev DEJAGNULIBS
+If set, the location of DejaGnu's library in the filesystem.
+The search described in
+.Sx FILES
+does not happen if
+.Ev DEJAGNULIBS
+is set.
+.It Ev AWK
+Full file name for an Awk interpreter that may or may not actually be GNU Awk.
+If not set,
+.Ev PATH
+will be searched for an
+.Nm awk
+program.
+If the Awk interpreter is actually GNU Awk, the
+.Fl -posix
+option will be given if an Awk implementation is used.
+.It Ev GAWK
+Full file name for GNU Awk. If not set,
+.Ev PATH
+will be searched for a
+.Nm gawk
+program.
+.It Ev BASH
+Full file name for GNU Bash. If not set,
+.Ev PATH
+will be searched for a
+.Nm bash
+program.
+Note that Bash itself sets this variable, even when run as
+.Nm sh ,
+even when running a script.
+.It Ev EXPECT
+Full file name for Expect, which is a Tcl interpreter with the Expect
+extension already loaded. If not set,
+.Ev PATH
+will be searched for an
+.Nm expect
+program. Note that the DejaGnu core is written in Expect, so this
+interpreter should always be available.
+.It Ev TCLSH
+Full file name for a Tcl interpreter. If not set,
+.Ev PATH
+will be searched for a
+.Nm tclsh
+program.
+.El
+.Pp
+Note that GNU Awk is considered a superset of Awk and that Expect is
+considered a superset of Tcl, allowing the former to be used to run scripts
+written for the latter.
+This means that, while Awk programs will generally be run with GNU
+extensions disabled using the
+.Fl -posix
+option to GNU Awk, Tcl programs may be run with either
+.Nm tclsh
+or
+.Nm expect
+and should be written accordingly.
+.Sh FILES
+.Bl -tag -width ".Pa $DEJAGNULIBS/commands"
+.It Pa $DEJAGNULIBS/commands
+If
+.Ev DEJAGNULIBS
+is set, all command scripts are expected to be in this directory.
+.El
+Otherwise, the first directory that actually exists in the following list
+is used, where
+.Pa @bindir@
+represents the directory containing
+.Nm
+itself.
+.Bl -item -offset indent
+.It
+.Pa @bindir@/../share/dejagnu/commands
+.It
+.Pa @bindir@/../../share/dejagnu/commands
+.It
+.Pa /usr/share/dejagnu/commands
+.It
+.Pa /usr/local/share/dejagnu/commands
+.El
+.\" .Sh EXAMPLES
+.Sh "SEE ALSO"
+The full documentation for DejaGnu is maintained as a Texinfo manual.
+If the
+.Nm info
+program is properly installed at your site, the command
+.Li info dejagnu
+should give you access to the complete manual.
+.Sh AUTHORS
+.An "Jacob Bachmeyer"
+.Sh BUGS
+The command name must precede all other arguments due to limitations of the
+shell.
+.\" LocalWords: Dt URM Nm DejaGnu Ao DEJAGNULIBS DejaGnu's Sx awk posix tclsh
+.\" LocalWords: tcl superset bindir usr Texinfo
diff --git a/doc/dejagnu.texi b/doc/dejagnu.texi
index c4b4467..b7561b4 100644
--- a/doc/dejagnu.texi
+++ b/doc/dejagnu.texi
@@ -50,6 +50,7 @@ Free Documentation License''.
@menu
* Introduction::
* Running tests::
+* Running other DejaGnu commands::
* Customizing DejaGnu::
* Extending DejaGnu::
* Unit testing::
@@ -75,6 +76,10 @@ Running tests
* Running runtest: Runtest.
* Output files: Output Files.
+Running other DejaGnu commands
+
+* Invoking dejagnu:: Command line options for the launcher itself.
+
Customizing DejaGnu
* Global config file::
@@ -421,7 +426,7 @@ tools you want to test; then, in each test directory, run
@emph{make
check} to build auxiliary programs required by some of the tests, and
run the test suites.
address@hidden Running tests, Customizing DejaGnu, Introduction, Top
address@hidden Running tests, Running other DejaGnu commands, Introduction, Top
@chapter Running tests
There are two ways to execute a testsuite. The most common way is when
@@ -1008,7 +1013,81 @@ make the actions for fail-safe patterns produce messages
starting with
@end itemize
address@hidden Customizing DejaGnu, Extending DejaGnu, Running tests, Top
address@hidden Running other DejaGnu commands, Customizing DejaGnu, Running
tests, Top
address@hidden Running other DejaGnu commands
+
+DejaGnu now features auxiliary commands not directly related to
+running tests, but somehow related to the broader purpose of testing.
+
+These commands are run via the @command{dejagnu} multiplex launcher,
+which locates an appropriate script and the required interpreter and
+then runs the requested command.
+
address@hidden
+* Invoking dejagnu:: Command line options for the launcher itself.
address@hidden menu
+
address@hidden Invoking dejagnu, , Running other DejaGnu commands, Running
other DejaGnu commands
address@hidden Invoking @command{dejagnu}
address@hidden dejagnu, invoking
+
+The @command{dejagnu} launcher is primarily designed to pass most
+options on to the scripts that it runs, but does process the
address@hidden and @option{--version} options entirely internally,
+while also recognizing the @option{--verbose} option.
+
address@hidden
address@hidden <command> [options...]
address@hidden --help
address@hidden --version
address@hidden example
+
+Note that the command names may contain multiple words. In this case,
+the command can be given as separate arguments to @command{dejagnu} or
+combined with dashes(@samp{-}); both forms are equivalent.
+
+All words of the command name must appear before any options. The
+search for a command terminates when an option is found.
+
+Note that the first valid command found is used. A longer command
+name can be shadowed by a shorter command name that happens to be a
+prefix of the longer name, if the command name is given as multiple
+arguments. The equivalent form with the longer command name combined
+using dashes into a single argument will correctly refer to the
+otherwise shadowed command.
+
+The @command{dejagnu} launcher can also be run using symbolic links,
+provided that the shell places the name under which @command{dejagnu}
+was invoked in @code{$0}. Any dash-separated words after ``dejagnu''
+in the name of such a link are taken to be the leading words of a
+command name.
+
+The @command{dejagnu} launcher supports alternate implementations
+depending upon available interpreters.
+
+Options for the @command{dejagnu} launcher itself cannot be
+abbreviated, since the launcher has no way to know which abbreviations
+are unique and which would be ambiguous to the invoked command.
+
address@hidden @asis
+
address@hidden @code{--help}
+Print a help message instead of running a command.
+
address@hidden @code{-V}, @code{--version}
+Print a version banner for the launcher itself including the
+version of DejaGnu. Any command given is ignored.
+
address@hidden @code{-v}, @code{--verbose}
+Emit additional output describing the inner workings of the
address@hidden launcher. This option is also passed on to the
+invoked command.
+
address@hidden table
+
+All arguments after the command name are passed to the invoked command.
+
address@hidden Customizing DejaGnu, Extending DejaGnu, Running other DejaGnu
commands, Top
@chapter Customizing DejaGnu
@cindex customization
diff --git a/testsuite/launcher.all/command.exp
b/testsuite/launcher.all/command.exp
new file mode 100644
index 0000000..eb7b219
--- /dev/null
+++ b/testsuite/launcher.all/command.exp
@@ -0,0 +1,210 @@
+# Copyright (C) 2018 Free Software Foundation, Inc.
+#
+# This file is part of DejaGnu.
+#
+# DejaGnu is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# DejaGnu is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with DejaGnu; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+
+# This file was written by Jacob Bachmeyer.
+
+# each element: { launcher_alias
+# { name args envars exit_code output_re... }... }
+set tests {
+ { dejagnu
+ { "#" direct calls }
+
+ { "dejagnu foo as Tcl"
+ {foo -v -v} {EXPECT=true TCLSH=true } 0
+ "Found subcommand foo with variants: tcl sh\n"
+ "Selected variant tcl" }
+ { "dejagnu foo as Tcl via Expect"
+ {foo -v -v} {EXPECT=true TCLSH=bogus} 0
+ "Found subcommand foo with variants: tcl sh\n"
+ "Selected variant tcl" }
+ { "dejagnu foo as shell"
+ {foo -v -v} {EXPECT=bogus TCLSH=bogus} 0
+ "Found subcommand foo with variants: tcl sh\n"
+ "Selected variant sh" }
+
+ { "dejagnu bar as Awk"
+ {bar -v -v} {AWK=true GAWK=true } 0
+ "Found subcommand bar with variants: awk sh\n"
+ "Selected variant awk" }
+ { "dejagnu bar as Awk via GNU Awk"
+ {bar -v -v} {AWK=bogus GAWK=true } 0
+ "Found subcommand bar with variants: awk sh\n"
+ "Selected variant awk" }
+ { "dejagnu bar as shell"
+ {bar -v -v} {AWK=bogus GAWK=bogus} 0
+ "Found subcommand bar with variants: awk sh\n"
+ "Selected variant sh" }
+
+ { "dejagnu bar shadows bar-baz"
+ {bar baz -v -v} {AWK=true} 0
+ "Found subcommand bar with variants: awk sh" }
+ { "dejagnu bar-baz works"
+ {bar-baz -v -v} {AWK=true} 0
+ "Found subcommand bar-baz with variants: awk" }
+ { "dejagnu bar-baz fails without Awk"
+ {bar-baz -v -v} {AWK=bogus GAWK=bogus} 2
+ "Found subcommand bar-baz with variants: awk\n"
+ "ERROR: no variant of bar-baz was selected" }
+
+ { "dejagnu baz-quux works"
+ {baz-quux -v -v} {GAWK=true} 0
+ "Found subcommand baz-quux with variants: gawk\n"
+ "Selected variant gawk" }
+ { "dejagnu baz quux works"
+ {baz quux -v -v} {GAWK=true} 0
+ "Found subcommand baz-quux with variants: gawk\n"
+ "Selected variant gawk" }
+
+ { "#" invalid direct calls }
+
+ { "dejagnu -v bar fails" {-v bar} {} 2
+ "Running nothing.\nERROR: no command given" }
+
+ { "dejagnu \"\" bar fails" {"" bar} {} 2 "ERROR: " }
+ { "dejagnu \"\" \"\" bar fails" {"" "" bar} {} 2 "ERROR: " }
+
+ { "dejagnu baz \"\" quux fails" {baz "" quux} {GAWK=true} 2 "ERROR:
" }
+
+ { "dejagnu bar \"\" baz finds bar subcommand"
+ {--DGTimpl echo bar "" baz} {} 0
+ "command: bar\nargs: baz" }
+
+ { "#" help system }
+
+ { "dejagnu --help works" {--help} {} 0 "Usage: dejagnu" }
+
+ { "dejagnu foo --help works if shell variant selected"
+ {foo --help} {EXPECT=bogus TCLSH=bogus} 0
+ "foo.sh help message" }
+ { "dejagnu foo --help fails if Tcl variant selected"
+ {foo --help} {EXPECT=true TCLSH=true } 2
+ "does not contain a help message" }
+ }
+
+ { dejagnu-foo
+ { "#" calls via dejagnu-foo link }
+ { "dejagnu-foo as Tcl"
+ {-v -v} {EXPECT=true TCLSH=true } 0
+ "Found subcommand foo with variants: tcl sh\n"
+ "Selected variant tcl" }
+ { "dejagnu-foo as Tcl via Expect"
+ {-v -v} {EXPECT=true TCLSH=bogus} 0
+ "Found subcommand foo with variants: tcl sh\n"
+ "Selected variant tcl" }
+ { "dejagnu-foo as shell"
+ {-v -v} {EXPECT=bogus TCLSH=bogus} 0
+ "Found subcommand foo with variants: tcl sh\n"
+ "Selected variant sh" }
+ }
+
+ { dejagnu-bar
+ { "#" calls via dejagnu-bar link }
+ { "dejagnu-bar as Awk"
+ {-v -v} {AWK=true GAWK=true } 0
+ "Found subcommand bar with variants: awk sh\n"
+ "Selected variant awk" }
+ { "dejagnu-bar as Awk via GNU Awk"
+ {-v -v} {AWK=bogus GAWK=true } 0
+ "Found subcommand bar with variants: awk sh\n"
+ "Selected variant awk" }
+ { "dejagnu-bar as shell"
+ {-v -v} {AWK=bogus GAWK=bogus} 0
+ "Found subcommand bar with variants: awk sh\n"
+ "Selected variant sh" }
+
+ { "#" false hybrid: bar shadows bar-baz }
+ { "dejagnu-bar as Awk shadows bar-baz"
+ {baz -v -v} {AWK=true} 0
+ "Found subcommand bar with variants: awk sh\n"
+ "Selected variant awk" }
+ }
+
+ { dejagnu-bar-baz
+ { "#" calls via dejagnu-bar-baz link }
+ { "dejagnu-bar-baz as Awk"
+ {-v -v} {AWK=true GAWK=true } 0
+ "Found subcommand bar-baz with variants: awk\n"
+ "Selected variant awk" }
+ { "dejagnu-bar-baz as Awk via GNU Awk"
+ {-v -v} {AWK=bogus GAWK=true } 0
+ "Found subcommand bar-baz with variants: awk\n"
+ "Selected variant awk" }
+ { "dejagnu-bar-baz fails without Awk"
+ {-v -v} {AWK=bogus GAWK=bogus} 2
+ "Found subcommand bar-baz with variants: awk\n"
+ "ERROR: no variant of bar-baz was selected" }
+ }
+
+ { dejagnu-baz
+ { "#" hybrid: link as prefix }
+ { "dejagnu-baz quux as GNU Awk"
+ {quux -v -v} {GAWK=true} 0
+ "Found subcommand baz-quux with variants: gawk\n"
+ "Selected variant gawk" }
+ { "dejagnu-baz quux fails without GNU Awk"
+ {quux -v -v} {GAWK=bogus AWK=true} 2
+ "Found subcommand baz-quux with variants: gawk\n"
+ "ERROR: no variant of baz-quux was selected" }
+ }
+
+}
+
+# Build the test environment.
+
+# This must be done at run-time in the build directory because symbolic
+# links are not allowed in GNU source tarballs.
+
+catch {
+ file link -symbolic \
+ [testsuite file -object -test command share dejagnu commands] \
+ [testsuite file -source -test command commands]
+}
+foreach alias_item $tests {
+ catch {
+ file link -symbolic \
+ [testsuite file -object -test command bin [lindex $alias_item 0]] \
+ $LAUNCHER
+ }
+}
+if { ![file exists \
+ [testsuite file -object -test command share dejagnu commands]] } {
+ foreach alias_set $tests {
+ skip_dejagnu_launcher_tests \
+ "Could not set up test environment." \
+ unsupported [lrange $alias_set 1 end]
+ }
+ return
+}
+
+# Run the tests.
+
+if { [which true] == 0 || [which bogus] != 0 } {
+ foreach alias_set $tests {
+ skip_dejagnu_launcher_tests \
+ "Either 'true' is not on PATH or 'bogus' is." \
+ unresolved [lrange $alias_set 1 end]
+ }
+} else {
+ foreach alias_set $tests {
+ run_dejagnu_launcher_tests \
+ [testsuite file -object -test command bin [lindex $alias_set 0]] \
+ [lrange $alias_set 1 end]
+ }
+}
+
+#EOF
diff --git a/testsuite/launcher.all/command/commands/bar-baz.awk
b/testsuite/launcher.all/command/commands/bar-baz.awk
new file mode 100644
index 0000000..28753c8
--- /dev/null
+++ b/testsuite/launcher.all/command/commands/bar-baz.awk
@@ -0,0 +1 @@
+# empty "hidden" command
diff --git a/testsuite/launcher.all/command/commands/bar.awk
b/testsuite/launcher.all/command/commands/bar.awk
new file mode 100644
index 0000000..1bb8bf6
--- /dev/null
+++ b/testsuite/launcher.all/command/commands/bar.awk
@@ -0,0 +1 @@
+# empty
diff --git a/testsuite/launcher.all/command/commands/bar.sh
b/testsuite/launcher.all/command/commands/bar.sh
new file mode 100755
index 0000000..c52d3c2
--- /dev/null
+++ b/testsuite/launcher.all/command/commands/bar.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+exit 0
diff --git a/testsuite/launcher.all/command/commands/baz-quux.gawk
b/testsuite/launcher.all/command/commands/baz-quux.gawk
new file mode 100644
index 0000000..9c4ff45
--- /dev/null
+++ b/testsuite/launcher.all/command/commands/baz-quux.gawk
@@ -0,0 +1 @@
+# empty -*- Awk -*- file
diff --git a/testsuite/launcher.all/command/commands/foo.sh
b/testsuite/launcher.all/command/commands/foo.sh
new file mode 100755
index 0000000..154687f
--- /dev/null
+++ b/testsuite/launcher.all/command/commands/foo.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# ##help
+# #foo.sh help message
+# ##end
+
+exit 0
diff --git a/testsuite/launcher.all/command/commands/foo.tcl
b/testsuite/launcher.all/command/commands/foo.tcl
new file mode 100644
index 0000000..1bb8bf6
--- /dev/null
+++ b/testsuite/launcher.all/command/commands/foo.tcl
@@ -0,0 +1 @@
+# empty
diff --git a/testsuite/launcher.all/interp.exp
b/testsuite/launcher.all/interp.exp
new file mode 100644
index 0000000..02e763e
--- /dev/null
+++ b/testsuite/launcher.all/interp.exp
@@ -0,0 +1,57 @@
+# Copyright (C) 2018 Free Software Foundation, Inc.
+#
+# This file is part of DejaGnu.
+#
+# DejaGnu is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# DejaGnu is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with DejaGnu; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+
+# This file was written by Jacob Bachmeyer.
+
+# each element: { name args envars exit_code output_re... }
+set tests {
+ { "have shell, always" {--DGTimpl sh} {} 0 "" }
+ { "report error probing 'echo'" {--DGTimpl echo} {} 2 "" }
+
+ { "have no Awk" {--DGTimpl awk} {AWK=bogus GAWK=bogus} 1 "" }
+ { "have Awk" {--DGTimpl awk} {AWK=true GAWK=bogus} 0 "" }
+
+ { "have no GNU Awk" {--DGTimpl gawk} {GAWK=bogus} 1 "" }
+ { "have GNU Awk" {--DGTimpl gawk} {GAWK=true } 0 "" }
+
+ { "GNU Awk substitutes for Awk"
+ {--DGTimpl awk} {AWK=bogus GAWK=true} 0 "" }
+
+ { "have no Tcl" {--DGTimpl tcl} {TCLSH=bogus EXPECT=bogus} 1 "" }
+ { "have Tcl" {--DGTimpl tcl} {TCLSH=true EXPECT=bogus} 0 "" }
+
+ { "have no Expect" {--DGTimpl exp} {EXPECT=bogus} 1 "" }
+ { "have Expect" {--DGTimpl exp} {EXPECT=true } 0 "" }
+
+ { "Expect substitutes for Tcl"
+ {--DGTimpl tcl} {TCLSH=bogus EXPECT=true} 0 "" }
+}
+
+if { [which true] == 0 || [which bogus] != 0 } {
+ skip_dejagnu_launcher_tests \
+ "Either 'true' is not on PATH or 'bogus' is." \
+ unresolved $tests
+} elseif { ![file isdirectory [file join [file dirname $LAUNCHER] commands]] }
{
+ skip_dejagnu_launcher_tests \
+ "The 'commands' directory is not present in the source tree." \
+ unsupported $tests
+} else {
+ run_dejagnu_launcher_tests $LAUNCHER $tests
+}
+
+#EOF
diff --git a/testsuite/launcher.all/verbose.exp
b/testsuite/launcher.all/verbose.exp
new file mode 100644
index 0000000..a45ca2d
--- /dev/null
+++ b/testsuite/launcher.all/verbose.exp
@@ -0,0 +1,55 @@
+# Copyright (C) 2018 Free Software Foundation, Inc.
+#
+# This file is part of DejaGnu.
+#
+# DejaGnu is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# DejaGnu is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with DejaGnu; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+
+# This file was written by Jacob Bachmeyer.
+
+# each element: { name args envars exit_code output_re... }
+set tests {
+ { "empty command" {} {} 2 "ERROR: no command given" }
+ { "empty; verbose 1" {-v} {} 2
+ "Verbose level is 1\nRunning launcher [^\n]*\n"
+ "Looking for commands [^\n]*\nRunning nothing.\n"
+ "ERROR: no command given" }
+ { "empty; verbose 2" {-v -v} {} 2
+ "Verbose level is 2\nRunning launcher [^\n]*\n"
+ "Running from source directory\n"
+ "Looking for commands [^\n]*\nRunning nothing.\n"
+ "ERROR: no command given" }
+ { "empty; verbose 3" {-v -v -v} {} 2
+ "Verbose level is 3\nRunning launcher [^\n]*\n"
+ "Running from source directory\n"
+ "Looking for commands [^\n]*\nRunning nothing.\n"
+ "Awk interpreter [^\n]*\nGNU Awk interpreter [^\n]*\n"
+ "Bash interpreter [^\n]*\n"
+ "Expect interpreter [^\n]*\nTcl interpreter [^\n]*\n"
+ "ERROR: no command given" }
+}
+
+lappend tests [list "check version" {--version} {} 0 \
+ [format "dejagnu auxiliary launcher \\(DejaGnu\\) %s" \
+ $frame_version]]
+
+if { ![file isdirectory [file join [file dirname $LAUNCHER] commands]] } {
+ skip_dejagnu_launcher_tests \
+ "The 'commands' directory is not present in the source tree." \
+ unsupported $tests
+} else {
+ run_dejagnu_launcher_tests $LAUNCHER $tests
+}
+
+#EOF
diff --git a/testsuite/lib/launcher.exp b/testsuite/lib/launcher.exp
new file mode 100644
index 0000000..906f722
--- /dev/null
+++ b/testsuite/lib/launcher.exp
@@ -0,0 +1,110 @@
+# Copyright (C) 2018 Free Software Foundation, Inc.
+#
+# This file is part of DejaGnu.
+#
+# DejaGnu is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# DejaGnu is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with DejaGnu; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+
+# This file was written by Jacob Bachmeyer.
+
+if { ![info exists LAUNCHER] } {
+ set LAUNCHER \
+ [file join [file dirname [testsuite file -source -top]] dejagnu]
+}
+verbose "Using LAUNCHER $LAUNCHER" 2
+
+if { [which $LAUNCHER] == 0 } {
+ perror "Can't find LAUNCHER = $LAUNCHER"
+ exit 2
+}
+
+# run dejagnu(1) LAUNCHER with ARGLIST, returning { output exit_code }
+proc dejagnu_run { launcher arglist envlist } {
+ global errorCode
+
+ set exec_cmd [list exec]
+ if { [llength $envlist] > 0 } {
+ lappend exec_cmd env
+ foreach var $envlist { lappend exec_cmd $var }
+ }
+ lappend exec_cmd $launcher
+
+ # reset errorCode
+ catch { error }
+
+ catch { eval $exec_cmd $arglist } output
+
+ if { [lindex $errorCode 0] eq "CHILDSTATUS" } {
+ return [list $output [lindex $errorCode 2]]
+ } else {
+ return [list $output 0]
+ }
+}
+
+# evaluate a test against LAUNCHER, returning true if it passes
+# TEST is a list: { name arglist envlist exit_code output_re... }
+proc try_dejagnu_launcher { launcher test } {
+ foreach part [lrange $test 4 end] { append re $part }
+
+ if { [llength [lindex $test 2]] > 0 } {
+ verbose "Spawning \"env [lindex $test 2] $launcher [lindex $test 1]\"
..."
+ } else {
+ verbose "Spawning \"$launcher [lindex $test 1]\" ..."
+ }
+ verbose "Expecting to match {$re} ..." 2
+ set result [dejagnu_run $launcher [lindex $test 1] [lindex $test 2]]
+ verbose "Exit code [lindex $result 1]; output {[lindex $result 0]}" 2
+
+ if { [regexp $re [lindex $result 0]]
+ && [lindex $test 3] == [lindex $result 1] } {
+ return 1
+ } else {
+ return 0
+ }
+}
+
+proc run_dejagnu_launcher_tests { launcher tests } {
+ foreach test $tests {
+ if { [lindex $test 0] == "#" } {
+ # ignore comments in test list
+ } elseif { [llength $test] == 1 } {
+ # name only is a stub
+ untested [lindex $test 0]
+ } elseif { [try_dejagnu_launcher $launcher $test] } {
+ pass [lindex $test 0]
+ } else {
+ fail [lindex $test 0]
+ }
+ }
+}
+
+proc skip_dejagnu_launcher_tests { why result tests } {
+ perror $why 0
+ foreach test $tests {
+ if { [lindex $test 0] == "#" } {
+ # ignore comments in test list
+ } else {
+ $result [lindex $test 0]
+ }
+ }
+}
+
+# stub: dejagnu(1) itself is non-interactive
+proc dejagnu_exit {} {}
+
+# stub: dejagnu(1) does not have a separate version number
+proc dejagnu_version {} {
+}
+
+#EOF
----
-- Jacob