summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Goncharov <dgoncharov@users.sf.net>2021-05-30 13:48:29 -0400
committerPaul Smith <psmith@gnu.org>2021-07-25 17:15:38 -0400
commit33468b3f31d65815152c11f32dc5c4384a6267b5 (patch)
treedd6ea8bec02ce9b16af732d0a229b7e74e63d75e
parent1cffd0a203bc1477671a02aafd6360b7bb964670 (diff)
downloadmake-git-33468b3f31d65815152c11f32dc5c4384a6267b5.tar.gz
[SV 60297] Add .NOTINTERMEDIATE special target
Support a new special target, .NOTINTERMEDIATE. Any file or pattern prerequisite of this target will never be considered intermediate. This differs from .SECONDARY in that .SECONDARY files won't be deleted but they will still not be built if they are missing. .NOTINTERMEDIATE files are treated the same way as a target which is explicitly mentioned in the makefile. This is mostly useful with patterns; obviously mentioning a target explicitly here is enough in and of itself to make something not intermediate. Some adjustments made by psmith@gnu.org * NEWS: Announce the new feature. * doc/make.texi (Special Targets): Document .NOTINTERMEDIATE. (Chained Rules): Describe how to use .NOTINTERMEDIATE. * src/main.c (main): Add "notintermediate" to the .FEATURES variable. * src/filedef.h (struct file): Add "notintermediate" flag. * src/file.c (no_intermediates): Mark global .NOTINTERMEDIATE. (snap_file): Support .NOTINTERMEDIATE special target. Throw an error if the same target is marked both .NOTINTERMEDIATE and .SECONDARY or .INTERMEDIATE. (rehash_file): Merge intermediate, notintermediate, secondary flags. (remove_intermediates): Check notintermediate flag before removing. (print_file): * src/implicit.c (pattern_search): Set notintermediate based on the pattern. * tests/scripts/targets/NOTINTERMEDIATE: Add a new test suite.
-rw-r--r--NEWS5
-rw-r--r--doc/make.texi42
-rw-r--r--src/file.c58
-rw-r--r--src/filedef.h2
-rw-r--r--src/implicit.c23
-rw-r--r--src/main.c2
-rw-r--r--tests/scripts/targets/NOTINTERMEDIATE119
7 files changed, 226 insertions, 25 deletions
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;