home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD2.mdf
/
c
/
tools
/
make
/
pdmake
/
make.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-07-06
|
19KB
|
864 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
* 89-07-12 v1.3 stop appending shell commands, and flush output
* 89-08-01 v1.4 AB lots of new options and code
* 89-10-30 v1.5 -f -S -q options, took some changes from v1.4
* 90-04-18 v1.6 -b -- -W options, emulate <<, non-BSD cleanup
*/
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#ifdef MSDOS
#include <stdlib.h>
#endif
#include "make.h"
#include "tstring.h"
#include "decl.h"
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 */
char **shell_cmds = NULL; /* commands which force a SHELL */
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 */
optnode opts; /* all the options */
int readdef = 1; /* -r 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;
{
int i;
targptr targp;
int mk;
symptr symp;
char *envp;
char **envv;
/* initialize the various global lists */
opts.depend = 0;
dispcount = 0;
target_list = NULL;
file_list = NULL;
shell_list = NULL;
/* don't set symbol_list to NULL, or recursive makes won't work */
/* 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 (opts.query) /* -q never executes anything */
opts.noexec = 1;
if (opts.noexec)
opts.touch = 0; /* -n never touches */
if (dispcount > 1) /* display `default.mk' on -DD */
opts.display = 1;
first_targ = NULL; /* used in parse() */
if (readdef) /* read in `default.mk' */
parse(fopenp(MAKEINI, "r"));
if (dispcount > 0) /* display makefile's on -D */
opts.display = 1;
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" or "Makefile" */
if (i == 0)
{
if (parse(fopen("makefile", "r")) == 0)
{
#ifndef MSDOS
parse(fopen("Makefile", "r"));
#endif
}
}
tfree(flist); /* all done with makefile's */
/* find the current value of the $(MAKE) macro */
symp = get_symbol("MAKE", 0);
opts.make = (symp->svalue == NULL) ? "make" : symp->svalue;
/* get list of commands which will force usage of SHELL */
symp = get_symbol("SHELLCMD", 0);
shell_cmds = (symp->svalue) ? tokenize(tstrcpy(symp->svalue)) : NULL;
if ((targp = get_target(".INIT")) != NULL)
build(targp->tshell); /* process the .INIT rule */
mk = 0;
for (i = 0; tlist[i] != NULL; i++)
{
/* process command line arguments */
mk |= (make(tlist[i], 1) > 0) ? 1 : 0;
}
tfree(tlist); /* all done with targets */
/* if no targets specified, make the first one */
if (i == 0 && first_targ)
mk |= (make(first_targ->tfile->fname, 1) > 0) ? 1 : 0;
if ((targp = get_target(".DONE")) != NULL)
build(targp->tshell); /* process the .DONE rule */
/* all done with the shell commands, so clean up */
if (shell_cmds)
{
if (*shell_cmds)
tfree(*shell_cmds);
tfree(shell_cmds);
}
return (mk & opts.query); /* not exit(); see new_make() */
}
/*
* make_args - process the command line arguments
*/
make_args(argc, argv)
int argc;
char **argv;
{
int tlen;
int flen;
int mlen;
int no_k = 0; /* override the -k option */
char *tmf;
int addflag;
fileptr fp;
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);
while (*argv && *++*argv)
{
addflag = 1; /* add to MAKEFLAGS */
switch (**argv)
{
case 'b': /* backwards compatibility */
case '-': /* SCO Xenix compatibility */
addflag = 0; /* don't add to MAKEFLAGS */
break;
case 'd': /* show dependencies */
addflag = 0; /* don't add to MAKEFLAGS */
opts.depend++;
break;
case 'D': /* display makefiles */
dispcount++;
break;
case 'e': /* don't override environment */
opts.envirn = 1;
break;
case 'f': /* new makefile name */
addflag = 0; /* don't add to MAKEFLAGS */
if (argc < 2)
usage();
if (flen == fmax)
flist = grow_list(flist, &fmax);
++argv, --argc;
flist[flen++] = *argv;
*argv = NULL;
break;
case 'i': /* ignore errors */
opts.ignore = 1;
break;
case 'k': /* give up on current target on error */
opts.keepon = 1;
break;
case 'n': /* don't execute commands */
opts.noexec = 1;
break;
case 'q': /* question mode */
opts.query = 1;
break;
case 'r': /* don't read default.mk */
readdef = 0;
break;
case 's': /* don't echo commands */
opts.silent = 1;
break;
case 'S': /* Undo -k option */
no_k = 1;
break;
case 't': /* touch files, don't build */
opts.touch = 1;
break;
case 'W': /* What-if file is touched? */
if (argc < 2)
usage();
++argv, --argc;
fp = add_file(*argv);
fp->ftime = now;
*argv = NULL;
break;
default:
usage(); /* never returns */
}
}
if (addflag)
{
tfree(makeflags);
makeflags = tstrcat(tmf, " ");
}
tfree(tmf);
}
/* terminate all lists with a NULL pointer */
tlist[tlen] = NULL;
flist[flen] = NULL;
mlist[mlen] = NULL;
/* check for -S over-riding -k option */
if (no_k)
opts.keepon = 0;
/* 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;
{
int l;
/* if list is NULL, start off with a default list */
if (list == NULL)
list = (char **) talloc(((l = 1) + 1) * sizeof(char *));
else
{
l = *len; /* get current length */
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;
{
int len;
char *fpath;
FILE *fd;
char *path;
char *tp;
/* try to open file relative to current directory */
if ((fd = fopen(fname, type)) != NULL)
return (fd);
#ifndef MSDOS
/* didn't work, try home directory */
if ((path = getenv("HOME")) != NULL)
{
fpath = talloc(strlen(path) + strlen(fname) + 2);
strcpy(fpath, path);
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 */
fd = fopen(fpath, type);
tfree(fpath);
if (fd != NULL)
return (fd);
}
#endif
/* 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, NULL);
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, NULL);
}
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 */
{
targptr targp;
fileptr *preqp;
int mk;
fileptr filep;
long targtime;
long preqtime;
char *dol_quest;
char *dq;
mk = 0;
/* 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, NULL, worry, 0));
/* keep actual time of current target */
targtime = file_time(targname, 0);
/* must build non-existant files, even with no pre-requisites */
preqtime = MAXNEGTIME + 1;
dol_quest = tstrcpy("");
/* make all pre-requisites */
preqp = targp->tpreq;
while (preqp && *preqp)
{
mk |= make((*preqp)->fname, worry);
/* keep track of newest pre-requisite */
if (preqtime < (*preqp)->ftime)
preqtime = (*preqp)->ftime;
if (targtime < (*preqp)->ftime)
{
dq = tstrcat(dol_quest, (*preqp)->fname);
tfree(dol_quest);
dol_quest = tstrcat(dq, " ");
tfree(dq);
}
/* display as necessary */
if (opts.depend > 1 ||
(opts.depend && (*preqp)->ftime > targtime))
{
display_prereq(targname, targtime, (*preqp)->fname,
(*preqp)->ftime);
}
++preqp;
}
add_symbol("?", dol_quest, 0);
tfree(dol_quest);
if (targp->tshell == NULL) /* try default rules anyway */
{
if (default_rule(targname, targp, 0, preqtime > targtime))
return (1);
return (mk);
}
else
if (preqtime > targtime)
{
if (opts.touch) /* won't be set when `noexec' */
touch_file(targname);
else
{
add_metas("", "", targname);
if (build(targp->tshell))
return (0);
}
targp->tfile->ftime = (opts.noexec) ? now
: file_time(targname, 1);
return (1);
}
targp->tfile->ftime = targtime;
return (mk);
}
/*
* 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, targetp, worry, mustbuild)
char *targname;
targptr targetp;
int worry;
int mustbuild;
{
targptr targp;
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, 0);
/* 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 = get_preqname(targetp, (*preqp)->fname,
basename);
preqtime = file_time(preqname, 0);
/*
* 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, 0);
if (preqtime > targtime || (mustbuild && built))
{
if (opts.depend)
{
display_prereq(targname, targtime,
preqname, preqtime);
}
if (opts.touch) /* won't be set when `noexec' */
touch_file(targname);
else
{
add_metas(basename, preqname, targname);
if (build(targp->tshell))
return (0);
}
built = 1;
}
else
if (opts.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);
if (build(targp->tshell))
return (0);
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 && opts.noexec) ? now
: file_time(targname, 1);
}
return (built ? built : ((targtime == MAXNEGTIME) ? 0 : 1));
}
/*
* get_preqname - find prerequisite name from target and prerequisite suffix
*/
char *get_preqname(targp, suffix, basename)
targptr targp;
char *suffix;
char *basename;
{
fileptr *preqp;
char *preqf;
char *basef;
int i;
if (targp != NULL)
{
/* strip the directory name from the basename */
basef = tsplit(basename, FILE_SEPARATOR, NULL);
/* look through prerequisite list for file with right name */
for (preqp = targp->tpreq; preqp && *preqp; ++preqp)
{
/* split the pre-requisite into dir and filenames */
preqf = tsplit((*preqp)->fname, FILE_SEPARATOR, NULL);
/* see if the filename part matches the target */
for (i = 0; preqf[i] != '\0'; i++)
{
if (preqf[i] != basef[i])
break;
}
/* if we differed only on the suffix, we're okay */
if (strcmp(preqf + i, suffix) == 0)
return (tstrcpy((*preqp)->fname));
}
#ifdef ALL_PREQS
/* didn't find a matching basename + suffix in the preq-list. */
/* look through prerequisite list for file with right suffix. */
for (preqp = targp->tpreq; preqp && *preqp; ++preqp)
{
preqf = strrchr((*preqp)->fname, '.');
if (preqf == NULL)
continue;
/* take the first file which has right suffix */
if (strcmp(suffix, preqf) == 0)
return (tstrcpy((*preqp)->fname));
}
#endif /* ALL_PREQS */
}
/* didn't find one, so try forming one using basename + suffix */
return (tstrcat(basename, suffix));
}
/*
* add_metas - add symbols for $*, $< and $@
*/
add_metas(basename, preqname, targname)
char *basename;
char *preqname;
char *targname;
{
/* $* is the basename */
add_symbol("*", basename, 0);
split_meta("*", basename);
add_symbol("<", preqname, 0);
split_meta("<", preqname);
add_symbol("@", targname, 0);
split_meta("@", targname);
}
/*
* split_meta - split a metasymbol into Directory and File parts
*/
split_meta(sym, name)
char *sym;
char *name;
{
char *dname;
char *dsym;
char *fsym;
/* construct the macro names (e.g. $(*D), $(@F)) */
dsym = tstrcat(sym, "D");
fsym = tstrcat(sym, "F");
add_symbol(fsym, tsplit(name, FILE_SEPARATOR, &dname), 0);
if (dname == NULL)
add_symbol(dsym, ".", 0);
else
{
add_symbol(dsym, dname, 0);
tfree(dname);
}
tfree(dsym);
tfree(fsym);
}
/*
* touch_file - set the MODIFICATION time of the file to NOW
*/
touch_file(targname)
char *targname;
{
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);
}
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, built)
char *fname;
int built;
{
struct stat sbuf;
/*
* if the file is supposedly built, but still does not exists, just
* fake it by returning the current time.
*/
if (stat(fname, &sbuf) != 0)
return (built ? now : MAXNEGTIME);
return (sbuf.st_mtime);
}
usage()
{
puts("make [-f file] [-dDiknqrsStWb-] [target ...] [macro=value ...]");
exit(1);
}