In Makefiles generated by automake versions to date (1.18.1), submakes are
called serially.

---8<---
$(am__recursive_targets):
        @fail=; \
        ...
        for subdir in $$list; do \
          ..
          ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
          || eval $$failcom; \
--->8---

As a directory runs out of targets to make just before completion,
the parallelization factor drops for that directory, and thus also the
parent make. A project with SUBDIRS can therefore accrue a lot of idle
time. This can be fixed by running over SUBDIRS in parallel.

An updated ruleset fragment needs to looks like this:

---8<---
$(am__recursive_targets):
        @fail=; \
        if $(am__make_keepgoing); then \
          failcom='fail=yes'; \
        else \
          failcom='exit 1'; \
        fi; \
        dot_seen=no; \
        target=`echo $@ | sed s/-recursive//`; \
        case "$@" in \
          distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
          *) list='$(SUBDIRS)' ;; \
        esac; \
        md="$${TMPDIR=/tmp}/am-rm-$$$$"; \
        mkdir -p "$$md"; \
        mf="$$md/Makefile.$@"; \
        new_phony=".PHONY:"; \
        new_targets="all:"; \
        for subdir in $$list; do \
          if test "$$subdir" = "."; then \
            dot_seen=yes; \
          fi; \
          new_phony="$$new_phony d.$$subdir"; \
          new_targets="$$new_targets d.$$subdir"; \
        done; \
        if test "$$dot_seen" = "no"; then \
          new_phony="$$new_phony d.."; \
          new_targets="$$new_targets d.."; \
        fi; \
        echo "$$new_phony" >"$$mf"; \
        echo "$$new_targets" >>"$$mf"; \
        for subdir in $$list; do \
          if test "$$subdir" = "."; then \
            dot_seen=yes; \
            echo "d.$$subdir:" >>"$$mf"; \
            echo "      $$""(MAKE) $$""(AM_MAKEFLAGS) -C $$subdir $$target-am" 
>>"$$mf"; \
          else \
            echo "d.$$subdir:" >>"$$mf"; \
            echo "      $$""(MAKE) $$""(AM_MAKEFLAGS) -C $$subdir $$target" 
>>"$$mf"; \
          fi; \
        done; \
        if test "$$dot_seen" = "no"; then \
          echo "d..:" >>"$$mf"; \
          echo "        $$""(MAKE) $$""(AM_MAKEFLAGS) -C . $$target-am" 
>>"$$mf"; \
        fi; \
        $(MAKE) $(AM_MAKEFLAGS) -f "$$mf"
---8<--

This way, if you have multiple directories each with a target
involving `sleep 60`, you can now run `make -j` at the toplevel and
complete the whole thing in 60 rather than 60*x seconds.

There are only few unsolved points:

* delete $md again
* making this conditional on an automake option e.g.
  AM_INIT_AUTOMAKE([parallel-recursion]), because a lot of projects
  likely depend on serial recursion

Reply via email to