home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programming
/
powerprogramming1994.iso
/
progtool
/
c
/
gymake12.arc
/
MAKE.C
< prev
next >
Wrap
C/C++ Source or Header
|
1988-11-18
|
19KB
|
861 lines
/*
* make.c An imitation of the Unix MAKE facility
*
* 88-10-01 v1.0 created by greg yachuk, placed in the public domain
* 88-10-06 v1.1 changed prerequisite list handling
* 88-11-11 v1.2 fixed some bugs and added environment variables
*
*/
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <memory.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#ifdef MSDOS
#include <stdlib.h>
#include <process.h>
#include <dos.h>
#else
#include <sys/errno.h>
#include <sys/wait.h>
#endif
#include "make.h"
#include "tstring.h"
#include "decl.h"
#ifdef MSDOS
#define PATH_SEPARATOR ";"
#define FILE_SEPARATOR "/\\"
#else
#define PATH_SEPARATOR ":"
#define FILE_SEPARATOR "/"
#endif
#define MAKEINI "default.mk"
char *shell_cmds[] = {
#ifdef MSDOS
"dir", "type", "rem", "pause", "date", "time", "ren", "rename",
"ver", "vol", "break", "verify", "mkdir", "md", "exit", "ctty",
"echo", "if", "cls", "chdir", "cd", "rmdir", "rd", "copy", "del",
"erase",
#endif
NULL,
};
targptr target_list = NULL; /* list of target nodes */
fileptr file_list = NULL; /* list of file nodes */
symptr symbol_list = NULL; /* list of symbol nodes */
shellptr shell_list = NULL; /* list of shell nodes */
int make_level = 0; /* for counting new_make()'s */
targptr first_targ = NULL; /* first target, in case nothing explicit */
targptr suffix_targ = NULL; /* .SUFFIXES target pointer */
char **tlist = NULL; /* command line targets */
char **flist = NULL; /* command line make files */
char **mlist = NULL; /* command line macros */
int tmax = 0; /* max size of tlist */
int fmax = 0; /* max size of flist */
int mmax = 0; /* max size of mlist */
int depend = 0; /* -d option */
int display = 0; /* -D option */
int envirn = 0; /* -e option */
int ignore = 0; /* -i option */
int noexec = 0; /* -n option */
int readdef = 1; /* -r option */
int silent = 0; /* -s option */
int touch = 0; /* -t option */
int dispcount = 0; /* used for -D option */
long now; /* time at startup */
char *makeflags; /* value to update the MAKEFLAGS macro with */
main(argc, argv)
int argc;
char **argv;
{
REGISTER int i;
REGISTER targptr targp;
symptr symp;
char *envp;
char **envv;
/* initialize the various global lists */
depend = 0;
dispcount = 0;
target_list = NULL;
file_list = NULL;
shell_list = NULL;
/* allocate space for command line targets, files and macros */
tlist = grow_list(NULL, &tmax);
flist = grow_list(NULL, &fmax);
mlist = grow_list(NULL, &mmax);
/* process MAKEFLAGS environment variable, first */
symp = get_symbol("MAKEFLAGS", 0);
if (symp->svalue != NULL)
{
/* chop up the MAKEFLAGS and feed them to to make_args() */
envp = tstrcpy(symp->svalue);
envv = tokenize(envp);
for (i=0; envv[i] != NULL; i++);
make_args(i, envv);
/* free the vector of pointers, and the string itself, */
/* since you cannot have macros, targets or makefiles */
/* in the MAKEFLAGS macro. */
tfree(envv);
tfree(envp);
tfree(makeflags); /* ignore this, since we just read it */
}
make_args(--argc, ++argv); /* process command line options */
add_macro(makeflags, 0); /* update the MAKEFLAGS macro */
tfree(makeflags);
/* add command line macros, so they DON'T get overridden */
for(i = 0; mlist[i] != NULL; i++)
add_macro(mlist[i], 1);
tfree(mlist); /* all done with macros */
if (noexec)
silent = touch = 0; /* -n always displays; never touches */
if (dispcount > 1)
display = 1; /* display `default.mk' on -DD */
first_targ = NULL; /* used in parse() */
if (readdef)
parse(fopenp(MAKEINI, "r")); /* read in `default.mk' */
if (dispcount > 0)
display = 1; /* display makefile's on -D */
first_targ = NULL; /* get first target in `makefile' */
/* parse the makefiles given on command line */
for(i = 0; flist[i] != NULL; i++)
{
parse(equal(flist[i],"-") ? fdopen(dup(fileno(stdin)), "r")
: fopen(flist[i], "r"));
}
/* no makefiles specified, so use "makefile" */
if (i == 0)
parse(fopen("makefile", "r"));
tfree(flist); /* all done with makefile's */
if ((targp = get_target(".INIT")) != NULL)
build(targp->tshell); /* process the .INIT rule */
for (i = 0; tlist[i] != NULL; i++)
make(tlist[i], 1); /* process command line targets */
tfree(tlist); /* all done with targets */
/* if no targets specified, make the first one */
if (i == 0 && first_targ)
make(first_targ->tfile->fname, 1);
if ((targp = get_target(".DONE")) != NULL)
build(targp->tshell); /* process the .DONE rule */
return (0); /* not exit(); see new_make() */
}
/*
* make_args - process the command line arguments
*/
make_args(argc, argv)
int argc;
char **argv;
{
REGISTER int tlen;
REGISTER int flen;
REGISTER int mlen;
char *tmf;
now = time(NULL); /* get current date & time */
makeflags = tstrcpy("MAKEFLAGS+=");
tlen = flen = mlen = 0;
for (;argc != 0; ++argv, --argc)
{
if (**argv != '-')
{
/* doesn't start with '-'; must be macro or target */
if (strchr(*argv, '='))
{ /* store as a macro */
if (mlen == mmax)
mlist = grow_list(mlist, &mmax);
mlist[mlen++] = *argv;
}
else
{ /* store as a target */
if (tlen == tmax)
tlist = grow_list(tlist, &tmax);
tlist[tlen++] = *argv;
}
continue;
}
/* must be an option */
tmf = tstrcat(makeflags, *argv);
tfree(makeflags);
makeflags = tstrcat(tmf, " ");
tfree(tmf);
while (*argv && *++*argv)
{
switch (**argv)
{
case 'd': /* show dependencies */
depend++;
break;
case 'D': /* display makefiles */
dispcount++;
break;
case 'e': /* don't override environment */
envirn = 1;
break;
case 'f': /* new makefile name */
if (argc < 2)
usage();
if (flen == fmax)
flist = grow_list(flist, &fmax);
++argv, --argc;
flist[flen++] = *argv;
/* save, but ignore, the makefile */
tmf = tstrcat(makeflags, *argv);
tfree(makeflags);
makeflags = tstrcat(tmf, " ");
tfree(tmf);
*argv = NULL;
break;
case 'i': /* ignore errors */
ignore = 1;
break;
case 'n': /* don't execute commands */
noexec = 1;
break;
case 'r': /* don't read default.mk */
readdef = 0;
break;
case 's': /* don't echo commands */
silent = 1;
break;
case 't': /* touch files, don't build */
touch = 1;
break;
default:
usage(); /* never returns */
}
}
}
/* terminate all lists with a NULL pointer */
tlist[tlen] = NULL;
flist[flen] = NULL;
mlist[mlen] = NULL;
/* let the caller update the makeflags macro */
}
/*
* grow_list - expand the list of pointers by a factor of two
*/
char **
grow_list(list, len)
char **list;
int *len;
{
REGISTER int l;
l = *len; /* get current length */
/* if list is NULL, start off with a default list */
if (list == NULL)
list = (char **) talloc(((l=1)+1) * sizeof(char *));
else
list = (char **) trealloc((char *)list,
((l <<=1)+1)*sizeof(char *));
if (list == NULL)
terror(1, "too many options");
/* if we are initially allocating it, set first pointer to NULL */
if (l == 1)
*list = NULL;
*len = l; /* update current length */
return (list);
}
/*
* fopenp - open file in current directory or along PATH
*/
FILE *
fopenp(fname, type)
char *fname;
char *type;
{
REGISTER int len;
REGISTER char *fpath;
FILE *fd;
char *path;
char *tp;
/* try to open file relative to current directory */
if ((fd = fopen(fname, type)) != NULL)
return (fd);
/* didn't work, search along path */
if ((path = getenv("PATH")) == NULL)
return (NULL);
path = tstrcpy(path); /* allocate string and copy */
fpath = talloc(strlen(path) + strlen(fname) + 2);
/* look for tokens separated by semi-colons (;) or colons (:) */
tp = token(path, PATH_SEPARATOR);
while (tp != NULL)
{
strcpy(fpath, tp);
len = strlen(fpath) - 1;
/* make sure there is a separator between path and filename */
if (!strchr(FILE_SEPARATOR, fpath[len]))
fpath[++len] = '/';
strcpy(&fpath[len+1], fname); /* attach the filename */
if ((fd = fopen(fpath, type)) != NULL)
break;
tp = token(NULL, PATH_SEPARATOR);
}
tfree(path);
tfree(fpath);
return (fd);
}
/*
* make - guts of the make command
* - make all pre-requisites, and if necessary, build target
*
* returns -1 target was already up to date w.r.t. pre-requisites
* 0 target has not been built
* 1 target is now built (and up to date)
*/
make(targname, worry)
char *targname;
int worry; /* if set, it is an error to NOT build this */
{
REGISTER targptr targp;
REGISTER fileptr *preqp;
fileptr filep;
long targtime;
long preqtime;
/* if recorded time of file is not default, we've already built it */
filep = get_file(targname);
if (filep && filep->ftime != MAXNEGTIME)
return (-1);
targp = get_target(targname); /* find the target node */
if (targp == NULL)
return (default_rule(targname, worry, 0));
targtime = file_time(targname); /* keep actual time of current target */
/* must build non-existant files, even with no pre-requisites */
preqtime = MAXNEGTIME + 1;
/* make all pre-requisites */
preqp = targp->tpreq;
while (preqp && *preqp)
{
make((*preqp)->fname, worry);
/* keep track of newest pre-requisite */
if (preqtime < (*preqp)->ftime)
preqtime = (*preqp)->ftime;
/* display as necessary */
if (depend > 1 || (depend && (*preqp)->ftime > targtime))
{
display_prereq(targname, targtime, (*preqp)->fname,
(*preqp)->ftime);
}
++preqp;
}
if (targp->tshell == NULL) /* try default rules anyway */
return (default_rule(targname, 0, preqtime > targtime));
else if (preqtime > targtime)
{
if (touch) /* won't be set when `noexec' */
touch_file(targname);
else
{
add_metas("", "", targname);
build(targp->tshell);
}
targp->tfile->ftime = (noexec) ? now : file_time(targname);
return (1);
}
targp->tfile->ftime = targtime;
return (-1);
}
/*
* default_rule - try the .SUFFIXES when we don't have an explicit target
* - if `worry' is set, it is an ERROR to NOT build this target
* - `mustbuild' is set if make() has out-of-date prereq's
* but no explicit shell rules
*/
default_rule(targname, worry, mustbuild)
char *targname;
int worry;
int mustbuild;
{
REGISTER targptr targp;
REGISTER fileptr *preqp;
fileptr filep;
char *ext;
char *basename;
char *preqname;
long targtime;
long preqtime;
int built;
char suffrule[80];
ext = strrchr(targname, '.'); /* find the extension */
if (ext == NULL)
ext = targname + strlen(targname);
basename = tstrncpy(targname, ext - targname); /* find the base name */
targtime = file_time(targname);
/* suffix_targ is used to (slightly) speed up this function */
preqp = suffix_targ ? suffix_targ->tpreq : NULL;
built = 0;
while (preqp && *preqp && !built)
{
/* look for a default rule from SUFFIX to `ext' */
strcat(strcpy(suffrule, (*preqp)->fname), ext);
targp = get_target(suffrule); /* e.g. `.c.o' */
if (targp != NULL)
{
/* found a rule; see if file exists */
preqname = tstrcat(basename, (*preqp)->fname);
preqtime = file_time(preqname);
/*
* don't bother recursive makes unless necessary
* e.g. we have .c.o and .l.c, but also .l.o!
* we want to use .l.o if a .c file does not exist
*/
if (preqtime != MAXNEGTIME || mustbuild)
built = make(preqname, 0);
/* check if pre-req file exists and is newer */
preqtime = file_time(preqname);
if (preqtime > targtime || (mustbuild && built))
{
if (depend)
{
display_prereq(targname, targtime,
preqname, preqtime);
}
if (touch) /* won't be set when `noexec' */
touch_file(targname);
else
{
add_metas(basename, preqname, targname);
build(targp->tshell);
}
built = 1;
}
else if (depend > 1 && preqtime != MAXNEGTIME)
{
display_prereq(targname, targtime,
preqname, preqtime);
}
tfree(preqname);
}
++preqp; /* try next .SUFFIXES rule */
}
if (!built)
{
/* didn't find anything; try the default rule */
targp = get_target(".DEFAULT");
if (targp != NULL)
{
add_metas(basename, "", targname);
build(targp->tshell);
built = 1;
}
else if (targtime == MAXNEGTIME && worry)
terror(1, tstrcat("Don't know how to make ", targname));
}
tfree(basename);
/* record the current file time */
if ((filep = get_file(targname)) != NULL)
{
filep->ftime = (built == 1 && noexec) ? now
: file_time(targname);
}
return (built ? built : ((targtime == MAXNEGTIME) ? 0 : -1));
}
/*
* add_metas - add symbols for $*, $< and $@
*/
add_metas(basename, preqname, targname)
char *basename;
char *preqname;
char *targname;
{
add_symbol("*", basename, 0);
add_symbol("<", preqname, 0);
add_symbol("@", targname, 0);
}
/*
* touch_file - set the MODIFICATION time of the file to NOW
*/
touch_file(targname)
char *targname;
{
REGISTER int handle;
#ifndef MSDOS
time_t timep[2];
time(&timep[0]);
timep[1] = timep[0];
handle = utime(targname, timep);
#else
handle = utime(targname, NULL);
#endif
fputs("touch ", stdout);
puts(targname);
if (handle == 0)
return;
/* create the file, if it did not exist */
if (errno == ENOENT)
{
handle = open(targname, O_CREAT | O_TRUNC, S_IWRITE);
if (handle != -1)
{
close(handle);
return;
}
}
perror("touch");
exit (1);
}
/*
* build - process the shell commands
*/
build(shellp)
REGISTER shellptr *shellp;
{
int runst; /* exec return status */
char *scmd; /* command with symbols broken out */
char *tcmd; /* copy of scmd for `tokenize' */
REGISTER char **argv; /* argified version of scmd */
char **shcp; /* pointer to shell command list */
if (shellp == NULL)
return;
for (;*shellp != NULL;
tfree(scmd), tfree(tcmd), tfree(argv), ++shellp)
{
/* breakout runtime symbols (e.g. $* $@ $<) */
scmd = breakout((*shellp)->scmd);
if (!(*shellp)->s_silent && !silent)
puts(scmd);
/* make a copy because tokenize litters '\0's */
tcmd = tstrcpy(scmd);
argv = tokenize(tcmd);
if (equal(argv[0], "make"))
{
/* call ourselves recursively */
new_make(argv);
continue;
}
if (noexec)
continue;
/* any indirection MUST be handled by the shell */
if (!(*shellp)->s_shell && strpbrk(scmd, "<|>"))
(*shellp)->s_shell = 1;
#ifdef MSDOS
/* likewise, check for COMMAND.COM builtin commands */
shcp = shell_cmds;
for (; !(*shellp)->s_shell && *shcp; ++shcp)
{
if (equal(*shcp, argv[0]))
{
(*shellp)->s_shell = 1;
break;
}
}
#endif
/* run without COMMAND.COM if possible, 'cause it uses RAM */
if ((*shellp)->s_shell)
runst = system(scmd);
else
runst = spawnvp(P_WAIT, argv[0], argv);
if (runst == 0)
continue;
/* uh-oh, an error */
if (runst == -1)
perror("make");
#ifdef MSDOS
terror(0, tstrcat("\007*** Error code ",itoa(runst, scmd, 10)));
#else
sprintf(scmd, "\007*** Error code %d\n", runst);
terror(0, scmd);
#endif
if (!ignore && !(*shellp)->s_ignore)
exit(1);
}
}
/*
* new_make - save current environment
* - call make() recursively (actually main())
* - clean up new environment
* - restore environment
*/
new_make(argv)
char **argv;
{
targptr thead, tnext, tsuffix;
fileptr fhead, fnext;
symptr shead, snext;
shellptr shhead, shnext;
char **ttlist;
long tnow;
int tdepend;
int i;
/* save all the globals */
tsuffix = suffix_targ;
thead = target_list;
fhead = file_list;
shead = symbol_list;
shhead = shell_list;
ttlist = tlist;
tnow = now;
tdepend = depend;
/* count the arguments */
for (i = 0; argv[i]; ++i);
/* call ourselves recursively; this inherits flags */
++make_level;
main(i, argv);
--make_level;
/* we're back, so gotta clean up and dispose of a few things */
while (target_list)
{
tnext = target_list->tnext;
if (target_list->tpreq)
tfree(target_list->tpreq);
if (target_list->tshell)
tfree(target_list->tshell);
tfree(target_list);
target_list = tnext;
}
while (file_list)
{
fnext = file_list->fnext;
tfree(file_list->fname);
tfree(file_list);
file_list = fnext;
}
/* don't drop all symbols, just the new ones */
while (symbol_list != shead)
{
snext = symbol_list->snext;
tfree(symbol_list->sname);
tfree(symbol_list->svalue);
tfree(symbol_list);
symbol_list = snext;
}
while (shell_list)
{
shnext = shell_list->slink;
tfree(shell_list->scmd);
tfree(shell_list);
shell_list = shnext;
}
/* restore our original globals */
suffix_targ = tsuffix;
target_list = thead;
file_list = fhead;
symbol_list = shead;
shell_list = shhead;
tlist = ttlist;
now = tnow;
depend = tdepend;
}
usage()
{
puts("make [-f filename] [-dDinrst] [target ...] [macro = value ...]");
exit(1);
}
display_prereq(targname, targtime, preqname, preqtime)
char *targname;
long targtime;
char *preqname;
long preqtime;
{
#ifdef MSDOS
char chtime[10];
fputs(targname, stdout);
fputs(" (", stdout);
fputs(ltoa(targtime, chtime, 16), stdout);
fputs((targtime<=preqtime) ? ") older than ":") newer than ", stdout);
fputs(preqname, stdout);
fputs(" (", stdout);
fputs(ltoa(preqtime, chtime, 16), stdout);
puts(")");
#else
printf("%s (%08lx) %s than %s (%08lx)\n",
targname, targtime,
(targtime < preqtime) ? "older" : "newer",
preqname, preqtime);
#endif
}
long
file_time(fname)
char *fname;
{
struct stat sbuf;
if (stat(fname, &sbuf) != 0)
return (MAXNEGTIME);
return (sbuf.st_mtime);
}
#ifndef MSDOS
int
spawnvp(mode, path, args)
int mode;
char *path;
char **args;
{
int pid = 0;
union wait waitword;
if (mode != P_OVERLAY)
pid = fork();
if (pid == 0)
execvp(path, args);
wait(&waitword);
return (waitword.w_retcode);
}
#endif