/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1984-2012 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* Glenn Fowler
* AT&T Research
*
* make state variable routines
* this file and the is*() macros specify the state variable name format
*
* (<var>) the state of <var>
* (<var>)<rule> the state of <var> qualified by <rule>
* ()<rule> the state of <rule>
*
* NOTE: update VERSION in compile.c if the format changes
*/
#include "make.h"
#include "options.h"
/*
* return a pointer to the rule <s1><s2><s3>
* force causes the rule to be created
* force<0 canon's name first
*/
{
Rule_t* r;
if (*s2)
{
if (force < 0)
{
return r;
}
}
return r;
}
/*
* return the state file name
* assumes the main makefile has already been read
*/
char*
statefile(void)
{
char* dir;
{
if (!state.writestate || streq(state.writestate, "-") || !stat(state.writestate, &st) && S_ISDIR(st.st_mode) && (dir = state.writestate))
else
sfstrclose(sp);
}
}
/*
* reconcile s with state view from r
*
* NOTE: requires state.maxview>0 && 0<=view<=state.maxview
*/
static Rule_t*
stateview(int op, char* name, register Rule_t* s, register Rule_t* r, int view, int accept, Rule_t** pv)
{
register Rule_t* v;
register List_t* p;
if (pv)
*pv = 0;
{
#if DEBUG
#endif
return 0;
}
{
{
char* file;
long n;
if (name && !s)
{
}
else
n = 0;
{
/*
* NOTE: this load should not be a problem for
* internal rule pointers since all
* non-state rules in state files
* are just references
*/
}
if (n)
}
}
if (name)
{
#if DEBUG
error(2, "STATEVIEW %s [%s] test %d [%s] -> %s [%s]", name, s ? timestr(s->time) : "no rule", view, timestr(r->time), v ? v->name : null, v ? timestr(v->time) : "no rule");
#endif
if (v)
{
if (pv)
*pv = v;
if (s && (op == RULE && (s->event >= v->event && s->event || !v->time && (v->property & P_force)) || op == PREREQS && s->time >= v->time))
return s;
if (v->time == r->time || accept || r->view == view || (r->property & (P_state|P_use|P_virtual)) || state.believe && view >= (state.believe - 1))
{
{
if (r->property & P_statevar)
{
if (r->statedata && (!v->statedata && *r->statedata || v->statedata && !streq(r->statedata, v->statedata) || r->time > v->time))
return 0;
s = r;
if (v->property & P_parameter)
s->property |= P_parameter;
}
}
else
{
{
return 0;
}
if (!s)
}
else
else
else
{
v = p->rule;
{
unviewname(v->name);
}
}
#if DEBUG
{
}
#endif
}
}
}
return s;
}
/*
* return a pointer to the state rule of var qualified by r
* force>0 causes the state rule to be created
* force<0 prevents a state bind
*/
{
register Rule_t* s;
register int i;
char* rul;
char* nam;
Rule_t* v;
int j;
int k;
int m;
Flags_t* b;
switch (op)
{
case PREREQS:
var = "+";
if (!r)
{
if (nobind)
{
}
return r;
}
break;
case RULE:
if (!r)
{
if (nobind)
{
}
nobind = 1;
}
break;
case VAR:
if (!r)
{
{
if (!force)
return 0;
}
}
if (nobind)
return r;
break;
#if DEBUG
default:
break;
#endif
}
if (r->property & P_statevar)
{
return 0;
s = r;
}
return 0;
else
{
rul++;
else
}
{
k = 0;
if (!tstbit(*b, i = (r->property & (P_statevar|P_use|P_virtual)) && state.targetview > 0 ? state.targetview : r->view) && (!s || !s->time || (r->property & P_statevar) || !(k = statetimeq(r, s))))
{
if (!(r->property & (P_statevar|P_use|P_virtual)) && !(r->dynamic & D_bound) && !(r->mark & M_bind) && (s && s->time || !s && state.compile >= COMPILED))
{
/*
* M_bind guards staterule() recursion
*/
s = r;
r = bind(r);
goto again;
}
{
{
setbit(*b, i);
}
}
else
{
{
m = 1;
j = 0;
}
else
{
m = 0;
j = k = state.targetview;
}
#if DEBUG
#endif
for (i = j; i <= k; i++)
{
setbit(*b, i);
if (i || !(r->property & P_statevar))
{
if (v)
{
if (m)
{
m = i;
while (++m <= k)
setbit(*b, m);
}
break;
}
}
if (!i)
{
}
}
}
}
else if (k)
setbit(*b, i);
}
if (!s && force > 0)
{
}
return s;
}
/*
* return a non-state rule pointer corresponding to the staterule r
* force causes the non-state rule to be created
*/
{
register char* s;
if (r->property & P_staterule)
{
s = r->name;
while (*s && *s++ != ')');
r = makerule(s);
}
return r;
}
/*
* return a variable pointer corresponding to the state variable r
* force causes the variable to be created
*/
{
register char* s;
register char* t;
register Var_t* v;
s = r->name;
{
if (r->property & P_statevar)
{
s++;
*(t = s + strlen(s) - 1) = 0;
}
else
return 0;
}
else
t = 0;
if (t)
*t = ')';
return v;
}
/*
* return the auxiliary variable pointer for s
* force causes the variable to be created
*/
{
Var_t* v;
return v;
}
/*
* force r->scan == (*(unsigned char*)h) or all files to be re-scanned
*/
int
forcescan(const char* s, char* v, void* h)
{
register int n = h ? *((unsigned char*)h) : r->scan;
NoP(s);
return 0;
}
/*
* report state file lock fatal error
*/
static void
{
long d;
/*
* probably a bad lock if too old
*/
if (d > 24 * 60 * 60)
else if (d > 0)
error(1, "another make has been running on %s in %s for the past %s", state.makefile, state.view[view].path, fmtelapsed(d, 1));
else
}
/*
* get|release exclusive (advisory) access to state file
*
* this is a general solution that should work on all systems
* the following problems need to be addressed
*
* o flock() or lockf() need to work for distributed
* as well as local file systems
*
* o the file system clock may be different than the
* local system clock
*
* o creating a specific lock file opens the door for
* lock files laying around after program and system
* crashes -- placing the pid of the locking process
* as file data may not work on distributed process
* systems
*/
void
{
register int fd;
register char* file;
Time_t t;
static char* lockfile;
static int lockmtime;
if (set)
{
return;
if (!state.ignorelock)
{
{
}
}
locktime = 0;
for (;;)
{
break;
lockfile = 0;
if (!state.ignorelock)
}
/*
* fstat() here would be best but some systems
* like cygwin which shall remain nameless
* return different time values after the close()
*/
}
else if (lockfile)
{
if (locktime)
{
if (stat(lockfile, &st) < 0 || (t = LOCKTIME(&st, lockmtime)) != locktime && t != tmxsns(tmxsec(locktime), 0))
{
if (state.writestate)
}
}
else
lockfile = 0;
}
}
/*
* read state from a previous make
*/
void
readstate(void)
{
char* file;
{
lockstate(1);
{
error(3, "use -%c%c to accept current state or -%c to remake", OPT(OPT_accept), OPT(OPT_readstate), OPT(OPT_readstate));
}
}
}
/*
* update the superimposed code
*/
static void
code(register const char* s)
{
if (*s)
{
if (*s)
{
if (*s)
{
if (*s)
{
if (*s)
{
if (*s)
{
}
}
}
}
}
}
}
/*
* bind() and scan() r->parameter file prerequisites
* this catches all implicit state variables before the
* parent files are scanned
*/
static int
checkparam(const char* s, char* v, void* h)
{
register List_t* p;
register char* t;
NoP(s);
NoP(h);
{
{
{
*t = 0;
*t = ')';
}
}
}
return 0;
}
/*
* check implicit state variable vars
*/
static int
checkvar1(register const char* s, char* u, void* h)
{
register Rule_t* r;
NoP(h);
{
if (!r->scan)
{
r->scan = SCAN_STATE;
}
code(s);
}
return 0;
}
/*
* check implicit state variable rules
*/
static int
checkvar2(const char* s, char* u, void* h)
{
Var_t* v;
NoP(s);
NoP(h);
if ((r->property & P_statevar) && r->scan && !r->view && (!(v = varstate(r, 0)) || !(v->property & V_scan)) && (!(r->property & P_parameter) || !(r->dynamic & D_scanned)))
{
r->scan = 0;
}
return 0;
}
/*
* freeze the parameter files and candidate state variables
*/
void
candidates(void)
{
int view;
{
{
}
}
}
/*
* save state for the next make
*/
void
savestate(void)
{
char* file;
{
if (state.writestate)
{
{
}
}
lockstate(0);
}
}
/*
* bind statevar r to a variable
*/
{
Rule_t* s;
Time_t t;
#if DEBUG
#endif
r = s;
return r;
if (r->property & P_statevar)
{
register Var_t* v;
char* e;
/*
* determine the current state variable value
*/
if (val)
else if (v = varstate(r, 0))
{
}
{
*e = ')';
}
else
{
/*
* see if some other view has an initial value
*/
}
/*
* check if the state variable value changed
* the previous value, if defined, has already
* been placed in r->statedata by readstate()
*/
message((-2, "checking state variable %s value `%s'", r->name, r->statedata ? r->statedata : null));
{
/*
* state variable changed
*/
{
if (r->time)
else
}
t++;
r->time = t;
}
if (tmp)
}
bindattribute(r);
return r;
}
/*
* check and stat built target r
* otherwise check for motion from . to dir of r
*
* NOTE: this needs clarification
*/
static int
{
register int n;
register char* s;
long pos;
oldname(r);
{
rebind(r, -1);
}
if (!(r->dynamic & D_triggered))
return n;
return n;
pathcanon(s, 0, 0);
{
if (!r->uname)
}
#if DEBUG
error(2, "statetime(%s): dir=%s n=%d time=[%s]", r->name, s, n, timestr(n ? NOTIME : tmxgetmtime(st)));
#endif
return n;
}
/*
* update internal time of r after its action has completed
* sync>0 syncs the state rule prereqs and action
* sync<0 resolves r but does not update state
*/
{
register Rule_t* s;
int a;
int n;
int skip = 0;
int zerostate = 0;
Time_t t;
Time_t q;
Rule_t* x;
{
return r->time;
}
s = 0;
zerostate = 1;
{
r->time = 0;
{
skip = 1;
}
}
{
}
else if (checkcurrent(r, &st))
{
if (r->property & P_dontcare)
t = 0;
else
{
t = CURTIME;
zerostate = 1;
}
tmxsetmtime(&st, t);
}
else if (sync < 0)
return r->time;
{
{
/*
* this alternate event time prevents the action from
* triggering next time if nothing else changes
*/
if (r->dynamic & D_triggered)
}
}
{
static int localsync;
static int localtest;
/*
* r is built since its time changed after its action triggered
*/
/*
* check for file system and local system time consistency
* directories, archives and multi hard link files not sync'd
*/
{
#if DEBUG
error(2, "%s: r[%s] s[%s] f[%s]", r->name, timestr(r->time), timestr(s->time), timestr(tmxgetmtime(&st)));
#endif
if (!localsync && !state.override && r->time && r->time != OLDTIME && !(r->property & P_force) && tmxgetmtime(&st) == tmxgetctime(&st))
{
if (((n = (tmxsec(r->time) - (unsigned long)st.st_mtime - 1)) >= 0 || (n = (CURSECS - (unsigned long)st.st_mtime + 2)) <= 0) && (lstat(r->name, &ln) || !S_ISLNK(ln.st_mode)))
{
/*
* warn if difference not tolerable
*/
a = (n > 0) ? n : -n;
if (a > 1)
error(state.regress ? -1 : 1, "%s file system time %s local time by at least %s", r->name, n > 0 ? "lags" : "leads", fmtelapsed(a, 1));
}
}
if (localsync > 0)
{
/*
* NOTE: time stamp syncs work on the assumption that
* all source files have an mtime that is older
* than CURTIME -- this isn't too bad since
* only built files are sync'd; also note that
* nsec is set to 0 to avoid resolution mismatches
*/
for (;;)
{
{
break;
}
if (localtest)
{
tmxsetmtime(&st, t);
break;
}
/*
* some systems try to fix up the local
* remote skew in the utime() call
* >> this never works <<
* members of the club include
* darwin.ppc
* netbsd.i386
*/
{
break;
}
localtest = 1;
q = tmxgetmtime(&st);
break;
error(state.regress ? -1 : 1, "the utime(2) or utimes(2) system call is botched for the filesystem containing %s (the current time is adjusted by %lu seconds) -- the state may be out of sync", r->name, tmxsec(localskew));
/*
* the botch may only be for times near "now"
* localskew=1s handles this
*/
{
if (!tmxtouch(r->name, TMX_NOTIME, t, TMX_NOTIME, 0) && !stat(r->name, &st) && tmxgetmtime(&st) == t)
}
}
}
}
}
if (!s)
if (sync)
{
{
}
}
if (!skip && (s->time != ((r->property & P_virtual) ? r->time : tmxgetmtime(&st)) || zerostate && s->time))
{
}
return s->time;
}
/*
* return 1 if rule r time matches state s time modulo
* tolerance and low resolution time state
*/
int
{
long d;
static int warned;
return 1;
{
{
return 1;
}
return 1;
}
else if (r->time < s->time && !(r->property & P_state) && tmxsec(r->time) == tmxsec(s->time) && !tmxnsec(r->time))
{
if (!warned)
{
warned = 1;
}
return 1;
}
return 0;
}