diff --git a/NEWS b/NEWS index 5efa8bad..e1eeb531 100644 --- a/NEWS +++ b/NEWS @@ -39,6 +39,11 @@ 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 .NOTINTERMEDIATE special target + .NOTINTERMEDIATE Disables intermediate behavior for specific files, for all + files built using a pattern, or for the entire makefile. + Implementation provided by Dmitry Goncharov <dgoncharov@users.sf.net> + * New feature: The $(let ...) function This function allows user-defined functions to define a set of local variables: values can be assigned to these variables from within the diff --git a/doc/make.texi b/doc/make.texi index 1464d0ca..db4d7e7a 100644 --- a/doc/make.texi +++ b/doc/make.texi @@ -2969,6 +2969,18 @@ The targets which @code{.INTERMEDIATE} depends on are treated as intermediate files. @xref{Chained Rules, ,Chains of Implicit Rules}. @code{.INTERMEDIATE} with no prerequisites has no effect. +@findex .NOTINTERMEDIATE +@item .NOTINTERMEDIATE +@cindex notintermediate targets, explicit + +Prerequisites of the special target @code{.NOTINTERMEDIATE} are never +considered intermediate files. @xref{Chained Rules, ,Chains of Implicit Rules}. +@code{.NOTINTERMEDIATE} with no prerequisites causes all targets to be treated +as notintermediate. + +If the prerequisite is a target pattern then targets that are built using that +pattern rule are not considered intermediate. + @findex .SECONDARY @item .SECONDARY @cindex secondary targets @@ -9994,22 +10006,26 @@ file as intermediate by listing it as a prerequisite of the special target @code{.INTERMEDIATE}. This takes effect even if the file is mentioned explicitly in some other way. +Listing a file as a prerequisite of the special target +@code{.NOTINTERMEDIATE} forces it to not be considered intermediate +(just as any other mention of the file will do). Also, listing the +target pattern of a pattern rule as a prerequisite of +@code{.NOTINTERMEDIATE} ensures that no targets generated using that +pattern rule are considered intermediate. + +You can disable intermediate files completely in your makefile by +providing @code{.NOTINTERMEDIATE} as a target with no prerequisites: +in that case it applies to every file in the makefile. + @cindex intermediate files, preserving @cindex preserving intermediate files @cindex secondary files -You can prevent automatic deletion of an intermediate file by marking it -as a @dfn{secondary} file. To do this, list it as a prerequisite of the -special target @code{.SECONDARY}. When a file is secondary, @code{make} -will not create the file merely because it does not already exist, but -@code{make} does not automatically delete the file. Marking a file as -secondary also marks it as intermediate. - -You can list the target pattern of an implicit rule (such as @samp{%.o}) -as a prerequisite of the special target @code{.PRECIOUS} to preserve -intermediate files made by implicit rules whose target patterns match -that file's name; see @ref{Interrupts}.@refill -@cindex preserving with @code{.PRECIOUS} -@cindex @code{.PRECIOUS} intermediate files +If you do not want @code{make} to create a file merely because it does +not already exist, but you also do not want @code{make} to +automatically delete the file, you can mark it as a @dfn{secondary} +file. To do this, list it as a prerequisite of the special target +@code{.SECONDARY}. Marking a file as secondary also marks it as +intermediate. A chain can involve more than two implicit rules. For example, it is possible to make a file @file{foo} from @file{RCS/foo.y,v} by running RCS, diff --git a/src/file.c b/src/file.c index 76503750..80696b8e 100644 --- a/src/file.c +++ b/src/file.c @@ -62,6 +62,9 @@ static struct hash_table files; /* Whether or not .SECONDARY with no prerequisites was given. */ static int all_secondary = 0; +/* Whether or not .NOTINTERMEDIATE with no prerequisites was given. */ +static int no_intermediates = 0; + /* Access the hash table of all file records. lookup_file given a name, return the struct file * for that name, or nil if there is none. @@ -327,13 +330,16 @@ rehash_file (struct file *from_file, const char *to_hname) #define MERGE(field) to_file->field |= from_file->field MERGE (precious); + MERGE (loaded); MERGE (tried_implicit); MERGE (updating); MERGE (updated); MERGE (is_target); MERGE (cmd_target); MERGE (phony); - MERGE (loaded); + /* Don't merge intermediate because this file might be pre-existing */ + MERGE (secondary); + MERGE (notintermediate); MERGE (ignore_vpath); #undef MERGE @@ -386,7 +392,7 @@ remove_intermediates (int sig) given on the command line, and it's either a -include makefile or it's not precious. */ if (f->intermediate && (f->dontcare || !f->precious) - && !f->secondary && !f->cmd_target) + && !f->secondary && !f->notintermediate && !f->cmd_target) { int status; if (f->update_status == us_none) @@ -671,10 +677,18 @@ snap_file (const void *item, void *arg) if (!second_expansion) f->updating = 0; - /* If .SECONDARY is set with no deps, mark all targets as intermediate. */ - if (all_secondary) + /* More specific setting has priority. */ + + /* If .SECONDARY is set with no deps, mark all targets as intermediate, + unless the target is a prereq of .NOTINTERMEDIATE. */ + if (all_secondary && !f->notintermediate) f->intermediate = 1; + /* If .NOTINTERMEDIATE is set with no deps, mark all targets as + notintermediate, unless the target is a prereq of .INTERMEDIATE. */ + if (no_intermediates && !f->intermediate && !f->secondary) + f->notintermediate = 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)); @@ -776,11 +790,32 @@ snap_deps (void) f2->mtime_before_update = NONEXISTENT_MTIME; } + for (f = lookup_file (".NOTINTERMEDIATE"); f != 0; f = f->prev) + /* Mark .NOTINTERMEDIATE deps as notintermediate files. */ + if (f->deps) + for (d = f->deps; d != 0; d = d->next) + for (f2 = d->file; f2 != 0; f2 = f2->prev) + f2->notintermediate = 1; + /* .NOTINTERMEDIATE with no deps marks all files as notintermediate. */ + else + no_intermediates = 1; + + /* The same file connot be both .INTERMEDIATE and .NOTINTERMEDIATE. + However, it is possible for a file to be .INTERMEDIATE and also match a + .NOTINTERMEDIATE pattern. In that case, the intermediate file has + priority over the notintermediate pattern. This priority is enforced by + pattern_search. */ + for (f = lookup_file (".INTERMEDIATE"); f != 0; f = f->prev) /* Mark .INTERMEDIATE deps as intermediate files. */ for (d = f->deps; d != 0; d = d->next) for (f2 = d->file; f2 != 0; f2 = f2->prev) - f2->intermediate = 1; + if (f2->notintermediate) + OS (fatal, NILF, + _("%s cannot be both .NOTINTERMEDIATE and .INTERMEDIATE."), + f2->name); + else + f2->intermediate = 1; /* .INTERMEDIATE with no deps does nothing. Marking all files as intermediates is useless since the goal targets would be deleted after they are built. */ @@ -790,11 +825,20 @@ snap_deps (void) if (f->deps) for (d = f->deps; d != 0; d = d->next) for (f2 = d->file; f2 != 0; f2 = f2->prev) + if (f2->notintermediate) + OS (fatal, NILF, + _("%s cannot be both .NOTINTERMEDIATE and .SECONDARY."), + f2->name); + else f2->intermediate = f2->secondary = 1; /* .SECONDARY with no deps listed marks *all* files that way. */ else all_secondary = 1; + if (no_intermediates && all_secondary) + O (fatal, NILF, + _(".NOTINTERMEDIATE and .SECONDARY are mutually exclusive")); + f = lookup_file (".EXPORT_ALL_VARIABLES"); if (f != 0 && f->is_target) export_all_variables = 1; @@ -1038,6 +1082,10 @@ print_file (const void *item) printf (_("# Implicit/static pattern stem: '%s'\n"), f->stem); if (f->intermediate) puts (_("# File is an intermediate prerequisite.")); + if (f->notintermediate) + puts (_("# File is a prerequisite of .NOTINTERMEDIATE.")); + if (f->secondary) + puts (_("# File is secondary (prerequisite of .SECONDARY).")); if (f->also_make != 0) { const struct dep *d; diff --git a/src/filedef.h b/src/filedef.h index 972f8533..36701b76 100644 --- a/src/filedef.h +++ b/src/filedef.h @@ -98,6 +98,8 @@ struct file unsigned int intermediate:1;/* Nonzero if this is an intermediate file. */ unsigned int secondary:1; /* Nonzero means remove_intermediates should not delete it. */ + unsigned int notintermediate:1; /* Nonzero means a file is a prereq to + .NOTINTERMEDIATE. */ unsigned int dontcare:1; /* Nonzero if no complaint is to be made if this target cannot be remade. */ unsigned int ignore_vpath:1;/* Nonzero if we threw out VPATH name. */ diff --git a/src/implicit.c b/src/implicit.c index 5bbd6b79..dec61428 100644 --- a/src/implicit.c +++ b/src/implicit.c @@ -909,7 +909,8 @@ pattern_search (struct file *file, int archive, f->pat_searched = imf->pat_searched; f->also_make = imf->also_make; f->is_target = 1; - f->intermediate = !pat->is_explicit; + f->notintermediate = imf->notintermediate; + f->intermediate = !(pat->is_explicit || f->notintermediate); f->tried_implicit = 1; imf = lookup_file (pat->pattern); @@ -976,11 +977,16 @@ pattern_search (struct file *file, int archive, file->cmds = rule->cmds; file->is_target = 1; - /* Set precious flag. */ + /* Set precious and notintermediate flags. */ { struct file *f = lookup_file (rule->targets[tryrules[foundrule].matches]); - if (f && f->precious) - file->precious = 1; + if (f) + { + if (f->precious) + file->precious = 1; + if (f->notintermediate) + file->notintermediate = 1; + } } /* If this rule builds other targets, too, put the others into FILE's @@ -1009,8 +1015,13 @@ pattern_search (struct file *file, int archive, /* Set precious flag. */ f = lookup_file (rule->targets[ri]); - if (f && f->precious) - new->file->precious = 1; + if (f) + { + if (f->precious) + new->file->precious = 1; + if (f->notintermediate) + new->file->notintermediate = 1; + } /* Set the is_target flag so that this file is not treated as intermediate by the pattern rule search algorithm and diff --git a/src/main.c b/src/main.c index 0f8a66dd..54060f2f 100644 --- a/src/main.c +++ b/src/main.c @@ -1330,7 +1330,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 extra-prereqs" + " grouped-target extra-prereqs notintermediate" #ifndef NO_ARCHIVES " archives" #endif diff --git a/tests/scripts/targets/NOTINTERMEDIATE b/tests/scripts/targets/NOTINTERMEDIATE new file mode 100644 index 00000000..bf4cb52e --- /dev/null +++ b/tests/scripts/targets/NOTINTERMEDIATE @@ -0,0 +1,119 @@ +# -*-perl-*- + +$description = "Test the behaviour of the .NOTINTERMEDIATE target."; + +$details = "\ +Test the behavior of the .NOTINTERMEDIATE special target.\n"; + +touch('hello.z'); +unlink('hello.x'); + + +# Test 1. A file which matches a .NOTINTERMEDIATE pattern is not intermediate. +run_make_test(q! +hello.z: +%.z: %.x; touch $@ +%.x: ; +.NOTINTERMEDIATE: %.q %.x +!, '', "touch hello.z\n"); + +# Test 2. .NOTINTERMEDIATE: %.q pattern has no effect on hello.x. +touch('hello.z'); +run_make_test(q! +hello.z: +%.z: %.x; touch $@ +%.x: ; +.NOTINTERMEDIATE: %.q +!, '', "#MAKE#: 'hello.z' is up to date.\n"); + +# Test 3. A file which is a prereq of .NOTINTERMEDIATE is not intermediate. +run_make_test(q! +hello.z: +%.z: %.x; touch $@ +%.x: ; +.NOTINTERMEDIATE: %.q hello.x +!, '', "touch hello.z\n"); + +# Test 4. .NOTINTERMEDIATE without prerequisites makes everything +# notintermediate. +unlink('hello.z'); +run_make_test(q! +hello.z: +%.z: %.x; touch $@ +%.x: ; +.NOTINTERMEDIATE: +!, '', "touch hello.z\n"); + +# Test 5. Same file cannot be intermediate and notintermediate. +run_make_test(q! +.INTERMEDIATE: hello.x +.NOTINTERMEDIATE: hello.x +!, '', "#MAKE#: *** hello.x cannot be both .NOTINTERMEDIATE and .INTERMEDIATE.. Stop.\n", 512); + +# Test 6. Same file cannot be secondary and notintermediate. +run_make_test(q! +.SECONDARY: hello.x +.NOTINTERMEDIATE: hello.x +!, '', "#MAKE#: *** hello.x cannot be both .NOTINTERMEDIATE and .SECONDARY.. Stop.\n", 512); + +# Test 7. All .SECONDARY and all .NOTINTERMEDIATE are mutually exclusive. +run_make_test(q! +.SECONDARY: +.NOTINTERMEDIATE: +!, '', "#MAKE#: *** .NOTINTERMEDIATE and .SECONDARY are mutually exclusive. Stop.\n", 512); + +# Test 8. .INTERMEDIATE file takes priority over a .NOTINTERMEDIATE pattern. +unlink('hello.x'); +run_make_test(q! +hello.z: +%.z: %.x; touch $@ +%.x: ; +.INTERMEDIATE: hello.x +.NOTINTERMEDIATE: %.q %.x +!, '', "#MAKE#: 'hello.z' is up to date.\n"); + +# Test 9. Everything is notintermediate, except hello.x. +unlink('hello.x'); +run_make_test(q! +hello.z: +%.z: %.x; touch $@ +%.x: ; +.INTERMEDIATE: hello.x +.NOTINTERMEDIATE: +!, '', "#MAKE#: 'hello.z' is up to date.\n"); + +# Test 10. Everything is notintermediate, except hello.x. +unlink('hello.x'); +run_make_test(q! +hello.z: +%.z: %.x; touch $@ +%.x: ; +.SECONDARY: hello.x +.NOTINTERMEDIATE: +!, '', "#MAKE#: 'hello.z' is up to date.\n"); + +# Test 11. Everything is secondary, except %.q, hello.x. +unlink('hello.x'); +run_make_test(q! +hello.z: +%.z: %.x; touch $@ +%.x: ; +.NOTINTERMEDIATE: %.q hello.x +.SECONDARY: +!, '', "touch hello.z\n"); + +# Test 12. Everything is secondary, except %.q %.x. +unlink('hello.x'); +run_make_test(q! +hello.z: +%.z: %.x; touch $@ +%.x: ; +.NOTINTERMEDIATE: %.q %.x +.SECONDARY: +!, '', "touch hello.z\n"); + + + +unlink('hello.z'); +# This tells the test driver that the perl test script executed properly. +1;