/* Target file management for GNU Make.
-Copyright (C) 1988-2020 Free Software Foundation, Inc.
+Copyright (C) 1988-2022 Free Software Foundation, Inc.
This file is part of GNU Make.
GNU Make is free software; you can redistribute it and/or modify it under the
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
-this program. If not, see <http://www.gnu.org/licenses/>. */
+this program. If not, see <https://www.gnu.org/licenses/>. */
#include "makeint.h"
#include "variable.h"
#include "debug.h"
#include "hash.h"
+#include "shuffle.h"
/* Remember whether snap_deps has been invoked: we need this to be sure we
/* 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.
while (name[0] == '<' && name[1] == '>' && name[2] != '\0')
name += 2;
#endif
- while (name[0] == '.'
-#ifdef HAVE_DOS_PATHS
- && (name[1] == '/' || name[1] == '\\')
-#else
- && name[1] == '/'
-#endif
- && name[2] != '\0')
+ while (name[0] == '.' && ISDIRSEP (name[1]) && name[2] != '\0')
{
name += 2;
- while (*name == '/'
-#ifdef HAVE_DOS_PATHS
- || *name == '\\'
-#endif
- )
+ while (ISDIRSEP (*name))
/* Skip following slashes: ".//foo" is "foo", not "/foo". */
++name;
}
{
size_t l = strlen (from_file->name);
/* We have two sets of commands. We will go with the
- one given in the rule explicitly mentioning this name,
+ one given in the rule found through directory search,
but give a message to let the user know what's going on. */
if (to_file->cmds->fileinfo.filenm != 0)
error (&from_file->cmds->fileinfo,
l + strlen (to_file->cmds->fileinfo.filenm) + INTSTR_LENGTH,
_("Recipe was specified for file '%s' at %s:%lu,"),
- from_file->name, to_file->cmds->fileinfo.filenm,
- to_file->cmds->fileinfo.lineno);
+ from_file->name, from_file->cmds->fileinfo.filenm,
+ from_file->cmds->fileinfo.lineno);
else
error (&from_file->cmds->fileinfo, l,
_("Recipe for file '%s' was found by implicit rule search,"),
from_file->name, to_hname);
error (&from_file->cmds->fileinfo, l,
_("Recipe for '%s' will be ignored in favor of the one for '%s'."),
- to_hname, from_file->name);
+ from_file->name, 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 (is_explicit);
+ MERGE (secondary);
+ MERGE (notintermediate);
MERGE (ignore_vpath);
+ MERGE (snapped);
#undef MERGE
to_file->builtin = 0;
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)
}
}
if (status < 0)
- perror_with_name ("unlink: ", f->name);
+ {
+ perror_with_name ("\nunlink: ", f->name);
+ /* Start printing over. */
+ doneany = 0;
+ }
}
}
}
struct dep *
split_prereqs (char *p)
{
- struct dep *new = PARSE_FILE_SEQ (&p, struct dep, MAP_PIPE, NULL,
- PARSEFS_NONE);
+ struct dep *new = PARSE_FILE_SEQ (&p, struct dep, MAP_PIPE, NULL, PARSEFS_WAIT);
if (*p)
{
struct dep *ood;
++p;
- ood = PARSE_SIMPLE_SEQ (&p, struct dep);
+ ood = PARSE_FILE_SEQ (&p, struct dep, MAP_NUL, NULL, PARSEFS_WAIT);
if (! new)
new = ood;
if (stem)
{
const char *pattern = "%";
- char *buffer = variable_expand ("");
struct dep *dp = deps, *dl = 0;
while (dp != 0)
if (stem[0] == '\0')
{
memmove (percent, percent+1, strlen (percent));
- o = variable_buffer_output (buffer, nm, strlen (nm) + 1);
+ o = variable_buffer_output (variable_buffer, nm,
+ strlen (nm) + 1);
}
else
- o = patsubst_expand_pat (buffer, stem, pattern, nm,
+ o = patsubst_expand_pat (variable_buffer, stem, pattern, nm,
pattern+1, percent+1);
/* If the name expanded to the empty string, ignore it. */
- if (buffer[0] == '\0')
+ if (variable_buffer[0] == '\0')
{
struct dep *df = dp;
if (dp == deps)
}
/* Save the name. */
- dp->name = strcache_add_len (buffer, o - buffer);
+ dp->name = strcache_add_len (variable_buffer,
+ o - variable_buffer);
}
dp->stem = stem;
dp->staticpattern = 1;
d1->file = enter_file (d1->name);
d1->staticpattern = 0;
d1->name = 0;
+ if (!stem)
+ /* This file is explicitly mentioned as a prereq. */
+ d1->file->is_explicit = 1;
}
return deps;
}
-/* Expand and parse each dependency line. */
-static void
+/* Expand and parse each dependency line.
+ For each dependency of the file, make the 'struct dep' point
+ at the appropriate 'struct file' (which may have to be created). */
+void
expand_deps (struct file *f)
{
struct dep *d;
struct dep **dp;
- const char *file_stem = f->stem;
+ const char *fstem;
int initialized = 0;
+ int changed_dep = 0;
- f->updating = 0;
+ if (f->snapped)
+ return;
+ f->snapped = 1;
/* Walk through the dependencies. For any dependency that needs 2nd
expansion, expand it then insert the result into the list. */
{
char *p;
struct dep *new, *next;
- char *name = (char *)d->name;
if (! d->name || ! d->need_2nd_expansion)
{
continue;
}
- /* If it's from a static pattern rule, convert the patterns into
- "$*" so they'll expand properly. */
+ /* If it's from a static pattern rule, convert the initial pattern in
+ each word to "$*" so they'll expand properly. */
if (d->staticpattern)
{
- char *o = variable_expand ("");
- o = subst_expand (o, name, "%", "$*", 1, 2, 0);
- *o = '\0';
- free (name);
- d->name = name = xstrdup (variable_buffer);
- d->staticpattern = 0;
+ const char *cs = d->name;
+ size_t nperc = 0;
+
+ /* Count the number of % in the string. */
+ while ((cs = strchr (cs, '%')) != NULL)
+ {
+ ++nperc;
+ ++cs;
+ }
+
+ if (nperc)
+ {
+ /* Allocate enough space to replace all % with $*. */
+ size_t slen = strlen (d->name) + nperc + 1;
+ const char *pcs = d->name;
+ char *name = xmalloc (slen);
+ char *s = name;
+
+ /* Substitute the first % in each word. */
+ cs = strchr (pcs, '%');
+
+ while (cs)
+ {
+ s = mempcpy (s, pcs, cs - pcs);
+ *(s++) = '$';
+ *(s++) = '*';
+ pcs = ++cs;
+
+ /* Find the first % after the next whitespace. */
+ cs = strchr (end_of_token (cs), '%');
+ }
+ strcpy (s, pcs);
+
+ free ((char*)d->name);
+ d->name = name;
+ }
}
/* We're going to do second expansion so initialize file variables for
initialized = 1;
}
- if (d->stem != 0)
- f->stem = d->stem;
-
- set_file_variables (f);
+ set_file_variables (f, d->stem ? d->stem : f->stem);
+ /* Perform second expansion. */
p = variable_expand_for_file (d->name, f);
- if (d->stem != 0)
- f->stem = file_stem;
-
- /* At this point we don't need the name anymore: free it. */
- free (name);
+ /* Free the un-expanded name. */
+ free ((char*)d->name);
/* Parse the prerequisites and enter them into the file database. */
- new = enter_prereqs (split_prereqs (p), d->stem);
+ new = split_prereqs (p);
/* If there were no prereqs here (blank!) then throw this one out. */
if (new == 0)
{
*dp = d->next;
+ changed_dep = 1;
free_dep (d);
d = *dp;
continue;
}
/* Add newly parsed prerequisites. */
+ fstem = d->stem;
next = d->next;
+ changed_dep = 1;
+ free_dep (d);
*dp = new;
- for (dp = &new->next, d = new->next; d != 0; dp = &d->next, d = d->next)
- ;
+ for (dp = &new, d = new; d != 0; dp = &d->next, d = d->next)
+ {
+ d->file = lookup_file (d->name);
+ if (d->file == 0)
+ d->file = enter_file (d->name);
+ d->name = 0;
+ d->stem = fstem;
+ if (!fstem)
+ /* This file is explicitly mentioned as a prereq. */
+ d->file->is_explicit = 1;
+ }
*dp = next;
d = *dp;
}
+
+ /* Shuffle mode assumes '->next' and '->shuf' links both traverse the same
+ dependencies (in different sequences). Regenerate '->shuf' so we don't
+ refer to stale data. */
+ if (changed_dep)
+ shuffle_deps_recursive (f->deps);
}
/* Add extra prereqs to the file in question. */
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));
}
}
-/* For each dependency of each file, make the 'struct dep' point
- at the appropriate 'struct file' (which may have to be created).
-
- Also mark the files depended on by .PRECIOUS, .PHONY, .SILENT,
+/* Mark the files depended on by .PRECIOUS, .PHONY, .SILENT,
and various other special targets. */
void
longer define new targets. */
snapped_deps = 1;
- /* Perform second expansion and enter each dependency name as a file. We
- must use hash_dump() here because within these loops we likely add new
- files to the table, possibly causing an in-situ table expansion.
-
- We only need to do this if second_expansion has been defined; if it
- hasn't then all deps were expanded as the makefile was read in. If we
- ever change make to be able to unset .SECONDARY_EXPANSION this will have
- to change. */
-
- if (second_expansion)
- {
- struct file **file_slot_0 = (struct file **) hash_dump (&files, 0, 0);
- struct file **file_end = file_slot_0 + files.ht_fill;
- struct file **file_slot;
- const char *suffixes;
-
- /* Expand .SUFFIXES: its prerequisites are used for $$* calc. */
- f = lookup_file (".SUFFIXES");
- suffixes = f ? f->name : 0;
- for (; f != 0; f = f->prev)
- expand_deps (f);
-
- /* For every target that's not .SUFFIXES, expand its prerequisites. */
-
- for (file_slot = file_slot_0; file_slot < file_end; file_slot++)
- for (f = *file_slot; f != 0; f = f->prev)
- if (f->name != suffixes)
- expand_deps (f);
- free (file_slot_0);
- }
-
/* Now manage all the special targets. */
for (f = lookup_file (".PRECIOUS"); f != 0; f = f->prev)
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. */
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;
f = lookup_file (".NOTPARALLEL");
if (f != 0 && f->is_target)
- not_parallel = 1;
+ {
+ struct dep *d2;
+
+ if (!f->deps)
+ not_parallel = 1;
+ else
+ /* Set a wait point between every prerequisite of each target. */
+ for (d = f->deps; d != NULL; d = d->next)
+ for (f2 = d->file; f2 != NULL; f2 = f2->prev)
+ if (f2->deps)
+ for (d2 = f2->deps->next; d2 != NULL; d2 = d2->next)
+ d2->wait_here = 1;
+ }
{
struct dep *prereqs = expand_extra_prereqs (lookup_variable (STRING_SIZE_TUPLE(".EXTRA_PREREQS")));
struct tm *tm = localtime (&t);
if (tm)
- sprintf (p, "%04d-%02d-%02d %02d:%02d:%02d",
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ {
+ intmax_t year = tm->tm_year;
+ sprintf (p, "%04" PRIdMAX "-%02d-%02d %02d:%02d:%02d",
+ year + 1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ }
else if (t < 0)
- sprintf (p, "%ld", (long) t);
+ sprintf (p, "%" PRIdMAX, (intmax_t) t);
else
- sprintf (p, "%lu", (unsigned long) t);
+ sprintf (p, "%" PRIuMAX, (uintmax_t) t);
p += strlen (p);
/* Append nanoseconds as a fraction, but remove trailing zeros. We don't
/* Print all normal dependencies; note any order-only deps. */
for (; deps != 0; deps = deps->next)
if (! deps->ignore_mtime)
- printf (" %s", dep_name (deps));
+ printf (" %s%s", deps->wait_here ? ".WAIT " : "", dep_name (deps));
else if (! ood)
ood = deps;
/* Print order-only deps, if we have any. */
if (ood)
{
- printf (" | %s", dep_name (ood));
+ printf (" | %s%s", ood->wait_here ? ".WAIT " : "", dep_name (ood));
for (ood = ood->next; ood != 0; ood = ood->next)
if (ood->ignore_mtime)
- printf (" %s", dep_name (ood));
+ printf (" %s%s", ood->wait_here ? ".WAIT " : "", dep_name (ood));
}
putchar ('\n');
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;
p = &value[off];
}
- memcpy (p, f->name, l);
- p += l;
+ p = mempcpy (p, f->name, l);
*(p++) = ' ';
}
*(p-1) = '\0';