From 5d647da46a3a1c41cc547caa75f6d8fe318d102e Mon Sep 17 00:00:00 2001 From: Christof Warlich Date: Fri, 5 Jul 2019 10:51:54 +0200 Subject: [PATCH 1/2] Add support for dependency-only prerequisites. --- doc/make.texi | 76 ++++++++++++++++++++++++++++++++++++++++++++++---- src/commands.c | 8 +++--- src/dep.h | 3 +- src/file.c | 56 +++++++++++++++++++++++++++++++++++-- src/main.c | 1 + src/makeint.h | 1 + 6 files changed, 132 insertions(+), 13 deletions(-) diff --git a/doc/make.texi b/doc/make.texi index 12f70c6..9e58802 100644 --- a/doc/make.texi +++ b/doc/make.texi @@ -155,7 +155,7 @@ Writing Rules * Rule Example:: An example explained. * Rule Syntax:: General syntax explained. -* Prerequisite Types:: There are two types of prerequisites. +* Prerequisite Types:: There are three types of prerequisites. * Wildcards:: Using wildcard characters such as `*'. * Directory Search:: Searching other directories for source files. * Phony Targets:: Using a target that is not a real file's name. @@ -1883,7 +1883,7 @@ the makefile (often with a target called @samp{all}). @menu * Rule Example:: An example explained. * Rule Syntax:: General syntax explained. -* Prerequisite Types:: There are two types of prerequisites. +* Prerequisite Types:: There are three types of prerequisites. * Wildcards:: Using wildcard characters such as `*'. * Directory Search:: Searching other directories for source files. * Phony Targets:: Using a target that is not a real file's name. @@ -2022,10 +2022,13 @@ extra features (@pxref{Recipes, ,Writing Recipes in Rules}). @cindex normal prerequisites @cindex prerequisites, order-only @cindex order-only prerequisites -There are actually two different types of prerequisites understood by +There are actually three different types of prerequisites understood by GNU @code{make}: normal prerequisites such as described in the -previous section, and @dfn{order-only} prerequisites. A normal -prerequisite makes two statements: first, it imposes an order in which +previous section, @dfn{order-only} prerequisites, and @dfn{dependency-only} +prerequisites. + +A normal prerequisite makes two statements: +first, it imposes an order in which recipes will be invoked: the recipes for all prerequisites of a target will be completed before the recipe for the target is run. Second, it imposes a dependency relationship: if any prerequisite is newer than @@ -2085,6 +2088,69 @@ Now the rule to create the @file{objdir} directory will be run, if needed, before any @samp{.o} is built, but no @samp{.o} will be built because the @file{objdir} directory timestamp changed. +Finally, the third type of prerequisites, i.e. depenency-only +prerequisites, may be specified by placing a "smaller than" symbol +(@code{<}) in the prerequisite list: any prerequisites to the left +of the "smaller than" symbol are normal (or order-only); any prerequisites +to the right are dependency-only (and possibly order-only as well). + +Dependency-only prerequisites behave almost identical to the other +two prerequisite types, with one important exception: They do not +contribute to any of their list-type related automatic variables. +Thus, dependency-only prerequisites are not added to neither +of the automatic variable lists $^, $+, $?, $*, $(^F), $(+F), $(?F), +$(*F), $(^D), $(+D), $(?D) and $(*D), and prerequisites that are both +dependency-only and order-only are not added to neither of the +automatic variable lists $|, $(|F), $(|D). + +The rationale behind dependency-only dependencies is to make it more +easy to extend dependency lists of existing Makefiles. An example may +illustrate this: + +The following code may be considered as a snippet of a large and +maybe rather complex Makefile: + +@example +myappl: main.o file1.o file2.o + gcc -o $@ $^ +@end example + +At a first glance, it lists all the relevant prerequisites, but a +second thought reveals that this is just not true: The target +certainly also depends on the compiler frontend, the linker backend +and the Makefile itself. + +Thus, a more complete snippet should look more like this: + +@example +myappl: main.o file1.o file2.o /usr/bin/gcc /usr/bin/ld Makefile + gcc -o $@ $(filter %.o, $^) +@end example + +Please note the need for the newly introduced GNU Make's $(filter ) +function besides the additional prerequisites. + +But for big projects, say the Linux kernel or a toolchain build, +it would be rather laborious to change and fix all the Makefiles +accordingly, and it would be more than questionable if such patches +would be welcomed by every project. Fortunately, with dependency-only +prerequisites at hand, the upstream Makefiles do not need to be +changed at all. Instead, it's sufficient to list the additional +dependencies as dependency-only prerequisites in another Makefile +that just includes the upstream Makefile. To continue with our +example (and assuming the related upstream Makefile was just called +@code{Makefile}, we could most conviniently add a @code{GNUmakefile} +with the following content: + +@example +include Makefile +myappl: < /usr/bin/gcc /usr/bin/ld Makefile +@end example + +Calling @code{make} now would prefer @code{GNUmakefile} over +@code{Makefile}, thus respecting the additional prerequisites +without affecting the related reciepe. + @node Wildcards, Directory Search, Prerequisite Types, Rules @section Using Wildcard Characters in File Names @cindex wildcard diff --git a/src/commands.c b/src/commands.c index dd47851..7c08c01 100644 --- a/src/commands.c +++ b/src/commands.c @@ -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 baa64df..bedf41a 100644 --- a/src/dep.h +++ b/src/dep.h @@ -48,7 +48,8 @@ struct nameseq unsigned short changed : 1; \ unsigned short ignore_mtime : 1; \ unsigned short staticpattern : 1; \ - unsigned short need_2nd_expansion : 1 + unsigned short need_2nd_expansion : 1; \ + unsigned short ignore_automatic_vars : 1 struct dep { diff --git a/src/file.c b/src/file.c index c20fcf8..f624836 100644 --- a/src/file.c +++ b/src/file.c @@ -442,17 +442,41 @@ remove_intermediates (int sig) struct dep * split_prereqs (char *p) { - struct dep *new = PARSE_FILE_SEQ (&p, struct dep, MAP_PIPE, NULL, + struct dep *new = PARSE_FILE_SEQ (&p, struct dep, MAP_PIPE|MAP_DEP, NULL, PARSEFS_NONE); - if (*p) + if (*p == '<') + { + /* Files that follow '<' are "dependency-only" prerequisites that do not + contribute to the automatic variables $^, $+, $*, $? and their + $(xF) and $(xD) counterparts. */ + struct dep *dod; + + ++p; + dod = PARSE_FILE_SEQ (&p, struct dep, MAP_PIPE, NULL, PARSEFS_NONE); + + if (! new) + new = dod; + else + { + struct dep *dp; + for (dp = new; dp->next != NULL; dp = dp->next) + ; + dp->next = dod; + } + + for (; dod != NULL; dod = dod->next) + dod->ignore_automatic_vars = 1; + } + + if (*p == '|') { /* Files that follow '|' are "order-only" prerequisites that satisfy the dependency by existing: their modification times are irrelevant. */ struct dep *ood; ++p; - ood = PARSE_SIMPLE_SEQ (&p, struct dep); + ood = PARSE_FILE_SEQ (&p, struct dep, MAP_DEP, NULL, PARSEFS_NONE); if (! new) new = ood; @@ -468,6 +492,32 @@ split_prereqs (char *p) ood->ignore_mtime = 1; } + if (*p) + { + /* Files that follow '<' and '|' are "dependency-only" and "order-only" prerequisites, + their modification times are irrelevant and they do not contribute to the automatic variable $|. */ + struct dep *ood_dod; + + ++p; + ood_dod = PARSE_SIMPLE_SEQ (&p, struct dep); + + if (! new) + new = ood_dod; + else + { + struct dep *dp; + for (dp = new; dp->next != NULL; dp = dp->next) + ; + dp->next = ood_dod; + } + + for (; ood_dod != NULL; ood_dod = ood_dod->next) + { + ood_dod->ignore_automatic_vars = 1; + ood_dod->ignore_mtime = 1; + } + } + return new; } diff --git a/src/main.c b/src/main.c index afc7fd9..c3fe228 100644 --- a/src/main.c +++ b/src/main.c @@ -637,6 +637,7 @@ initialize_stopchar_map (void) stopchar_map[(int)'='] = MAP_EQUALS; stopchar_map[(int)':'] = MAP_COLON; stopchar_map[(int)'|'] = MAP_PIPE; + stopchar_map[(int)'<'] = MAP_DEP; stopchar_map[(int)'.'] = MAP_DOT | MAP_USERFUNC; stopchar_map[(int)','] = MAP_COMMA; stopchar_map[(int)'('] = MAP_VARSEP; diff --git a/src/makeint.h b/src/makeint.h index dd6e894..2d38efc 100644 --- a/src/makeint.h +++ b/src/makeint.h @@ -402,6 +402,7 @@ extern int unixy_shell; #define MAP_PIPE 0x0100 #define MAP_DOT 0x0200 #define MAP_COMMA 0x0400 +#define MAP_DEP 0x0800 /* These are the valid characters for a user-defined function. */ #define MAP_USERFUNC 0x2000 -- 2.17.1