autoconf-patches
[Top][All Lists]
Advanced

[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. ##
 ## ------------------- ##




reply via email to

[Prev in Thread] Current Thread [Next in Thread]