David Boyce <dsb@boyski.com>
Frank Heckenbach <f.heckenbach@fh-soft.de>
Kaz Kylheku <kaz@kylheku.com>
+ Christof Warlich <cwarlich@gmx.de>
With suggestions/comments/bug reports from a cast of ... well ...
hundreds, anyway :)
search for 'grouped-target' in the .FEATURES special variable.
Implementation contributed by Kaz Kylheku <kaz@kylheku.com>
+* 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.
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
/* $< 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);
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;
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);
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);
{
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);
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
{
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)
}
}
-/* 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
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. */
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)
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?
#include "hash.h"
+struct commands;
+struct dep;
+struct variable;
+struct variable_set_list;
+
struct 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);
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
{
++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. */
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
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;
{
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
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. */
static size_t maxsuffix;
\f
-/* 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)
+ {
+ 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 = 0;
- struct dep *dep;
- struct rule *next = 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);
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. */
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);
}
\f
/* Create a pattern rule from a suffix rule.
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,
--- /dev/null
+# -*-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}/$@/$</$^/$?/$+/$|/$*/
+tick tack: ; @echo $@
+',
+ '', "tick\ntack\ntick tack/all///////\n");
+
+# Global .EXTRA_PREREQS and pattern rules
+run_make_test('
+.EXTRA_PREREQS = tick tack
+a%: ; @echo ${.EXTRA_PREREQS}/$@/$</$^/$?/$+/$|/$*/
+tick tack: ; @echo $@
+',
+ 'all', "tick\ntack\ntick tack/all//////ll/\n");
+
+# Simple target-specific .EXTRA_PREREQS and automatic variable settings
+run_make_test('
+.PHONY: all
+all: ; @echo ${.EXTRA_PREREQS}/$@/$</$^/$?/$+/$|/$*/
+all: .EXTRA_PREREQS = tick tack
+tick tack: ; @echo $@
+',
+ '', "tick\ntack\ntick tack/all///////\n");
+
+# Simple pattern-specific .EXTRA_PREREQS and automatic variable settings
+# This is not currently supported :-/
+if (0) {
+ run_make_test('
+.PHONY: all
+all: ; @echo ${.EXTRA_PREREQS}/$@/$</$^/$?/$+/$|/$*/
+a%: .EXTRA_PREREQS = tick tack
+tick tack: ; @echo $@
+',
+ '', "tick\ntack\ntick tack/all///////\n");
+}
+
+touch('hi');
+
+# Basic test target specific .EXTRA_PREREQS:
+run_make_test('
+DEPENDENCY_ONLY_PREREQUISITES = ho hey
+OTHER_PREREQUISITES := foo bar baz
+target: .EXTRA_PREREQS := hi ${DEPENDENCY_ONLY_PREREQUISITES}
+target: ${OTHER_PREREQUISITES} ; @echo ${.EXTRA_PREREQS} $^
+.PHONY: target ${DEPENDENCY_ONLY_PREREQUISITES} ${OTHER_PREREQUISITES}
+${DEPENDENCY_ONLY_PREREQUISITES} ${OTHER_PREREQUISITES}: ; @echo $@
+',
+ '', "foo\nbar\nbaz\nho\nhey\nhi ho hey foo bar baz\n");
+
+# Test target specific .EXTRA_PREREQS and pattern rules:
+run_make_test('
+all: target.dst
+DEPENDENCY_ONLY_PREREQUISITES = ho hey
+target.dst: .EXTRA_PREREQS := hi ${DEPENDENCY_ONLY_PREREQUISITES}
+%.dst: %.src ; @echo ${.EXTRA_PREREQS} $^
+.PHONY: ${DEPENDENCY_ONLY_PREREQUISITES} target.src
+${DEPENDENCY_ONLY_PREREQUISITES} target.src: ; @echo $@
+',
+ '', "target.src\nho\nhey\nhi ho hey target.src\n");
+
+# Test that global .EXTRA_PREREQS are built first:
+run_make_test('
+.EXTRA_PREREQS = hi ho hey
+OTHER_PREREQUISITES := foo bar baz
+target: ${OTHER_PREREQUISITES} ; @echo ${.EXTRA_PREREQS} $^
+.PHONY: target ${.EXTRA_PREREQS} ${OTHER_PREREQUISITES}
+${.EXTRA_PREREQS} ${OTHER_PREREQUISITES}: ; @echo $@
+',
+ '', "hi\nho\nhey\nfoo\nbar\nbaz\nhi ho hey foo bar baz\n");
+
+# Test that target specific .EXTRA_PREREQS override global .EXTRA_PREREQS:
+run_make_test('
+.EXTRA_PREREQS = tick tack
+DEPENDENCY_ONLY_PREREQUISITES = ho hey
+OTHER_PREREQUISITES := foo bar baz
+target: .EXTRA_PREREQS := hi ${DEPENDENCY_ONLY_PREREQUISITES}
+target: ${OTHER_PREREQUISITES} ; @echo ${.EXTRA_PREREQS} $^
+.PHONY: target ${DEPENDENCY_ONLY_PREREQUISITES} ${OTHER_PREREQUISITES} ${.EXTRA_PREREQS}
+${DEPENDENCY_ONLY_PREREQUISITES} ${OTHER_PREREQUISITES} ${.EXTRA_PREREQS}: ; @echo $@
+',
+ '', "tick\ntack\nfoo\nbar\nbaz\nho\nhey\nhi ho hey foo bar baz\n");
+
+# Cleanup:
+unlink('hi');
+
+# Test error reporting of global .EXTRA_PREREQS:
+run_make_test('
+.EXTRA_PREREQS = tick tack
+.PHONY: all
+all: ; @echo ${.EXTRA_PREREQS} $^
+',
+ '', "#MAKE#: *** No rule to make target 'tick', needed by 'all'. Stop.", 512);
+
+# Test error reporting of global .EXTRA_PREREQS and keep-going:
+run_make_test('
+.EXTRA_PREREQS = tick tack
+.PHONY: all
+all: ; @echo ${.EXTRA_PREREQS} $^
+',
+ '-k', "#MAKE#: *** No rule to make target 'tick', needed by 'all'.\n#MAKE#: *** No rule to make target 'tack', needed by 'all'.\n#MAKE#: Target 'all' not remade because of errors.", 512);
+
+# Test error reporting of target specific .EXTRA_PREREQS and keep-going:
+run_make_test('
+all: .EXTRA_PREREQS = tick tack
+.PHONY: all
+all: ; @echo ${.EXTRA_PREREQS} $^
+',
+ '-k',
+ "#MAKE#: *** No rule to make target 'tick', needed by 'all'.
+#MAKE#: *** No rule to make target 'tack', needed by 'all'.
+#MAKE#: Target 'all' not remade because of errors.\n", 512);
+
+# Test wildcard
+
+touch('tick', 'tack');
+
+run_make_test('
+.EXTRA_PREREQS = *ck
+.PHONY: all tick tack
+all: ; @echo ${.EXTRA_PREREQS} $^
+tick tack: ; @echo $@
+',
+ '', "tack\ntick\ntack tick\n");
+
+run_make_test('
+.PHONY: all tick tack
+all: ; @echo ${.EXTRA_PREREQS} $^
+all: .EXTRA_PREREQS = *ck
+tick tack: ; @echo $@
+',
+ '', "tack\ntick\ntack tick\n");
+
+run_make_test('
+.PHONY: tick tack
+a%: ; @echo ${.EXTRA_PREREQS} $^
+.EXTRA_PREREQS = *ck
+tick tack: ; @echo $@
+',
+ 'all', "tack\ntick\ntack tick\n");
+
+unlink('tick', 'tack');
+
+# This tells the test driver that the perl test script executed properly.
+1;