#include "make.h"
#include "filedef.h"
#include "job.h"
#include "commands.h"
#include "dep.h"
#include "variable.h"
#include "debug.h"
#include <assert.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#else
#include <sys/file.h>
#endif
#ifdef VMS
#include <starlet.h>
#endif
#ifdef WINDOWS32
#include <io.h>
#endif
extern int try_implicit_rule PARAMS ((struct file *file, unsigned int depth));
#define start_updating(_f) (((_f)->double_colon ? (_f)->double_colon : (_f))\
->updating = 1)
#define finish_updating(_f) (((_f)->double_colon ? (_f)->double_colon : (_f))\
->updating = 0)
#define is_updating(_f) (((_f)->double_colon ? (_f)->double_colon : (_f))\
->updating)
unsigned int commands_started = 0;
static int update_file PARAMS ((struct file *file, unsigned int depth));
static int update_file_1 PARAMS ((struct file *file, unsigned int depth));
static int check_dep PARAMS ((struct file *file, unsigned int depth, FILE_TIMESTAMP this_mtime, int *must_make_ptr));
static int touch_file PARAMS ((struct file *file));
static void remake_file PARAMS ((struct file *file));
static FILE_TIMESTAMP name_mtime PARAMS ((char *name));
static int library_search PARAMS ((char **lib, FILE_TIMESTAMP *mtime_ptr));
int
update_goal_chain (goals, makefiles)
register struct dep *goals;
int makefiles;
{
int t = touch_flag, q = question_flag, n = just_print_flag;
unsigned int j = job_slots;
int status = -1;
#define MTIME(file) (makefiles ? file_mtime_no_search (file) \
: file_mtime (file))
goals = copy_dep_chain (goals);
{
struct dep *g;
for (g = goals; g != 0; g = g->next)
g->changed = 0;
}
considered = 1;
while (goals != 0)
{
register struct dep *g, *lastgoal;
start_waiting_jobs ();
reap_children (1, 0);
lastgoal = 0;
g = goals;
while (g != 0)
{
struct file *file;
int stop = 0, any_not_updated = 0;
for (file = g->file->double_colon ? g->file->double_colon : g->file;
file != NULL;
file = file->prev)
{
unsigned int ocommands_started;
int x;
check_renamed (file);
if (makefiles)
{
if (file->cmd_target)
{
touch_flag = t;
question_flag = q;
just_print_flag = n;
}
else
touch_flag = question_flag = just_print_flag = 0;
}
ocommands_started = commands_started;
x = update_file (file, makefiles ? 1 : 0);
check_renamed (file);
g->changed += commands_started - ocommands_started;
stop = 0;
if ((x != 0 || file->updated) && status < 1)
{
if (file->update_status != 0)
{
status = file->update_status;
stop = (!keep_going_flag && !question_flag
&& !makefiles);
}
else
{
FILE_TIMESTAMP mtime = MTIME (file);
check_renamed (file);
if (file->updated && g->changed &&
mtime != file->mtime_before_update)
{
if (!makefiles
|| (!just_print_flag && !question_flag))
status = 0;
if (makefiles && file->dontcare)
stop = 1;
}
}
}
any_not_updated |= !file->updated;
if (stop)
break;
}
file = g->file;
if (stop || !any_not_updated)
{
if (!makefiles
&& file->update_status == 0 && !g->changed
#if defined(__APPLE__) || defined(NeXT) || defined(NeXT_PDO)
&& !(next_flag & NEXT_QUIET_FLAG)
#endif
&& !silent_flag && !question_flag)
message (1, ((file->phony || file->cmds == 0)
? _("Nothing to be done for `%s'.")
: _("`%s' is up to date.")),
file->name);
if (lastgoal == 0)
goals = g->next;
else
lastgoal->next = g->next;
free ((char *) g);
g = lastgoal == 0 ? goals : lastgoal->next;
if (stop)
break;
}
else
{
lastgoal = g;
g = g->next;
}
}
if (g == 0)
considered = !considered;
}
if (makefiles)
{
touch_flag = t;
question_flag = q;
just_print_flag = n;
job_slots = j;
}
return status;
}
static int
update_file (file, depth)
struct file *file;
unsigned int depth;
{
register int status = 0;
register struct file *f;
f = file->double_colon ? file->double_colon : file;
if (f->considered == considered)
{
DBF (DB_VERBOSE, _("Pruning file `%s'.\n"));
return f->command_state == cs_finished ? f->update_status : 0;
}
for (; f != 0; f = f->prev)
{
f->considered = considered;
status |= update_file_1 (f, depth);
check_renamed (f);
if (status != 0 && !keep_going_flag)
break;
if (f->command_state == cs_running
|| f->command_state == cs_deps_running)
{
status = 0;
break;
}
}
for (; f != 0 ; f = f->prev)
{
struct dep *d;
f->considered = considered;
for (d = f->deps; d != 0; d = d->next)
status |= update_file (d->file, depth + 1);
}
return status;
}
static int
update_file_1 (file, depth)
struct file *file;
unsigned int depth;
{
register FILE_TIMESTAMP this_mtime;
int noexist, must_make, deps_changed;
int dep_status = 0;
register struct dep *d, *lastd;
int running = 0;
DBF (DB_VERBOSE, _("Considering target file `%s'.\n"));
if (file->updated)
{
if (file->update_status > 0)
{
DBF (DB_VERBOSE,
_("Recently tried and failed to update file `%s'.\n"));
return file->update_status;
}
DBF (DB_VERBOSE, _("File `%s' was considered already.\n"));
return 0;
}
switch (file->command_state)
{
case cs_not_started:
case cs_deps_running:
break;
case cs_running:
DBF (DB_VERBOSE, _("Still updating file `%s'.\n"));
return 0;
case cs_finished:
DBF (DB_VERBOSE, _("Finished updating file `%s'.\n"));
return file->update_status;
default:
abort ();
}
++depth;
start_updating (file);
this_mtime = file_mtime (file);
check_renamed (file);
noexist = this_mtime == (FILE_TIMESTAMP) -1;
if (noexist)
DBF (DB_BASIC, _("File `%s' does not exist.\n"));
must_make = noexist;
if (!file->phony && file->cmds == 0 && !file->tried_implicit)
{
if (try_implicit_rule (file, depth))
DBF (DB_IMPLICIT, _("Found an implicit rule for `%s'.\n"));
else
DBF (DB_IMPLICIT, _("No implicit rule found for `%s'.\n"));
file->tried_implicit = 1;
}
if (file->cmds == 0 && !file->is_target
&& default_file != 0 && default_file->cmds != 0)
{
DBF (DB_IMPLICIT, _("Using default commands for `%s'.\n"));
file->cmds = default_file->cmds;
}
lastd = 0;
d = file->deps;
while (d != 0)
{
FILE_TIMESTAMP mtime;
check_renamed (d->file);
mtime = file_mtime (d->file);
check_renamed (d->file);
if (is_updating (d->file))
{
#if __APPLE__ || NeXT || NeXT_PDO
if (!(next_flag & NEXT_QUIET_FLAG))
#endif
error (NILF, _("Circular %s <- %s dependency dropped."),
file->name, d->file->name);
if (lastd == 0)
file->deps = d->next;
else
lastd->next = d->next;
d = d->next;
continue;
}
d->file->parent = file;
dep_status |= check_dep (d->file, depth, this_mtime, &must_make);
check_renamed (d->file);
{
register struct file *f = d->file;
if (f->double_colon)
f = f->double_colon;
do
{
running |= (f->command_state == cs_running
|| f->command_state == cs_deps_running);
f = f->prev;
}
while (f != 0);
}
if (dep_status != 0 && !keep_going_flag)
break;
if (!running)
d->changed = file_mtime (d->file) != mtime;
lastd = d;
d = d->next;
}
if (must_make)
{
for (d = file->deps; d != 0; d = d->next)
if (d->file->intermediate)
{
FILE_TIMESTAMP mtime = file_mtime (d->file);
check_renamed (d->file);
d->file->parent = file;
dep_status |= update_file (d->file, depth);
check_renamed (d->file);
{
register struct file *f = d->file;
if (f->double_colon)
f = f->double_colon;
do
{
running |= (f->command_state == cs_running
|| f->command_state == cs_deps_running);
f = f->prev;
}
while (f != 0);
}
if (dep_status != 0 && !keep_going_flag)
break;
if (!running)
d->changed = ((file->phony && file->cmds != 0)
|| file_mtime (d->file) != mtime);
}
}
finish_updating (file);
DBF (DB_VERBOSE, _("Finished prerequisites of target file `%s'.\n"));
if (running)
{
set_command_state (file, cs_deps_running);
--depth;
DBF (DB_VERBOSE, _("The prerequisites of `%s' are being made.\n"));
return 0;
}
if (dep_status != 0)
{
file->update_status = dep_status;
notice_finished_file (file);
depth--;
DBF (DB_VERBOSE, _("Giving up on target file `%s'.\n"));
if (depth == 0 && keep_going_flag
&& !just_print_flag && !question_flag)
error (NILF,
_("Target `%s' not remade because of errors."), file->name);
return dep_status;
}
if (file->command_state == cs_deps_running)
set_command_state (file, cs_not_started);
deps_changed = 0;
for (d = file->deps; d != 0; d = d->next)
{
FILE_TIMESTAMP d_mtime = file_mtime (d->file);
check_renamed (d->file);
#if 1
if (d_mtime == (FILE_TIMESTAMP) -1 && !d->file->intermediate)
must_make = 1;
#endif
deps_changed |= d->changed;
d->changed |= noexist || d_mtime > this_mtime;
if (!noexist && ISDB (DB_BASIC|DB_VERBOSE))
{
const char *fmt = 0;
if (d_mtime == (FILE_TIMESTAMP) -1)
{
if (ISDB (DB_BASIC))
fmt = _("Prerequisite `%s' of target `%s' does not exist.\n");
}
else if (d->changed)
{
if (ISDB (DB_BASIC))
fmt = _("Prerequisite `%s' is newer than target `%s'.\n");
}
else if (ISDB (DB_VERBOSE))
fmt = _("Prerequisite `%s' is older than target `%s'.\n");
if (fmt)
{
print_spaces (depth);
printf (fmt, dep_name (d), file->name);
fflush (stdout);
}
}
}
depth--;
if (file->double_colon && file->deps == 0)
{
must_make = 1;
DBF (DB_BASIC,
_("Target `%s' is double-colon and has no prerequisites.\n"));
}
else if (!noexist && file->is_target && !deps_changed && file->cmds == 0)
{
must_make = 0;
DBF (DB_VERBOSE,
_("No commands for `%s' and no prerequisites actually changed.\n"));
}
if (!must_make)
{
if (ISDB (DB_VERBOSE))
{
print_spaces (depth);
printf (_("No need to remake target `%s'"), file->name);
if (!streq (file->name, file->hname))
printf (_("; using VPATH name `%s'"), file->hname);
puts (".");
fflush (stdout);
}
notice_finished_file (file);
while (file)
{
#if __APPLE__ || NeXT || NeXT_PDO
file->old_name = file->name;
#endif
file->name = file->hname;
file = file->prev;
}
return 0;
}
DBF (DB_BASIC, _("Must remake target `%s'.\n"));
if (!streq(file->name, file->hname))
{
DB (DB_BASIC, (_(" Ignoring VPATH name `%s'.\n"), file->hname));
file->ignore_vpath = 1;
}
remake_file (file);
if (file->command_state != cs_finished)
{
DBF (DB_VERBOSE, _("Commands of `%s' are being run.\n"));
return 0;
}
switch (file->update_status)
{
case 2:
DBF (DB_BASIC, _("Failed to remake target file `%s'.\n"));
break;
case 0:
DBF (DB_BASIC, _("Successfully remade target file `%s'.\n"));
break;
case 1:
DBF (DB_BASIC, _("Target file `%s' needs remade under -q.\n"));
break;
default:
assert (file->update_status >= 0 && file->update_status <= 2);
break;
}
file->updated = 1;
return file->update_status;
}
void
notice_finished_file (file)
register struct file *file;
{
struct dep *d;
int ran = file->command_state == cs_running;
file->command_state = cs_finished;
file->updated = 1;
if (touch_flag
&& file->update_status == 0)
{
if (file->cmds != 0 && file->cmds->any_recurse)
{
unsigned int i;
for (i = 0; i < file->cmds->ncommand_lines; ++i)
if (!(file->cmds->lines_flags[i] & COMMANDS_RECURSE))
goto have_nonrecursing;
}
else
{
have_nonrecursing:
if (file->phony)
file->update_status = 0;
else
file->update_status = touch_file (file);
}
}
if (file->mtime_before_update == 0)
file->mtime_before_update = file->last_mtime;
if (ran && !file->phony)
{
struct file *f;
int i = 0;
if (question_flag || just_print_flag)
{
for (i = file->cmds->ncommand_lines; i > 0; --i)
if (! (file->cmds->lines_flags[i-1] & COMMANDS_RECURSE))
break;
}
else if (file->is_target && file->cmds == 0)
i = 1;
file->last_mtime = i == 0 ? 0 : NEW_MTIME;
for (f = file->double_colon; f != 0; f = f->next)
f->last_mtime = file->last_mtime;
}
if (ran && file->update_status != -1)
for (d = file->also_make; d != 0; d = d->next)
{
d->file->command_state = cs_finished;
d->file->updated = 1;
d->file->update_status = file->update_status;
if (ran && !d->file->phony)
(void) f_mtime (d->file, 0);
}
else if (file->update_status == -1)
file->update_status = 0;
}
static int
check_dep (file, depth, this_mtime, must_make_ptr)
struct file *file;
unsigned int depth;
FILE_TIMESTAMP this_mtime;
int *must_make_ptr;
{
register struct dep *d;
int dep_status = 0;
++depth;
start_updating (file);
if (!file->intermediate)
{
FILE_TIMESTAMP mtime;
dep_status = update_file (file, depth);
check_renamed (file);
mtime = file_mtime (file);
check_renamed (file);
if (mtime == (FILE_TIMESTAMP) -1 || mtime > this_mtime)
*must_make_ptr = 1;
}
else
{
FILE_TIMESTAMP mtime;
if (!file->phony && file->cmds == 0 && !file->tried_implicit)
{
if (try_implicit_rule (file, depth))
DBF (DB_IMPLICIT, _("Found an implicit rule for `%s'.\n"));
else
DBF (DB_IMPLICIT, _("No implicit rule found for `%s'.\n"));
file->tried_implicit = 1;
}
if (file->cmds == 0 && !file->is_target
&& default_file != 0 && default_file->cmds != 0)
{
DBF (DB_IMPLICIT, _("Using default commands for `%s'.\n"));
file->cmds = default_file->cmds;
}
check_renamed (file);
mtime = file_mtime (file);
check_renamed (file);
if (mtime != (FILE_TIMESTAMP) -1 && mtime > this_mtime)
*must_make_ptr = 1;
else
{
register struct dep *lastd;
lastd = 0;
d = file->deps;
while (d != 0)
{
if (is_updating (d->file))
{
#if __APPLE__ || NeXT || NeXT_PDO
if (!(next_flag & NEXT_QUIET_FLAG))
#endif
error (NILF, _("Circular %s <- %s dependency dropped."),
file->name, d->file->name);
if (lastd == 0)
{
file->deps = d->next;
free ((char *) d);
d = file->deps;
}
else
{
lastd->next = d->next;
free ((char *) d);
d = lastd->next;
}
continue;
}
d->file->parent = file;
dep_status |= check_dep (d->file, depth, this_mtime,
must_make_ptr);
check_renamed (d->file);
if (dep_status != 0 && !keep_going_flag)
break;
if (d->file->command_state == cs_running
|| d->file->command_state == cs_deps_running)
set_command_state (file, cs_deps_running);
lastd = d;
d = d->next;
}
}
}
finish_updating (file);
return dep_status;
}
#define TOUCH_ERROR(call) return (perror_with_name (call, file->name), 1)
static int
touch_file (file)
register struct file *file;
{
if (!silent_flag)
message (0, "touch %s", file->name);
#ifndef NO_ARCHIVES
if (ar_name (file->name))
return ar_touch (file->name);
else
#endif
{
int fd = open (file->name, O_RDWR | O_CREAT, 0666);
if (fd < 0)
TOUCH_ERROR ("touch: open: ");
else
{
struct stat statbuf;
char buf;
int status;
do
status = fstat (fd, &statbuf);
while (status < 0 && EINTR_SET);
if (status < 0)
TOUCH_ERROR ("touch: fstat: ");
if (read (fd, &buf, 1) < 0)
TOUCH_ERROR ("touch: read: ");
if (lseek (fd, 0L, 0) < 0L)
TOUCH_ERROR ("touch: lseek: ");
if (write (fd, &buf, 1) < 0)
TOUCH_ERROR ("touch: write: ");
if (statbuf.st_size == 0)
{
(void) close (fd);
fd = open (file->name, O_RDWR | O_TRUNC, 0666);
if (fd < 0)
TOUCH_ERROR ("touch: open: ");
}
(void) close (fd);
}
}
return 0;
}
static void
remake_file (file)
struct file *file;
{
if (file->cmds == 0)
{
if (file->phony)
file->update_status = 0;
else if (file->is_target)
file->update_status = 0;
else
#if defined(__APPLE__) || defined(NeXT) || defined(NeXT_PDO)
{
char *name = file->name;
if ((next_flag & NEXT_VPATH_FLAG) && general_vpath_search(&name)) {
free(name);
file->update_status = 0;
} else
#endif
{
const char *msg_noparent
= _("%sNo rule to make target `%s'%s");
const char *msg_parent
= _("%sNo rule to make target `%s', needed by `%s'%s");
if (!keep_going_flag && !file->dontcare)
{
if (file->parent == 0)
fatal (NILF, msg_noparent, "", file->name, "");
fatal (NILF, msg_parent, "", file->name, file->parent->name, "");
}
if (!file->dontcare)
{
if (file->parent == 0)
error (NILF, msg_noparent, "*** ", file->name, ".");
else
error (NILF, msg_parent, "*** ",
file->name, file->parent->name, ".");
}
file->update_status = 2;
}
#if defined(__APPLE__) || defined(NeXT) || defined(NeXT_PDO)
}
#endif
}
else
{
chop_commands (file->cmds);
if (!touch_flag || file->cmds->any_recurse)
{
execute_file_commands (file);
return;
}
file->update_status = 0;
}
notice_finished_file (file);
}
FILE_TIMESTAMP
f_mtime (file, search)
register struct file *file;
int search;
{
FILE_TIMESTAMP mtime;
#ifndef NO_ARCHIVES
if (ar_name (file->name))
{
char *arname, *memname;
struct file *arfile;
int arname_used = 0;
ar_parse_name (file->name, &arname, &memname);
arfile = lookup_file (arname);
if (arfile == 0)
{
arfile = enter_file (arname);
arname_used = 1;
}
mtime = f_mtime (arfile, search);
check_renamed (arfile);
if (search && strcmp (arfile->hname, arname))
{
char *name;
unsigned int arlen, memlen;
if (!arname_used)
{
free (arname);
arname_used = 1;
}
arname = arfile->hname;
arlen = strlen (arname);
memlen = strlen (memname);
name = (char *) xmalloc (arlen + 1 + memlen + 2);
bcopy (arname, name, arlen);
name[arlen] = '(';
bcopy (memname, name + arlen + 1, memlen);
name[arlen + 1 + memlen] = ')';
name[arlen + 1 + memlen + 1] = '\0';
if (arfile->name == arfile->hname)
rename_file (file, name);
else
rehash_file (file, name);
check_renamed (file);
}
if (!arname_used)
free (arname);
free (memname);
if (mtime == (FILE_TIMESTAMP) -1)
return (FILE_TIMESTAMP) -1;
mtime = FILE_TIMESTAMP_FROM_S_AND_NS (ar_member_date (file->hname), 0);
}
else
#endif
{
mtime = name_mtime (file->name);
if (mtime == (FILE_TIMESTAMP) -1 && search && !file->ignore_vpath)
{
char *name = file->name;
if (vpath_search (&name, &mtime)
|| (name[0] == '-' && name[1] == 'l'
&& library_search (&name, &mtime)))
{
if (mtime != 0)
file->last_mtime = mtime;
if (gpath_search (name, strlen(name) - strlen(file->name) - 1))
{
rename_file (file, name);
check_renamed (file);
return file_mtime (file);
}
rehash_file (file, name);
check_renamed (file);
mtime = name_mtime (name);
}
}
}
{
static FILE_TIMESTAMP now = 0;
if (!clock_skew_detected
&& mtime != (FILE_TIMESTAMP)-1 && mtime > now
&& !file->updated)
{
now = file_timestamp_now ();
#ifdef WINDOWS32
if (mtime > now && (((mtime % 2) == 0) && ((mtime-1) > now)))
#else
#ifdef __MSDOS__
if (mtime > now + 3)
#else
if (mtime > now)
#endif
#endif
{
char mtimebuf[FILE_TIMESTAMP_PRINT_LEN_BOUND + 1];
char nowbuf[FILE_TIMESTAMP_PRINT_LEN_BOUND + 1];
file_timestamp_sprintf (mtimebuf, mtime);
file_timestamp_sprintf (nowbuf, now);
error (NILF, _("*** Warning: File `%s' has modification time in the future (%s > %s)"),
file->name, mtimebuf, nowbuf);
clock_skew_detected = 1;
}
}
}
if (file->double_colon)
file = file->double_colon;
do
{
if (mtime != (FILE_TIMESTAMP)-1 && file->command_state == cs_not_started
&& !file->tried_implicit && file->intermediate)
file->intermediate = 0;
file->last_mtime = mtime;
file = file->prev;
}
while (file != 0);
return mtime;
}
static FILE_TIMESTAMP
name_mtime (name)
register char *name;
{
struct stat st;
if (stat (name, &st) < 0)
return (FILE_TIMESTAMP) -1;
return FILE_TIMESTAMP_STAT_MODTIME (st);
}
static int
library_search (lib, mtime_ptr)
char **lib;
FILE_TIMESTAMP *mtime_ptr;
{
static char *dirs[] =
{
#ifndef _AMIGA
"/lib",
"/usr/lib",
#endif
#if defined(WINDOWS32) && !defined(LIBDIR)
#define LIBDIR "."
#endif
LIBDIR,
0
};
static char *libpatterns = NULL;
char *libname = &(*lib)[2];
FILE_TIMESTAMP mtime;
char *p, *p2;
unsigned int len;
char *file, **dp;
if (!libpatterns)
{
int save = warn_undefined_variables_flag;
warn_undefined_variables_flag = 0;
libpatterns = xstrdup (variable_expand ("$(strip $(.LIBPATTERNS))"));
warn_undefined_variables_flag = save;
}
p2 = libpatterns;
while ((p = find_next_token (&p2, &len)) != 0)
{
static char *buf = NULL;
static int buflen = 0;
static int libdir_maxlen = -1;
char *libbuf = variable_expand ("");
{
char c = p[len];
char *p3, *p4;
p[len] = '\0';
p3 = find_percent (p);
if (!p3)
{
error (NILF, _(".LIBPATTERNS element `%s' is not a pattern"), p);
for (; len; --len, ++p)
*p = ' ';
*p = c;
continue;
}
p4 = variable_buffer_output (libbuf, p, p3-p);
p4 = variable_buffer_output (p4, libname, strlen (libname));
p4 = variable_buffer_output (p4, p3+1, len - (p3-p));
p[len] = c;
}
mtime = name_mtime (libbuf);
if (mtime != (FILE_TIMESTAMP) -1)
{
*lib = xstrdup (libbuf);
if (mtime_ptr != 0)
*mtime_ptr = mtime;
return 1;
}
file = libbuf;
if (vpath_search (&file, mtime_ptr))
{
*lib = file;
return 1;
}
if (!buflen)
{
for (dp = dirs; *dp != 0; ++dp)
{
int l = strlen (*dp);
if (l > libdir_maxlen)
libdir_maxlen = l;
}
buflen = strlen (libbuf);
buf = xmalloc(libdir_maxlen + buflen + 2);
}
else if (buflen < strlen (libbuf))
{
buflen = strlen (libbuf);
buf = xrealloc (buf, libdir_maxlen + buflen + 2);
}
for (dp = dirs; *dp != 0; ++dp)
{
sprintf (buf, "%s/%s", *dp, libbuf);
mtime = name_mtime (buf);
if (mtime != (FILE_TIMESTAMP) -1)
{
*lib = xstrdup (buf);
if (mtime_ptr != 0)
*mtime_ptr = mtime;
return 1;
}
}
}
return 0;
}