diff options
-rw-r--r-- | NEWS | 15 | ||||
-rw-r--r-- | doc/make.texi | 134 | ||||
-rw-r--r-- | src/dep.h | 6 | ||||
-rw-r--r-- | src/file.c | 25 | ||||
-rw-r--r-- | src/implicit.c | 10 | ||||
-rw-r--r-- | src/read.c | 69 | ||||
-rw-r--r-- | src/remake.c | 7 | ||||
-rw-r--r-- | src/rule.c | 26 | ||||
-rw-r--r-- | src/shuffle.c | 11 | ||||
-rw-r--r-- | tests/scripts/targets/WAIT | 193 |
10 files changed, 445 insertions, 51 deletions
@@ -46,6 +46,21 @@ https://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=109&se https://www.gnu.org/software/gnulib/manual/html_node/C99-features-assumed.html The configure script should verify the compiler has these features. +* New feature: The .WAIT special target + If the .WAIT target appears between two prerequisites of a target, then + GNU make will wait for all of the targets to the left of .WAIT in the list + to complete before starting any of the targets to the right of .WAIT. + This feature is available in some other versions of make, and it will be + required by an upcoming version of the POSIX standard for make. + Different patches were made by Alexey Neyman <alex.neyman@auriga.ru> (2005) + and Steffen Nurpmeso <steffen@sdaoden.eu> (2020) that were useful but the + result is a different implementation (closer to Alexey's idea). + +* New feature: .NOTPARALLEL accepts prerequisites + If the .NOTPARALLEL special target has prerequisites then all prerequisites + of those targets will be run serially (as if .WAIT was specified between + each prerequisite). + * New feature: The .NOTINTERMEDIATE special target .NOTINTERMEDIATE Disables intermediate behavior for specific files, for all files built using a pattern, or for the entire makefile. diff --git a/doc/make.texi b/doc/make.texi index b1ff72ef..14ad2a37 100644 --- a/doc/make.texi +++ b/doc/make.texi @@ -224,6 +224,7 @@ Recipe Execution Parallel Execution +* Parallel Disable:: Disabling parallel execution * Parallel Output:: Handling output during parallel execution * Parallel Input:: Handling input during parallel execution @@ -3179,11 +3180,15 @@ Variables to a Sub-@code{make}}. @item .NOTPARALLEL @cindex parallel execution, overriding -If @code{.NOTPARALLEL} is mentioned as a target, then this invocation -of @code{make} will be run serially, even if the @samp{-j} option is -given. Any recursively invoked @code{make} command will still run -recipes in parallel (unless its makefile also contains this target). -Any prerequisites on this target are ignored. +If @code{.NOTPARALLEL} is mentioned as a target with no prerequisites, all +targets in this invocation of @code{make} will be run serially, even if the +@samp{-j} option is given. Any recursively invoked @code{make} command will +still run recipes in parallel (unless its makefile also contains this target). + +If @code{.NOTPARALLEL} has targets as prerequisites, then all the +prerequisites of those targets will be run serially. This implicitly adds a +@code{.WAIT} between each prerequisite of the listed targets. @xref{Parallel +Disable, , Disabling Parallel Execution}. @findex .ONESHELL @item .ONESHELL @@ -3191,8 +3196,8 @@ Any prerequisites on this target are ignored. If @code{.ONESHELL} is mentioned as a target, then when a target is built all lines of the recipe will be given to a single invocation of -the shell rather than each line being invoked separately -(@pxref{Execution, ,Recipe Execution}). +the shell rather than each line being invoked separately. +@xref{Execution, ,Recipe Execution}. @findex .POSIX @item .POSIX @@ -4329,13 +4334,12 @@ directory along your @code{PATH}. @cindex @code{-j} @cindex @code{--jobs} -GNU @code{make} knows how to execute several recipes at once. -Normally, @code{make} will execute only one recipe at a time, waiting -for it to finish before executing the next. However, the @samp{-j} or -@samp{--jobs} option tells @code{make} to execute many recipes -simultaneously. You can inhibit parallelism in a particular makefile -with the @code{.NOTPARALLEL} pseudo-target (@pxref{Special -Targets,Special Built-in Target Names}). +GNU @code{make} knows how to execute several recipes at once. Normally, +@code{make} will execute only one recipe at a time, waiting for it to finish +before executing the next. However, the @samp{-j} or @samp{--jobs} option +tells @code{make} to execute many recipes simultaneously. You can inhibit +parallelism for some or all targets from within the makefile (@pxref{Parallel +Disable, ,Disabling Parallel Execution}). On MS-DOS, the @samp{-j} option has no effect, since that system doesn't support multi-processing. @@ -4389,11 +4393,109 @@ average goes below that limit, or until all the other jobs finish. By default, there is no load limit. @menu +* Parallel Disable:: Disabling parallel execution * Parallel Output:: Handling output during parallel execution * Parallel Input:: Handling input during parallel execution @end menu -@node Parallel Output, Parallel Input, Parallel, Parallel +@node Parallel Disable, Parallel Output, Parallel, Parallel +@subsection Disabling Parallel Execution +@cindex disabling parallel execution +@cindex parallel execution, disabling + +If a makefile completely and accurately defines the dependency relationships +between all of its targets, then @code{make} will correctly build the goals +regardless of whether parallel execution is enabled or not. This is the ideal +way to write makefiles. + +However, sometimes some or all of the targets in a makefile cannot be executed +in parallel and it's not feasible to add the prerequisites needed to inform +@code{make}. In that case the makefile can use various methods to disable +parallel execution. + +@cindex .NOTPARALLEL special target +@findex .NOTPARALLEL +If the @code{.NOTPARALLEL} special target with no prerequisites is specified +anywhere then the entire instance of @code{make} will be run serially, +regardless of the parallel setting. For example: + +@example +@group +all: one two three +one two three: ; @@sleep 1; echo $@@ + +.NOTPARALLEL: +@end group +@end example + +Regardless of how @code{make} is invoked, the targets @file{one}, @file{two}, +and @file{three} will be run serially. + +If the @code{.NOTPARALLEL} special target has prerequisites, then each of +those prerequisites will be considered a target and all prerequisites of these +targets will be run serially. Note that only when building this target will +the prerequisites be run serially: if some other target lists the same +prerequisites and is not in @code{.NOTPARALLEL} then these prerequisites may +be run in parallel. For example: + +@example +@group +all: base notparallel + +base: one two three +notparallel: one two three + +one two three: ; @@sleep 1; echo $@@ + +.NOTPARALLEL: notparallel +@end group +@end example + +Here @samp{make -j base} will run the targets @file{one}, @file{two}, and +@file{three} in parallel, while @samp{make -j notparallel} will run them +serially. If you run @samp{make -j all} then they @emph{will} be run in +parallel since @file{base} lists them as prerequisites and is not serialized. + +The @code{.NOTPARALLEL} target should not have commands. + +@cindex .WAIT special target +@findex .WAIT +Finally you can control the serialization of specific prerequisites in a +fine-grained way using the @code{.WAIT} special target. When this target +appears in a prerequisite list and parallel execution is enabled, @code{make} +will not build any of the prerequisites to the @emph{right} of @code{.WAIT} +until all prerequisites to the @emph{left} of @code{.WAIT} have completed. +For example: + +@example +@group +all: one two .WAIT three +one two three: ; @@sleep 1; echo $@@ +@end group +@end example + +If parallel execution is enabled, @code{make} will try to build @file{one} and +@file{two} in parallel but will not try to build @file{three} until both are +complete. + +As with targets provided to @code{.NOTPARALLEL}, @code{.WAIT} has an effect +only when building the target in whose prerequisite list it appears. If the +same prerequisites are present in other targets, without @code{.WAIT}, then +they may still be run in parallel. Because of this, @code{.WAIT} is an +unreliable way to impose ordering than defining a prerequisite relationship. +However it is easy to use and may suffice for simple needs. + +The @code{.WAIT} prerequisite will not be present in any of the automatic +variables for the rule. + +You can create an actual target @code{.WAIT} in your makefile for portability +but this is not required to use this feature. If a @code{.WAIT} target is +created it should not have prerequisites or commands. + +The @code{.WAIT} feature is also implemented in other versions of @code{make} +and it's specified in the POSIX standard for @code{make}. + +@node Parallel Output, Parallel Input, Parallel Disable, Parallel @subsection Output During Parallel Execution @cindex output during parallel execution @cindex parallel execution, output during @@ -9575,6 +9677,8 @@ The order in which prerequisites are listed in automatic variables is not changed by this option. The @code{.NOTPARALLEL} pseudo-target disables shuffling for that makefile. +Also any prerequisite list which contains @code{.WAIT} will not be shuffled. +@xref{Parallel Disable, ,Disabling Parallel Execution}. The @samp{--shuffle=} option accepts these values: @@ -30,11 +30,11 @@ struct nameseq These flags are saved in the 'flags' field of each 'struct goaldep' in the chain returned by 'read_all_makefiles'. */ +#define RM_NOFLAG 0 #define RM_NO_DEFAULT_GOAL (1 << 0) /* Do not set default goal. */ #define RM_INCLUDED (1 << 1) /* Search makefile search path. */ #define RM_DONTCARE (1 << 2) /* No error if it doesn't exist. */ #define RM_NO_TILDE (1 << 3) /* Don't expand ~ in file name. */ -#define RM_NOFLAG 0 /* Structure representing one dependency of a file. Each struct file's 'deps' points to a chain of these, through 'next'. @@ -54,7 +54,8 @@ struct nameseq unsigned int staticpattern : 1; \ unsigned int need_2nd_expansion : 1; \ unsigned int ignore_automatic_vars : 1; \ - unsigned int is_explicit : 1 + unsigned int is_explicit : 1; \ + unsigned int wait_here : 1 struct dep { @@ -81,6 +82,7 @@ struct goaldep #define PARSEFS_EXISTS 0x0008 #define PARSEFS_NOCACHE 0x0010 #define PARSEFS_ONEWORD 0x0020 +#define PARSEFS_WAIT 0x0040 #define PARSE_FILE_SEQ(_s,_t,_c,_p,_f) \ (_t *)parse_file_seq ((_s),sizeof (_t),(_c),(_p),(_f)) @@ -455,8 +455,7 @@ remove_intermediates (int sig) struct dep * split_prereqs (char *p) { - struct dep *new = PARSE_FILE_SEQ (&p, struct dep, MAP_PIPE, NULL, - PARSEFS_NONE); + struct dep *new = PARSE_FILE_SEQ (&p, struct dep, MAP_PIPE, NULL, PARSEFS_WAIT); if (*p) { @@ -465,7 +464,7 @@ split_prereqs (char *p) struct dep *ood; ++p; - ood = PARSE_SIMPLE_SEQ (&p, struct dep); + ood = PARSE_FILE_SEQ (&p, struct dep, MAP_NUL, NULL, PARSEFS_WAIT); if (! new) new = ood; @@ -888,7 +887,19 @@ snap_deps (void) f = lookup_file (".NOTPARALLEL"); if (f != 0 && f->is_target) - not_parallel = 1; + { + struct dep *d2; + + if (!f->deps) + not_parallel = 1; + else + /* Set a wait point between every prerequisite of each target. */ + for (d = f->deps; d != NULL; d = d->next) + for (f2 = d->file; f2 != NULL; f2 = f2->prev) + if (f2->deps) + for (d2 = f2->deps->next; d2 != NULL; d2 = d2->next) + d2->wait_here = 1; + } { struct dep *prereqs = expand_extra_prereqs (lookup_variable (STRING_SIZE_TUPLE(".EXTRA_PREREQS"))); @@ -1039,17 +1050,17 @@ print_prereqs (const struct dep *deps) /* Print all normal dependencies; note any order-only deps. */ for (; deps != 0; deps = deps->next) if (! deps->ignore_mtime) - printf (" %s", dep_name (deps)); + printf (" %s%s", deps->wait_here ? ".WAIT " : "", dep_name (deps)); else if (! ood) ood = deps; /* Print order-only deps, if we have any. */ if (ood) { - printf (" | %s", dep_name (ood)); + printf (" | %s%s", ood->wait_here ? ".WAIT " : "", dep_name (ood)); for (ood = ood->next; ood != 0; ood = ood->next) if (ood->ignore_mtime) - printf (" %s", dep_name (ood)); + printf (" %s%s", ood->wait_here ? ".WAIT " : "", dep_name (ood)); } putchar ('\n'); diff --git a/src/implicit.c b/src/implicit.c index d3853726..43f0cd24 100644 --- a/src/implicit.c +++ b/src/implicit.c @@ -160,6 +160,7 @@ struct patdeps unsigned int ignore_mtime : 1; unsigned int ignore_automatic_vars : 1; unsigned int is_explicit : 1; + unsigned int wait_here : 1; }; /* This structure stores information about pattern rules that we need @@ -569,12 +570,14 @@ pattern_search (struct file *file, int archive, /* Parse the expanded string. It might have wildcards. */ p = depname; - dl = PARSE_FILE_SEQ (&p, struct dep, MAP_NUL, NULL, PARSEFS_ONEWORD); + dl = PARSE_FILE_SEQ (&p, struct dep, MAP_NUL, NULL, + PARSEFS_ONEWORD|PARSEFS_WAIT); for (d = dl; d != NULL; d = d->next) { ++deps_found; d->ignore_mtime = dep->ignore_mtime; d->ignore_automatic_vars = dep->ignore_automatic_vars; + d->wait_here |= dep->wait_here; d->is_explicit = is_explicit; } @@ -708,7 +711,8 @@ pattern_search (struct file *file, int archive, /* Parse the expanded string. */ struct dep *dp = PARSE_FILE_SEQ (&p, struct dep, order_only ? MAP_NUL : MAP_PIPE, - add_dir ? pathdir : NULL, PARSEFS_NONE); + add_dir ? pathdir : NULL, + PARSEFS_WAIT); *dptr = dp; for (d = dp; d != NULL; d = d->next) @@ -773,6 +777,7 @@ pattern_search (struct file *file, int archive, memset (pat, '\0', sizeof (struct patdeps)); pat->ignore_mtime = d->ignore_mtime; pat->ignore_automatic_vars = d->ignore_automatic_vars; + pat->wait_here = d->wait_here; pat->is_explicit = d->is_explicit; DBS (DB_IMPLICIT, @@ -1021,6 +1026,7 @@ pattern_search (struct file *file, int archive, dep->ignore_mtime = pat->ignore_mtime; dep->is_explicit = pat->is_explicit; dep->ignore_automatic_vars = pat->ignore_automatic_vars; + dep->wait_here = pat->wait_here; s = strcache_add (pat->name); if (recursions) dep->name = s; @@ -144,7 +144,8 @@ static void do_undefine (char *name, enum variable_origin origin, static struct variable *do_define (char *name, enum variable_origin origin, struct ebuffer *ebuf); static int conditional_line (char *line, size_t len, const floc *flocp); -static void check_specials (const struct nameseq *file, int set_default); +static void check_specials (struct nameseq *filep, int set_default); +static void check_special_file (struct file *filep, const floc *flocp); static void record_files (struct nameseq *filenames, int are_also_makes, const char *pattern, const char *pattern_percent, char *depstr, @@ -1883,16 +1884,12 @@ record_target_var (struct nameseq *filenames, char *defn, and it have been mis-parsed because these special targets haven't been considered yet. */ -static void check_specials (const struct nameseq* files, int set_default) +static void +check_specials (struct nameseq *files, int set_default) { - const struct nameseq *t = files; - - /* Unlikely but ... */ - if (posix_pedantic && second_expansion && one_shell - && (!set_default || default_goal_var->value[0] == '\0')) - return; + struct nameseq *t; - for (; t != 0; t = t->next) + for (t = files; t != NULL; t = t->next) { const char* nm = t->name; @@ -1977,6 +1974,34 @@ static void check_specials (const struct nameseq* files, int set_default) } } } + +/* Check for special targets. We used to do this in record_files() but that's + too late: by the time we get there we'll have already parsed the next line + and it have been mis-parsed because these special targets haven't been + considered yet. */ + +static void +check_special_file (struct file *file, const floc *flocp) +{ + if (streq (file->name, ".WAIT")) + { + static unsigned int wpre = 0, wcmd = 0; + + if (!wpre && file->deps) + { + O (error, flocp, _(".WAIT should not have prerequisites")); + wpre = 1; + } + + if (!wcmd && file->cmds) + { + O (error, flocp, _(".WAIT should not have commands")); + wcmd = 1; + } + + return; + } +} /* Record a description line for files FILENAMES, with dependencies DEPS, commands to execute described @@ -2265,6 +2290,8 @@ record_files (struct nameseq *filenames, int are_also_makes, name = f->name; + check_special_file (f, flocp); + /* All done! Set up for the next one. */ if (nextf == 0) break; @@ -3143,6 +3170,8 @@ tilde_expand (const char *name) PARSEFS_EXISTS - Only return globbed files that actually exist (cannot also set NOGLOB) PARSEFS_NOCACHE - Do not add filenames to the strcache (caller frees) + PARSEFS_ONEWORD - Don't break the sequence on whitespace + PARSEFS_WAIT - Assume struct dep and handle .WAIT */ void * @@ -3158,16 +3187,22 @@ parse_file_seq (char **stringp, size_t size, int stopmap, struct nameseq *new = 0; struct nameseq **newp = &new; #define NEWELT(_n) do { \ - const char *__n = (_n); \ - *newp = xcalloc (size); \ - (*newp)->name = (cachep ? strcache_add (__n) : xstrdup (__n)); \ - newp = &(*newp)->next; \ + struct nameseq *_ns = xcalloc (size); \ + const char *__n = (_n); \ + _ns->name = (cachep ? strcache_add (__n) : xstrdup (__n)); \ + if (found_wait) { \ + ((struct dep*)_ns)->wait_here = 1; \ + found_wait = 0; \ + } \ + *newp = _ns; \ + newp = &_ns->next; \ } while(0) char *p; glob_t gl; char *tp; int findmap = stopmap|MAP_VMSCOMMA|MAP_NUL; + int found_wait = 0; if (NONE_SET (flags, PARSEFS_ONEWORD)) findmap |= MAP_BLANK; @@ -3241,6 +3276,14 @@ parse_file_seq (char **stringp, size_t size, int stopmap, if (!p) p = s + strlen (s); + if (ANY_SET (flags, PARSEFS_WAIT) && p - s == CSTRLEN (".WAIT") + && memcmp (s, ".WAIT", CSTRLEN (".WAIT")) == 0) + { + /* Note that we found a .WAIT for the next dep but skip it. */ + found_wait = 1; + continue; + } + /* Strip leading "this directory" references. */ if (NONE_SET (flags, PARSEFS_NOSTRIP)) #ifdef VMS diff --git a/src/remake.c b/src/remake.c index 228b8af6..59bd644c 100644 --- a/src/remake.c +++ b/src/remake.c @@ -551,6 +551,9 @@ update_file_1 (struct file *file, unsigned int depth) d = du->shuf ? du->shuf : du; + if (d->wait_here && running) + break; + check_renamed (d->file); mtime = file_mtime (d->file); @@ -631,6 +634,10 @@ update_file_1 (struct file *file, unsigned int depth) for (du = file->deps; du != 0; du = du->next) { d = du->shuf ? du->shuf : du; + + if (d->wait_here && running) + break; + if (d->file->intermediate) { enum update_status new; @@ -65,21 +65,22 @@ static size_t maxsuffix; space separated rule prerequisites, followed by a pipe, followed by order-only prerequisites, if present. */ -const char *get_rule_defn (struct rule *r) +const char * +get_rule_defn (struct rule *r) { if (r->_defn == NULL) { + size_t len = 8; /* Reserve for ":: ", " | ", and nul. */ unsigned int k; - ptrdiff_t len = 8; // Reserve for ":: ", " | " and the null terminator. char *p; const char *sep = ""; const struct dep *dep, *ood = 0; for (k = 0; k < r->num; ++k) - len += r->lens[k] + 1; // Add one for a space. + len += r->lens[k] + 1; for (dep = r->deps; dep; dep = dep->next) - len += strlen (dep_name (dep)) + 1; // Add one for a space. + len += strlen (dep_name (dep)) + (dep->wait_here ? CSTRLEN (" .WAIT") : 0) + 1; p = r->_defn = xmalloc (len); for (k = 0; k < r->num; ++k, sep = " ") @@ -91,18 +92,25 @@ const char *get_rule_defn (struct rule *r) /* Copy all normal dependencies; note any order-only deps. */ for (dep = r->deps; dep; dep = dep->next) if (dep->ignore_mtime == 0) - p = mempcpy (mempcpy (p, " ", 1), dep_name (dep), - strlen (dep_name (dep))); + { + if (dep->wait_here) + p = mempcpy (p, STRING_SIZE_TUPLE (" .WAIT")); + p = mempcpy (mempcpy (p, " ", 1), dep_name (dep), + strlen (dep_name (dep))); + } else if (ood == 0) ood = dep; /* Copy order-only deps, if we have any. */ for (sep = " | "; ood; ood = ood->next, sep = " ") if (ood->ignore_mtime) - p = mempcpy (mempcpy (p, sep, strlen (sep)), dep_name (ood), - strlen (dep_name (ood))); + { + p = mempcpy (p, sep, strlen (sep)); + if (ood->wait_here) + p = mempcpy (p, STRING_SIZE_TUPLE (".WAIT ")); + p = mempcpy (p, dep_name (ood), strlen (dep_name (ood))); + } *p = '\0'; - assert (p - r->_defn < len); } return r->_defn; diff --git a/src/shuffle.c b/src/shuffle.c index ca54e528..95f60bea 100644 --- a/src/shuffle.c +++ b/src/shuffle.c @@ -157,7 +157,13 @@ shuffle_deps (struct dep *deps) void **dp; for (dep = deps; dep; dep = dep->next) - ndeps++; + { + /* Do not reshuffle prerequisites if any .WAIT is present. */ + if (dep->wait_here) + return; + + ndeps++; + } if (ndeps == 0) return; @@ -215,8 +221,7 @@ shuffle_deps_recursive (struct dep *deps) if (config.mode == sm_none) return; - /* Do not reshuffle targets if Makefile is explicitly marked as - problematic for parallelism. */ + /* Do not reshuffle prerequisites if .NOTPARALLEL was specified. */ if (not_parallel) return; diff --git a/tests/scripts/targets/WAIT b/tests/scripts/targets/WAIT new file mode 100644 index 00000000..24cf9179 --- /dev/null +++ b/tests/scripts/targets/WAIT @@ -0,0 +1,193 @@ +# -*-perl-*- + +$description = "Test the behaviour of the .WAIT target."; + +$details = ""; + +# Ensure .WAIT doesn't appear in any automatic variables + +run_make_test(q! +all: .WAIT pre1 .WAIT pre2 | .WAIT pre3 ; @echo '<=$< ^=$^ ?=$? +=$+ |=$|' +pre1 pre2 pre3:; + +# This is just here so we don't fail with older versions of make +.WAIT: +!, + '', '<=pre1 ^=pre1 pre2 ?=pre1 pre2 +=pre1 pre2 |=pre3'); + +run_make_test(q! +.SECONDEXPANSION: +all: $$(pre) ; @echo '<=$< ^=$^ ?=$? +=$+ |=$|' +pre1 pre2 pre3:; + +pre = .WAIT pre1 .WAIT pre2 | .WAIT pre3 +# This is just here so we don't fail with older versions of make +.WAIT: +!, + '', '<=pre1 ^=pre1 pre2 ?=pre1 pre2 +=pre1 pre2 |=pre3'); + +run_make_test(q! +all: pre +p% : .WAIT p%1 .WAIT p%2 | .WAIT p%3; @echo '<=$< ^=$^ ?=$? +=$+ |=$|' +pre1 pre2 pre3: ; + +# This is just here so we don't fail with older versions of make +.WAIT: +!, + '', "<=pre1 ^=pre1 pre2 ?=pre1 pre2 +=pre1 pre2 |=pre3\n"); + +# Unfortunately I don't think we can get away from using sleep here; at least +# I can't think of any way to make sure .WAIT works without it. Even with it, +# it's not reliable (in that even if .WAIT is not working we MIGHT succeed the +# test--it shouldn't ever be the case that we fail the test unexpectedly). +# That makes this test suite slow to run :-/. + +run_make_test(q! +all : pre1 .WAIT pre2 +pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@ +pre2: ; @#HELPER# -q out $@ + +# This is just here so we don't fail with older versions of make +.WAIT: +!, + '-j10', "start-pre1\nend-pre1\npre2\n"); + +# Ensure .WAIT doesn't add extra a dependency between its targets + +run_make_test(undef, '-j10 pre2', "pre2\n"); + +# Ensure .WAIT doesn't wait between all targets + +run_make_test(q! +all : pre1 .WAIT pre2 pre3 +pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@ +pre2: ; @#HELPER# -q out start-$@ file TWO wait THREE out end-$@ +pre3: ; @#HELPER# -q wait TWO out $@ file THREE + +# This is just here so we don't fail with older versions of make +.WAIT: +!, + '-j10', "start-pre1\nend-pre1\nstart-pre2\npre3\nend-pre2\n"); + +unlink(qw(TWO THREE)); + +# Ensure .WAIT waits for ALL targets on the left before ANY targets on the right + +run_make_test(q! +all : pre1 pre2 .WAIT post1 post2 +pre1: ; @#HELPER# -q out start-$@ file PRE1 wait PRE2 sleep 1 out end-$@ +pre2: ; @#HELPER# -q wait PRE1 out $@ file PRE2 + +post1: ; @#HELPER# -q wait POST2 out $@ file POST1 +post2: ; @#HELPER# -q file POST2 wait POST1 out $@ + +# This is just here so we don't fail with older versions of make +.WAIT: +!, + '-j10', "start-pre1\npre2\nend-pre1\npost1\npost2\n"); + +unlink(qw(PRE1 PRE2 POST1 POST2)); + +# See if .WAIT takes effect between different lists of prereqs +# In the current implementation, .WAIT waits only between two prerequisites +# in a given target. These same two targets might be run in a different +# order if they appear as prerequisites of another target. This is the way +# other implementations of .WAIT work. I personally think it's gross and +# makes .WAIT just a toy when it comes to ordering, but it's much simpler +# to implement than creating an actual edge in the DAG to represent .WAIT +# and since that's what users expect, we'll do the same for now. + +run_make_test(q! +all : one two +one: pre1 .WAIT pre2 +two: pre2 pre1 +pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@ +pre2: ; @#HELPER# -q out $@ + +# This is just here so we don't fail with older versions of make +.WAIT: +!, + '-j10', "start-pre1\npre2\nend-pre1\n"); + +# Check that .WAIT works with pattern rules + +run_make_test(q! +all: pre +p% : p%1 .WAIT p%2; +pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@ +pre2: ; @#HELPER# -q out $@ + +# This is just here so we don't fail with older versions of make +.WAIT: +!, + '-j10', "start-pre1\nend-pre1\npre2\n"); + +# Check that .WAIT works with secondarily expanded rules + +run_make_test(q! +.SECONDEXPANSION: +all: $$(pre) +pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@ +pre2: ; @#HELPER# -q out $@ +pre3: ; @#HELPER# -q out $@ + +pre = .WAIT pre1 .WAIT pre2 | .WAIT pre3 + +# This is just here so we don't fail with older versions of make +.WAIT: +!, + '-j10', "start-pre1\nend-pre1\npre2\npre3\n"); + +# Verify NOTPARALLEL works + +run_make_test(q! +all : pre1 pre2 +pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@ +pre2: ; @#HELPER# -q out $@ + +.NOTPARALLEL: +!, + '-j10', "start-pre1\nend-pre1\npre2\n"); + +run_make_test(q! +all : p1 .WAIT np1 + +p1: pre1 pre2 +pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@ +pre2: ; @#HELPER# -q out $@ + +np1: npre1 npre2 +npre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@ +npre2: ; @#HELPER# -q out $@ + +.NOTPARALLEL: np1 +!, + '-j10', "start-pre1\npre2\nend-pre1\nstart-npre1\nend-npre1\nnpre2\n"); + +# Ensure we don't shuffle if .WAIT is set + +run_make_test(q! +all : pre1 .WAIT pre2 +pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@ +pre2: ; @#HELPER# -q out $@ + +# This is just here so we don't fail with older versions of make +.WAIT: +!, + '-j10 --shuffle=reverse', "start-pre1\nend-pre1\npre2\n"); + +# Warn about invalid .WAIT definitions + +run_make_test(q! +.WAIT: foo +.WAIT: ; echo oops +all:;@: +!, + '', "#MAKEFILE#:2: .WAIT should not have prerequisites\n#MAKEFILE#:3: .WAIT should not have commands\n"); + +# This tells the test driver that the perl test script executed properly. +1; + +### Local Variables: +### eval: (setq whitespace-action (delq 'auto-cleanup whitespace-action)) +### End: |