home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993
- 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 terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- GNU Make is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR 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 GNU Make; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- #include "make.h"
- #include "commands.h"
- #include "dep.h"
- #include "file.h"
- #include "variable.h"
-
-
- /* Hash table of files the makefile knows how to make. */
-
- #ifndef FILE_BUCKETS
- #define FILE_BUCKETS 1007
- #endif
- static struct file *files[FILE_BUCKETS];
-
- /* Number of files with the `intermediate' flag set. */
-
- unsigned int num_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.
- enter_file similar, but create one if there is none. */
-
- struct file *
- lookup_file (name)
- char *name;
- {
- register struct file *f;
- register char *n;
- register unsigned int hashval;
-
- if (*name == '\0')
- abort ();
-
- /* This is also done in parse_file_seq, so this is redundant
- for names read from makefiles. It is here for names passed
- on the command line. */
- while (name[0] == '.' && name[1] == '/' && name[2] != '\0')
- {
- name += 2;
- while (*name == '/')
- /* Skip following slashes: ".//foo" is "foo", not "/foo". */
- ++name;
- }
-
- if (*name == '\0')
- /* It was all slashes after a dot. */
- name = "./";
-
- hashval = 0;
- for (n = name; *n != '\0'; ++n)
- HASH (hashval, *n);
- hashval %= FILE_BUCKETS;
-
- for (f = files[hashval]; f != 0; f = f->next)
- if (streq (f->name, name))
- return f;
- return 0;
- }
-
- struct file *
- enter_file (name)
- char *name;
- {
- register struct file *f, *new;
- register char *n;
- register unsigned int hashval;
-
- if (*name == '\0')
- abort ();
-
- hashval = 0;
- for (n = name; *n != '\0'; ++n)
- HASH (hashval, *n);
- hashval %= FILE_BUCKETS;
-
- for (f = files[hashval]; f != 0; f = f->next)
- if (streq (f->name, name))
- break;
-
- if (f != 0 && !f->double_colon)
- return f;
-
- new = (struct file *) xmalloc (sizeof (struct file));
- bzero ((char *) new, sizeof (struct file));
- new->name = name;
- new->update_status = -1;
-
- if (f == 0)
- {
- /* This is a completely new file. */
- new->next = files[hashval];
- files[hashval] = new;
- }
- else
- {
- /* There is already a double-colon entry for this file. */
- while (f->prev != 0)
- f = f->prev;
- f->prev = new;
- }
-
- return new;
- }
-
- /* Rename FILE to NAME. This is not as simple as resetting
- the `name' member, since it must be put in a new hash bucket,
- and possibly merged with an existing file called NAME. */
-
- void
- rename_file (file, name)
- register struct file *file;
- char *name;
- {
- char *oldname = file->name;
- register unsigned int oldhash;
- register char *n;
-
- while (file->renamed != 0)
- file = file->renamed;
-
- /* Find the hash values of the old and new names. */
-
- oldhash = 0;
- for (n = oldname; *n != '\0'; ++n)
- HASH (oldhash, *n);
-
- file_hash_enter (file, name, oldhash, file->name);
- }
-
- void
- file_hash_enter (file, name, oldhash, oldname)
- register struct file *file;
- char *name;
- unsigned int oldhash;
- char *oldname;
- {
- unsigned int oldbucket = oldhash % FILE_BUCKETS;
- register unsigned int newhash, newbucket;
- struct file *oldfile;
- register char *n;
- register struct file *f;
-
- newhash = 0;
- for (n = name; *n != '\0'; ++n)
- HASH (newhash, *n);
- newbucket = newhash % FILE_BUCKETS;
-
- /* Look for an existing file under the new name. */
-
- for (oldfile = files[newbucket]; oldfile != 0; oldfile = oldfile->next)
- if (streq (oldfile->name, name))
- break;
-
- if (oldhash != 0 && (newbucket != oldbucket || oldfile != 0))
- {
- /* Remove FILE from its hash bucket. */
-
- struct file *lastf = 0;
-
- for (f = files[oldbucket]; f != file; f = f->next)
- lastf = f;
-
- if (lastf == 0)
- files[oldbucket] = f->next;
- else
- lastf->next = f->next;
- }
-
- /* Give FILE its new name. */
-
- for (f = file; f != 0; f = f->prev)
- f->name = name;
-
- if (oldfile == 0)
- {
- /* There is no existing file with the new name. */
-
- if (newbucket != oldbucket)
- {
- /* Put FILE in its new hash bucket. */
- file->next = files[newbucket];
- files[newbucket] = file;
- }
- }
- else
- {
- /* There is an existing file with the new name.
- We must merge FILE into the existing file. */
-
- register struct dep *d;
-
- if (file->cmds != 0)
- {
- if (oldfile->cmds == 0)
- oldfile->cmds = file->cmds;
- else if (file->cmds != oldfile->cmds)
- {
- /* We have two sets of commands. We will go with the
- one given in the rule explicitly mentioning this name,
- but give a message to let the user know what's going on. */
- if (oldfile->cmds->filename != 0)
- makefile_error (file->cmds->filename, file->cmds->lineno,
- "Commands were specified for \
- file `%s' at %s:%u,",
- oldname, oldfile->cmds->filename,
- oldfile->cmds->lineno);
- else
- makefile_error (file->cmds->filename, file->cmds->lineno,
- "Commands for file `%s' were found by \
- implicit rule search,",
- oldname);
- makefile_error (file->cmds->filename, file->cmds->lineno,
- "but `%s' is now considered the same file \
- as `%s'.",
- oldname, name);
- makefile_error (file->cmds->filename, file->cmds->lineno,
- "Commands for `%s' will be ignored \
- in favor of those for `%s'.",
- name, oldname);
- }
- }
-
- /* Merge the dependencies of the two files. */
-
- d = oldfile->deps;
- if (d == 0)
- oldfile->deps = file->deps;
- else
- {
- while (d->next != 0)
- d = d->next;
- d->next = file->deps;
- }
-
- merge_variable_set_lists (&oldfile->variables, file->variables);
-
- if (oldfile->double_colon && !file->double_colon)
- fatal ("can't rename single-colon `%s' to double-colon `%s'",
- oldname, name);
- if (!oldfile->double_colon && file->double_colon)
- fatal ("can't rename double-colon `%s' to single-colon `%s'",
- oldname, name);
-
- if (file->last_mtime > oldfile->last_mtime)
- /* %%% Kludge so -W wins on a file that gets vpathized. */
- oldfile->last_mtime = file->last_mtime;
-
- #define MERGE(field) oldfile->field |= file->field
- MERGE (precious);
- MERGE (tried_implicit);
- MERGE (updating);
- MERGE (updated);
- MERGE (is_target);
- MERGE (cmd_target);
- MERGE (phony);
- #undef MERGE
-
- file->renamed = oldfile;
- }
- }
-
- /* Remove all nonprecious intermediate files.
- If SIG is nonzero, this was caused by a fatal signal,
- meaning that a different message will be printed, and
- the message will go to stderr rather than stdout. */
-
- void
- remove_intermediates (sig)
- int sig;
- {
- register int i;
- register struct file *f;
- char doneany;
-
- if (!sig && just_print_flag)
- return;
-
- doneany = 0;
- for (i = 0; i < FILE_BUCKETS; ++i)
- for (f = files[i]; f != 0; f = f->next)
- if (f->intermediate && (f->dontcare || !f->precious))
- {
- int status;
- if (just_print_flag)
- status = 0;
- else
- {
- status = unlink (f->name);
- if (status < 0 && errno == ENOENT)
- continue;
- }
- if (!f->dontcare)
- {
- if (sig)
- error ("*** Deleting intermediate file `%s'", f->name);
- else if (!silent_flag)
- {
- if (! doneany)
- {
- fputs ("rm ", stdout);
- doneany = 1;
- }
- else
- putchar (' ');
- fputs (f->name, stdout);
- fflush (stdout);
- }
- if (status < 0)
- perror_with_name ("unlink: ", f->name);
- }
- }
-
- if (doneany && !sig)
- {
- putchar ('\n');
- fflush (stdout);
- }
- }
-
- /* 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 and .PHONY. */
-
- void
- snap_deps ()
- {
- register struct file *f, *f2;
- register struct dep *d;
- register int i;
-
- /* Enter each dependency name as a file. */
- for (i = 0; i < FILE_BUCKETS; ++i)
- for (f = files[i]; f != 0; f = f->next)
- for (f2 = f; f2 != 0; f2 = f2->prev)
- for (d = f2->deps; d != 0; d = d->next)
- if (d->name != 0)
- {
- d->file = lookup_file (d->name);
- if (d->file == 0)
- d->file = enter_file (d->name);
- else
- free (d->name);
- d->name = 0;
- }
-
- for (f = lookup_file (".PRECIOUS"); f != 0; f = f->prev)
- for (d = f->deps; d != 0; d = d->next)
- for (f2 = d->file; f2 != 0; f2 = f2->prev)
- f2->precious = 1;
-
- for (f = lookup_file (".PHONY"); f != 0; f = f->prev)
- for (d = f->deps; d != 0; d = d->next)
- for (f2 = d->file; f2 != 0; f2 = f2->prev)
- {
- /* Mark this file as phony and nonexistent. */
- f2->phony = 1;
- f2->last_mtime = (time_t) -1;
- }
-
- f = lookup_file (".EXPORT_ALL_VARIABLES");
- if (f != 0 && f->is_target)
- export_all_variables = 1;
- }
-
- /* Print the data base of files. */
-
- static void
- print_file (f)
- struct file *f;
- {
- register struct dep *d;
-
- putchar ('\n');
- if (!f->is_target)
- puts ("# Not a target:");
- printf ("%s:%s", f->name, f->double_colon ? ":" : "");
-
- for (d = f->deps; d != 0; d = d->next)
- printf (" %s", dep_name (d));
- putchar ('\n');
-
- if (f->precious)
- puts ("# Precious file (dependency of .PRECIOUS).");
- if (f->phony)
- puts ("# Phony target (dependency of .PHONY).");
- if (f->cmd_target)
- puts ("# Command-line target.");
- if (f->dontcare)
- puts ("# A default or MAKEFILES makefile.");
- printf ("# Implicit rule search has%s been done.\n",
- f->tried_implicit ? "" : " not");
- if (f->stem != 0)
- printf ("# Implicit/static pattern stem: `%s'\n", f->stem);
- if (f->intermediate)
- puts ("# File is an intermediate dependency.");
- if (f->also_make != 0)
- {
- fputs ("# Also makes:", stdout);
- for (d = f->also_make; d != 0; d = d->next)
- printf (" %s", dep_name (d));
- putchar ('\n');
- }
- if (f->last_mtime == (time_t) 0)
- puts ("# Modification time never checked.");
- else if (f->last_mtime == (time_t) -1)
- puts ("# File does not exist.");
- else
- printf ("# Last modified %.24s (%ld)\n",
- ctime (&f->last_mtime), (long int) f->last_mtime);
- printf ("# File has%s been updated.\n",
- f->updated ? "" : " not");
- switch (f->command_state)
- {
- case cs_running:
- puts ("# Commands currently running (THIS IS A BUG).");
- break;
- case cs_deps_running:
- puts ("# Dependencies commands running (THIS IS A BUG).");
- break;
- case cs_not_started:
- case cs_finished:
- switch (f->update_status)
- {
- case -1:
- break;
- case 0:
- puts ("# Successfully updated.");
- break;
- case 1:
- puts ("# Failed to be updated.");
- break;
- default:
- puts ("# Invalid value in `update_status' member!");
- fflush (stdout);
- fflush (stderr);
- abort ();
- }
- break;
- default:
- puts ("# Invalid value in `command_state' member!");
- fflush (stdout);
- fflush (stderr);
- abort ();
- }
-
- if (f->variables != 0)
- print_file_variables (f);
-
- if (f->cmds != 0)
- print_commands (f->cmds);
- }
-
- void
- print_file_data_base ()
- {
- register unsigned int i, nfiles, per_bucket;
- register struct file *file;
-
- puts ("\n# Files");
-
- per_bucket = nfiles = 0;
- for (i = 0; i < FILE_BUCKETS; ++i)
- {
- register unsigned int this_bucket = 0;
-
- for (file = files[i]; file != 0; file = file->next)
- {
- register struct file *f;
-
- ++this_bucket;
-
- for (f = file; f != 0; f = f->prev)
- print_file (f);
- }
-
- nfiles += this_bucket;
- if (this_bucket > per_bucket)
- per_bucket = this_bucket;
- }
-
- if (nfiles == 0)
- puts ("\n# No files.");
- else
- {
- printf ("\n# %u files in %u hash buckets.\n", nfiles, FILE_BUCKETS);
- #ifndef NO_FLOAT
- printf ("# average %.1f files per bucket, max %u files in one bucket.\n",
- ((double) nfiles) / ((double) FILE_BUCKETS) * 100.0, per_bucket);
- #endif
- }
- }
-