diff --git a/AUTHORS b/AUTHORS index f3c83c61..284c530f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -68,6 +68,7 @@ Other contributors: David Boyce Frank Heckenbach Kaz Kylheku + Christof Warlich With suggestions/comments/bug reports from a cast of ... well ... hundreds, anyway :) diff --git a/NEWS b/NEWS index ba9401de..4c3e8a2e 100644 --- a/NEWS +++ b/NEWS @@ -48,6 +48,13 @@ http://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=108&set search for 'grouped-target' in the .FEATURES special variable. Implementation contributed by Kaz Kylheku +* New feature: .EXTRA_PREREQS variable + Words in this variable are considered prerequisites of targets but they are + not added to any of the automatic variable values when expanding the + recipe. This variable can either be global (applies to all targets) or + a target-specific variable. To detect this feature search for 'extra-prereqs' + in the .FEATURES special variable. + * Makefiles can now specify the '-j' option in their MAKEFLAGS variable and this will cause make to enable that parallelism mode. diff --git a/doc/make.texi b/doc/make.texi index 3e61f298..b210a0d4 100644 --- a/doc/make.texi +++ b/doc/make.texi @@ -6631,6 +6631,57 @@ Supports dynamically loadable objects for creating custom extensions. Expands to a list of directories that @code{make} searches for included makefiles (@pxref{Include, , Including Other Makefiles}). +@vindex .EXTRA_PREREQS @r{(prerequisites not added to automatic variables)} +@item .EXTRA_PREREQS +Each word in this variable is a new prerequisite which is added to +targets for which it is set. These prerequisites differ from normal +prerequisites in that they do not appear in any of the automatic +variables (@pxref{Automatic Variables}). This allows prerequisites to +be defined which do not impact the recipe. + +Consider a rule to link a program: + +@example +myprog: myprog.o file1.o file2.o + $(CC) $(CFLAGS) $(LDFLAGS) -o $@@ $^ $(LDLIBS) +@end example + +Now suppose you want to enhance this makefile to ensure that updates +to the compiler cause the program to be re-linked. You can add the +compiler as a prerequisite, but you must ensure that it's not passed +as an argument to link command. You'll need something like this: + +@example +myprog: myprog.o file1.o file2.o $(CC) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@@ $(filter-out $(CC),$^) $(LDLIBS) +@end example + +Then consider having multiple extra prerequisites: they would all have +to be filtered out. Using @code{.EXTRA_PREREQS} and target-specific +variables provides a simpler solution: + +@example +myprog: myprog.o file1.o file2.o + $(CC) $(CFLAGS) $(LDFLAGS) -o $@@ $^ $(LDLIBS) +myprog: .EXTRA_PREREQS = $(CC) +@end example + +This feature can also be useful if you want to add prerequisites to a +makefile you cannot easily modify: you can create a new file such as +@file{extra.mk}: + +@example +myprog: .EXTRA_PREREQS = $(CC) +@end example + +then invoke @code{make -f extra.mk -f Makefile}. + +Setting @code{.EXTRA_PREREQS} globally will cause those prerequisites +to be added to all targets (which did not themselves override it with +a target-specific value). Note @code{make} is smart enough not to add +a prerequisite listed in @code{.EXTRA_PREREQS} as a prerequisite to +itself. + @end table @node Conditionals, Functions, Using Variables, Top diff --git a/src/commands.c b/src/commands.c index ddb66431..d7bdded7 100644 --- a/src/commands.c +++ b/src/commands.c @@ -133,7 +133,7 @@ set_file_variables (struct file *file) /* $< is the first not order-only dependency. */ less = ""; for (d = file->deps; d != 0; d = d->next) - if (!d->ignore_mtime) + if (!d->ignore_mtime && !d->ignore_automatic_vars) { if (!d->need_2nd_expansion) less = dep_name (d); @@ -178,7 +178,7 @@ set_file_variables (struct file *file) bar_len = 0; for (d = file->deps; d != 0; d = d->next) { - if (!d->need_2nd_expansion) + if (!d->need_2nd_expansion && !d->ignore_automatic_vars) { if (d->ignore_mtime) bar_len += strlen (dep_name (d)) + 1; @@ -200,7 +200,7 @@ set_file_variables (struct file *file) qmark_len = plus_len + 1; /* Will be this or less. */ for (d = file->deps; d != 0; d = d->next) - if (! d->ignore_mtime && ! d->need_2nd_expansion) + if (! d->ignore_mtime && ! d->need_2nd_expansion && ! d->ignore_automatic_vars) { const char *c = dep_name (d); @@ -247,7 +247,7 @@ set_file_variables (struct file *file) for (d = file->deps; d != 0; d = d->next) { - if (d->need_2nd_expansion) + if (d->need_2nd_expansion || d->ignore_automatic_vars) continue; slot = hash_find_slot (&dep_hash, d); @@ -269,7 +269,7 @@ set_file_variables (struct file *file) { const char *c; - if (d->need_2nd_expansion || hash_find_item (&dep_hash, d) != d) + if (d->need_2nd_expansion || d->ignore_automatic_vars || hash_find_item (&dep_hash, d) != d) continue; c = dep_name (d); diff --git a/src/dep.h b/src/dep.h index 87872600..ef5e7460 100644 --- a/src/dep.h +++ b/src/dep.h @@ -48,7 +48,8 @@ struct nameseq unsigned int changed : 1; \ unsigned int ignore_mtime : 1; \ unsigned int staticpattern : 1; \ - unsigned int need_2nd_expansion : 1 + unsigned int need_2nd_expansion : 1; \ + unsigned int ignore_automatic_vars : 1 struct dep { diff --git a/src/file.c b/src/file.c index c20fcf8b..b46c6c6f 100644 --- a/src/file.c +++ b/src/file.c @@ -550,15 +550,6 @@ enter_prereqs (struct dep *deps, const char *stem) return deps; } -/* Set the intermediate flag. */ - -static void -set_intermediate (const void *item) -{ - struct file *f = (struct file *) item; - f->intermediate = 1; -} - /* Expand and parse each dependency line. */ static void expand_deps (struct file *f) @@ -644,13 +635,69 @@ expand_deps (struct file *f) } } -/* Reset the updating flag. */ +/* Add extra prereqs to the file in question. */ + +struct dep * +expand_extra_prereqs (const struct variable *extra) +{ + struct dep *prereqs = extra ? split_prereqs (variable_expand (extra->value)) : NULL; + + for (struct dep *d = prereqs; d; d = d->next) + { + d->file = lookup_file (d->name); + if (!d->file) + d->file = enter_file (d->name); + d->name = NULL; + d->ignore_automatic_vars = 1; + } + + return prereqs; +} + +/* Perform per-file snap operations. */ static void -reset_updating (const void *item) +snap_file (const void *item, void *arg) { - struct file *f = (struct file *) item; - f->updating = 0; + struct file *f = (struct file*)item; + struct dep *prereqs = NULL; + + /* If we're not doing second expansion then reset updating. */ + if (!second_expansion) + f->updating = 0; + + /* If .SECONDARY is set with no deps, mark all targets as intermediate. */ + if (all_secondary) + f->intermediate = 1; + + /* If .EXTRA_PREREQS is set, add them as ignored by automatic variables. */ + if (f->variables) + prereqs = expand_extra_prereqs (lookup_variable_in_set (STRING_SIZE_TUPLE(".EXTRA_PREREQS"), f->variables->set)); + + else if (f->is_target) + prereqs = copy_dep_chain (arg); + + if (prereqs) + { + struct dep *d; + for (d = prereqs; d; d = d->next) + if (streq (f->name, dep_name (d))) + /* Skip circular dependencies. */ + break; + + if (d) + /* We broke early: must have found a circular dependency. */ + free_dep_chain (prereqs); + else if (!f->deps) + f->deps = prereqs; + else + { + d = f->deps; + while (d->next) + d = d->next; + d->next = prereqs; + } + } } /* For each dependency of each file, make the 'struct dep' point @@ -700,9 +747,6 @@ snap_deps (void) expand_deps (f); free (file_slot_0); } - else - /* We're not doing second expansion, so reset updating. */ - hash_map (&files, reset_updating); /* Now manage all the special targets. */ @@ -744,10 +788,7 @@ snap_deps (void) f2->intermediate = f2->secondary = 1; /* .SECONDARY with no deps listed marks *all* files that way. */ else - { - all_secondary = 1; - hash_map (&files, set_intermediate); - } + all_secondary = 1; f = lookup_file (".EXPORT_ALL_VARIABLES"); if (f != 0 && f->is_target) @@ -779,6 +820,15 @@ snap_deps (void) if (f != 0 && f->is_target) not_parallel = 1; + { + struct dep *prereqs = expand_extra_prereqs (lookup_variable (STRING_SIZE_TUPLE(".EXTRA_PREREQS"))); + + /* Perform per-file snap operations. */ + hash_map_arg(&files, snap_file, prereqs); + + free_dep_chain (prereqs); + } + #ifndef NO_MINUS_C_MINUS_O /* If .POSIX was defined, remove OUTPUT_OPTION to comply. */ /* This needs more work: what if the user sets this in the makefile? diff --git a/src/filedef.h b/src/filedef.h index 329850e7..c33e859f 100644 --- a/src/filedef.h +++ b/src/filedef.h @@ -21,6 +21,11 @@ this program. If not, see . */ #include "hash.h" +struct commands; +struct dep; +struct variable; +struct variable_set_list; + struct file { const char *name; @@ -110,6 +115,7 @@ struct file *lookup_file (const char *name); struct file *enter_file (const char *name); struct dep *split_prereqs (char *prereqstr); struct dep *enter_prereqs (struct dep *prereqs, const char *stem); +struct dep *expand_extra_prereqs (const struct variable *extra); void remove_intermediates (int sig); void snap_deps (void); void rename_file (struct file *file, const char *name); diff --git a/src/implicit.c b/src/implicit.c index 96f453c8..2d442ce4 100644 --- a/src/implicit.c +++ b/src/implicit.c @@ -152,6 +152,7 @@ struct patdeps const char *pattern; struct file *file; unsigned int ignore_mtime : 1; + unsigned int ignore_automatic_vars : 1; }; /* This structure stores information about pattern rules that we need @@ -564,6 +565,7 @@ pattern_search (struct file *file, int archive, { ++deps_found; d->ignore_mtime = dep->ignore_mtime; + d->ignore_automatic_vars = dep->ignore_automatic_vars; } /* We've used up this dep, so next time get a new one. */ @@ -723,6 +725,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; DBS (DB_IMPLICIT, (is_rule @@ -913,6 +916,7 @@ pattern_search (struct file *file, int archive, dep = alloc_dep (); dep->ignore_mtime = pat->ignore_mtime; + dep->ignore_automatic_vars = pat->ignore_automatic_vars; s = strcache_add (pat->name); if (recursions) dep->name = s; diff --git a/src/main.c b/src/main.c index 04d6ba54..761cf5cb 100644 --- a/src/main.c +++ b/src/main.c @@ -1313,7 +1313,7 @@ main (int argc, char **argv, char **envp) { const char *features = "target-specific order-only second-expansion" " else-if shortest-stem undefine oneshell nocomment" - " grouped-target" + " grouped-target extra-prereqs" #ifndef NO_ARCHIVES " archives" #endif @@ -2134,9 +2134,9 @@ main (int argc, char **argv, char **envp) install_default_implicit_rules (); - /* Compute implicit rule limits. */ + /* Compute implicit rule limits and do magic for pattern rules. */ - count_implicit_rule_limits (); + snap_implicit_rules (); /* Construct the listings of directories in VPATH lists. */ diff --git a/src/rule.c b/src/rule.c index 6b01aa33..6645a2f7 100644 --- a/src/rule.c +++ b/src/rule.c @@ -60,36 +60,43 @@ struct file *suffix_file; static size_t maxsuffix; -/* Compute the maximum dependency length and maximum number of - dependencies of all implicit rules. Also sets the subdir - flag for a rule when appropriate, possibly removing the rule - completely when appropriate. */ +/* Compute the maximum dependency length and maximum number of dependencies of + all implicit rules. Also sets the subdir flag for a rule when appropriate, + possibly removing the rule completely when appropriate. + + Add any global EXTRA_PREREQS here as well. */ void -count_implicit_rule_limits (void) +snap_implicit_rules (void) { - char *name; - size_t namelen; - struct rule *rule; + char *name = NULL; + size_t namelen = 0; + struct dep *prereqs = expand_extra_prereqs (lookup_variable (STRING_SIZE_TUPLE(".EXTRA_PREREQS"))); + unsigned int pre_deps = 0; - num_pattern_rules = max_pattern_targets = max_pattern_deps = 0; max_pattern_dep_length = 0; - name = 0; - namelen = 0; - rule = pattern_rules; - while (rule != 0) + for (struct dep *d = prereqs; d; d = d->next) { - unsigned int ndeps = 0; - struct dep *dep; - struct rule *next = rule->next; + size_t l = strlen (dep_name (d)); + if (l > max_pattern_dep_length) + max_pattern_dep_length = l; + ++pre_deps; + } + + num_pattern_rules = max_pattern_targets = max_pattern_deps = 0; + + for (struct rule *rule = pattern_rules; rule; rule = rule->next) + { + unsigned int ndeps = pre_deps; + struct dep *lastdep = NULL; ++num_pattern_rules; if (rule->num > max_pattern_targets) max_pattern_targets = rule->num; - for (dep = rule->deps; dep != 0; dep = dep->next) + for (struct dep *dep = rule->deps; dep != 0; dep = dep->next) { const char *dname = dep_name (dep); size_t len = strlen (dname); @@ -99,17 +106,20 @@ count_implicit_rule_limits (void) const char *p2; if (p == 0) p = strrchr (dname, ':'); - p2 = p != 0 ? strchr (dname, '%') : 0; + p2 = p ? strchr (p, '%') : 0; #else const char *p = strrchr (dname, '/'); - const char *p2 = p != 0 ? strchr (dname, '%') : 0; + const char *p2 = p ? strchr (p, '%') : 0; #endif ndeps++; if (len > max_pattern_dep_length) max_pattern_dep_length = len; - if (p != 0 && p2 > p) + if (!dep->next) + lastdep = dep; + + if (p2) { /* There is a slash before the % in the dep name. Extract the directory name. */ @@ -134,13 +144,20 @@ count_implicit_rule_limits (void) dep->changed = 0; } + if (prereqs) + { + if (lastdep) + lastdep->next = copy_dep_chain (prereqs); + else + rule->deps = copy_dep_chain (prereqs); + } + if (ndeps > max_pattern_deps) max_pattern_deps = ndeps; - - rule = next; } free (name); + free_dep_chain (prereqs); } /* Create a pattern rule from a suffix rule. diff --git a/src/rule.h b/src/rule.h index c0cf8fae..db445aa2 100644 --- a/src/rule.h +++ b/src/rule.h @@ -48,7 +48,7 @@ extern size_t max_pattern_dep_length; extern struct file *suffix_file; -void count_implicit_rule_limits (void); +void snap_implicit_rules (void); void convert_to_pattern (void); void install_pattern_rule (struct pspec *p, int terminal); void create_pattern_rule (const char **targets, const char **target_percents, diff --git a/tests/scripts/variables/EXTRA_PREREQS b/tests/scripts/variables/EXTRA_PREREQS new file mode 100644 index 00000000..57ad597e --- /dev/null +++ b/tests/scripts/variables/EXTRA_PREREQS @@ -0,0 +1,151 @@ +# -*-perl-*- + +$description = "Test the .EXTRA_PREREQS special variable."; +$details = ""; + +# Simple global .EXTRA_PREREQS and automatic variable settings +run_make_test(' +.EXTRA_PREREQS = tick tack +.PHONY: all +all: ; @echo ${.EXTRA_PREREQS}/$@/$