[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
parallel autotest [3/3]: GNU make jobserver client.
From: |
Ralf Wildenhues |
Subject: |
parallel autotest [3/3]: GNU make jobserver client. |
Date: |
Mon, 26 May 2008 07:53:22 +0200 |
User-agent: |
Mutt/1.5.17+20080114 (2008-01-14) |
This patch, if to be used at all, would probably need something like
AC_SUBST([at_parse_makeflags],
['at_dry_run=; at_flags=; at_first=:; \
for at_flag in $(MAKEFLAGS) --; do \
case $$at_flag in \
--jobserver-fds=*) at_flags="$$at_flags $$at_flag";; \
-- | *=*) break;; \
--*) ;; \
*) \
if $$at_first; then \
case $$at_flag in *n*) at_dry_run=:;; esac; \
fi;; \
esac; \
at_first=false; \
done'])
to be generated by Autotest, for easier usage and to avoid duplication
(also, documentation for it).
Cheers,
Ralf
2008-05-26 Ralf Wildenhues <address@hidden>
Implement experimental GNU make jobserver client support.
* doc/autoconf.texi (testsuite Invocation): Document
--jobserver-fds.
* lib/autotest/general.m4 (AT_INIT): Accept --jobserver-fds,
overriding --jobs if needed; document it in --help output.
Move file descriptors in case of a clash with Autoconf's
hard-coded ones. Enable support only if the shell is capable
of one-character reads. Implement jobserver client for
spawning test groups.
* tests/Makefile.am (parse_makeflags): New macro.
(check-local, installcheck-local): Use it to pass
--jobserver-fds to the testsuite.
* tests/autotest.at (parallel jobserver support): New test.
diff --git a/NEWS b/NEWS
index fdee3e0..be1f102 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,7 @@ GNU Autoconf NEWS - User visible changes.
* Major changes in Autoconf 2.62a (2008-??-??)
** Autotest testsuites accept an option --jobs[=N] for parallel testing.
+ Alternatively, testsuites can act as GNU make jobserver client.
* Major changes in Autoconf 2.62 (2008-04-05) [stable]
diff --git a/doc/autoconf.texi b/doc/autoconf.texi
index 82ee582..047c512 100644
--- a/doc/autoconf.texi
+++ b/doc/autoconf.texi
@@ -20343,6 +20343,12 @@ may appear intermixed from concurrently running tests.
Parallel mode requires the @command{mkfifo} command to work, and will be
silently disabled otherwise.
address@hidden address@hidden,@var{n}
+Enable experimental parallel mode governed by a @acronym{GNU} @command{make}
+jobserver. This option should never be specified manually, but instead
+be extracted from @code{$(MAKEFLAGS)} in the @file{Makefile} starting
+the testsuite. It overrides a given @option{--jobs} option.
+
@item --clean
@itemx -c
Remove all the files the test suite might have created and exit. Meant
diff --git a/lib/autotest/general.m4 b/lib/autotest/general.m4
index f620561..bd20617 100644
--- a/lib/autotest/general.m4
+++ b/lib/autotest/general.m4
@@ -401,6 +401,9 @@ at_verbose=:
at_quiet=
# Running several jobs in parallel, 0 means as many as test groups.
at_jobs=1
+# File descriptors for the GNU make jobserver.
+at_fd_in=
+at_fd_out=
# Shall we keep the debug scripts? Must be `:' when the suite is
# run by a debug script, so that the script doesn't remove itself.
@@ -575,6 +578,37 @@ do
AS_ERROR([non-numeric argument to -j/--jobs: $at_jobs]) ;;
esac
;;
+ --jobserver-fds=* )
+ at_save_IFS=$IFS
+ IFS=,
+ set X $at_optarg
+ IFS=$at_save_IFS
+ at_fd_in=$[2]
+ at_fd_out=$[3]
+ case $at_fd_in$at_fd_out in *[[!0-9]]*)
+ AS_ERROR([bogus argument $at_optarg]) ;;
+ esac
+ # Resolve file descriptor clashes.
+ if test $at_fd_in -eq AS_MESSAGE_LOG_FD; then
+ at_old_fd=$at_fd_in
+ at_fd_in=m4_eval(AS_MESSAGE_LOG_FD + 1)
+ if test $at_fd_in -eq $at_fd_out; then
+ at_fd_in=`expr $at_fd_out + 1`
+ fi
+ eval exec "$at_fd_in<&$at_old_fd $at_old_fd<&-"
+ fi
+ if test $at_fd_out -eq AS_MESSAGE_LOG_FD; then
+ at_old_fd=$at_fd_out
+ at_fd_out=m4_eval(AS_MESSAGE_LOG_FD + 2)
+ eval exec "$at_fd_out>&$at_old_fd $at_old_fd>&-"
+ fi
+ # Don't bother if the shell is not capable.
+ if eval '( AS_ECHO_N([++]) | read -n1 at_token ) 2>/dev/null'; then :;
+ else
+ eval "$at_fd_in<&- $at_fd_out>&-"
+ at_fd_in=; at_fd_out=
+ fi
+ ;;
# Keywords.
--keywords | -k )
@@ -682,6 +716,8 @@ Execution tuning:
[ change to directory DIR before starting]
-j[[N]], --jobs[[=N]]
[ Allow N jobs at once; infinite jobs with no arg]
+ --jobserver-fds=[[M,N]]
+[ interface with GNU make jobserver; do not use manually]
-k, --keywords=KEYWORDS
[ select the tests matching all the comma-separated KEYWORDS]
[ multiple \`-k' accumulate; prefixed \`!' negates a KEYWORD]
@@ -974,6 +1010,11 @@ BEGIN { FS="" }
AS_ERROR([cannot create test line number cache])
rm -f "$at_suite_dir/at-source-lines"
+# Ignore --jobs with --jobserver-fds.
+if test -n "$at_fd_in" && test -n "$at_fd_out"; then
+ at_jobs=0
+fi
+
# If parallel mode, don't output banners, don't split summary lines.
if test $at_jobs -ne 1; then
at_print_banners=false
@@ -1146,7 +1187,35 @@ trap 'exit_status=$?
exit $exit_status' 1 2 13 15
at_first=:
-if test $at_jobs -ne 1 &&
+if test -n "$at_fd_in" && test -n "$at_fd_out"; then
+ # GNU make jobserver with bash or ksh.
+ echo
+ set x # We have one token initially.
+ for at_group in $at_groups; do
+ # Start one test group.
+ eval '(
+ at_func_group_prepare
+ if cd "$at_group_dir" &&
+ at_func_test $at_group &&
+ . "$at_test_source" # $at_fd_out>&-
+ then :; else
+ AS_WARN([unable to parse test group: $at_group])
+ at_failed=:
+ fi
+ at_func_group_postprocess
+ AS_ECHO_N([+]) >&$at_fd_out
+ )'" $at_fd_in<&- &"
+ # Consume a token unless we have infinite jobs.
+ shift
+ if test address@hidden:@] -gt 0; then :; else
+ read -n1 at_token <&$at_fd_in || break
+ set x $[*]
+ fi
+ test -f "$at_stop_file" && break
+ at_first=false
+ done
+ wait
+elif test $at_jobs -ne 1 &&
rm -f "$at_job_fifo" &&
( mkfifo "$at_job_fifo" ) 2>/dev/null &&
exec AT_JOB_FIFO_FD<> "$at_job_fifo"
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 01acf3a..4f00aba 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -139,12 +139,31 @@ clean-local:
rm -f *.tmp
rm -f -r autom4te.cache
+parse_makeflags = \
+ dry_run=; pflags=; first=:; \
+ for flag in $(MAKEFLAGS) --; do \
+ case $$flag in \
+ --jobserver-fds=*) pflags="$$pflags $$flag";; \
+ -- | *=*) break;; \
+ --*) ;; \
+ *) \
+ if $$first; then \
+ case $$flag in *n*) dry_run=:;; esac; \
+ fi;; \
+ esac; \
+ first=false; \
+ done
+
check-local: atconfig atlocal $(TESTSUITE)
- $(SHELL) $(TESTSUITE) $(TESTSUITEFLAGS)
+ @$(parse_makeflags); \
+ echo $(SHELL) $(TESTSUITE) $$pflags $(TESTSUITEFLAGS); \
+ $$dry_run $(SHELL) $(TESTSUITE) $$pflags $(TESTSUITEFLAGS) # $(MAKE)
# Run the test suite on the *installed* tree.
installcheck-local: atconfig atlocal $(TESTSUITE)
- $(SHELL) $(TESTSUITE) AUTOTEST_PATH="$(bindir)" $(TESTSUITEFLAGS)
+ @$(parse_makeflags); \
+ echo $(SHELL) $(TESTSUITE) $$pflags AUTOTEST_PATH="$(bindir)"
$(TESTSUITEFLAGS); \
+ $$dry_run $(SHELL) $(TESTSUITE) $$pflags AUTOTEST_PATH="$(bindir)"
$(TESTSUITEFLAGS) # $(MAKE)
diff --git a/tests/autotest.at b/tests/autotest.at
index 54399f3..07172aa 100644
--- a/tests/autotest.at
+++ b/tests/autotest.at
@@ -834,6 +834,76 @@ AT_CHECK_AT_TEST([parallel errexit],
[-j2 --errexit])
+AT_SETUP([parallel jobserver support])
+
+AT_CHECK_AT_PREP([micro-suite],
+[[AT_INIT([suite to test parallel execution])
+m4_for([count], [1], ]]AT_PARALLEL_TESTS_TOTAL[[, [],
+ [AT_SETUP([test number count])
+ AT_CHECK([sleep 1])
+ AT_CLEANUP
+])
+]])
+
+AT_DATA([Makefile],
+[[TESTSUITE = ./micro-suite
+parse_makeflags = \
+ dry_run=; pflags=; first=:; \
+ for flag in $(MAKEFLAGS) --; do \
+ case $$flag in \
+ --jobserver-fds=*) pflags="$$pflags $$flag";; \
+ -- | *=*) break;; \
+ --*) ;; \
+ *) \
+ if $$first; then \
+ case $$flag in *n*) dry_run=:;; esac; \
+ fi;; \
+ esac; \
+ first=false; \
+ done
+
+# Note: we omit $(TESTSUITEFLAGS) here on purpose,
+# in order not to be influenced by the outer testsuite.
+check:
+ @$(parse_makeflags); \
+ echo $(SHELL) $(TESTSUITE) $$pflags; \
+ $$dry_run $(SHELL) $(TESTSUITE) $$pflags # $(MAKE)
+]])
+
+# Take care not to let the jobserver inherited by the toplevel
+# testsuite invocation interfere.
+MAKEFLAGS=
+export MAKEFLAGS
+: ${MAKE=make}
+
+# If make does not grok -jN, skip this test.
+AT_CHECK([$MAKE -j[]AT_PARALLEL_TESTS_RUN check || exit 77],
+ [], [stdout], [ignore])
+# Ensure that all tests run, and lines are not split.
+AT_CHECK([grep -c '^.\{53\}ok' stdout], [], [AT_PARALLEL_TESTS_TOTAL
+])
+
+# Ensure we really are faster than sequential execution;
+# see the `parallel test execution' test for more details.
+AT_CHECK([case `$MAKE --version 2>&1` in ]dnl
+ [ *GNU\ Make*) ]dnl
+ [ eval '( AS_ECHO_N([++]) | read -n1 at_token ) 2>/dev/null' ]dnl
+ [ || exit 77 ;; ]dnl
+ [ *) exit 77 ;; ]dnl
+ [esac])
+mkdir serial
+sed 's|\./micro-suite|../micro-suite|g' < Makefile > serial/Makefile
+AT_CHECK([$MAKE -j[]AT_PARALLEL_TESTS_RUN check & ]dnl
+ [sleep 3 && ]dnl
+ [cd serial && $MAKE check TESTSUITEFLAGS=-3 >/dev/null && ]dnl
+ [{ kill $! && exit 1; :; }], [], [stdout], [ignore])
+AT_CHECK([grep -c '^.\{53\}ok' stdout], [], [AT_PARALLEL_TESTS_TOTAL
+])
+AT_CHECK([grep 'AT_PARALLEL_TESTS_TOTAL tests' stdout], [], [ignore])
+
+AT_CLEANUP
+
+
## ------------------- ##
## srcdir propagation. ##
## ------------------- ##