Category : Miscellaneous Language Source Code
Archive   : MAKE15.ZIP
Filename : MAKE.C

 
Output of file : MAKE.C contained in archive : MAKE15.ZIP
/*
* 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 greggy -f -S -q options, took some changes from v1.4
*
*/

#include
#include
#include
#include
#include
#include
#ifdef MSDOS
#include
#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 */

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;
{
REGISTER int i;
REGISTER targptr targp;
REGISTER 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;

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 */

return (mk & opts.query); /* 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;
REGISTER int no_k = 0; /* override the -k option */
char *tmf;
int addflag;

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 '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;

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;
{
REGISTER 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;
{
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);
#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 */
{
REGISTER targptr targp;
REGISTER fileptr *preqp;
REGISTER int mk;
fileptr filep;
long targtime;
long preqtime;

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;

/* 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;

/* display as necessary */
if (opts.depend > 1 ||
(opts.depend && (*preqp)->ftime > targtime))
{
display_prereq(targname, targtime, (*preqp)->fname,
(*preqp)->ftime);
}

++preqp;
}

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;
{
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, 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;
{
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);
}

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 filename] [-dDiknqrsSt] [target ...] [macro=value ...]");
exit(1);
}


  3 Responses to “Category : Miscellaneous Language Source Code
Archive   : MAKE15.ZIP
Filename : MAKE.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/