home *** CD-ROM | disk | FTP | other *** search
- #include <stdio.h>
- #include <ctype.h>
- #include "make.h"
-
- /*
- * MAKE - Maintain separate source files
- *
- * SYNOPSIS
- * MK [-f file] [-a] [-n] [-d] [-i] [-k] [name] ...
- * f: use 'file' instead of default makefile
- * a: assume all modules are obsolete (recompile everything)
- * n: don't recompile, just list steps to recompile
- * d: debugging (print tree, file info)
- * i: ignore return statuses from execution
- * k: if errors occur, propagate error status up tree; continue.
- * name: module name to recompile
- *
- * AUTHOR
- * Landon M. Dyer, Atari Inc.
- *
- * INCREDIBLY HACKED OVER BY
- * Eric C. Brown University of Utah.
- * Fred Fish, UniSoft Systems Inc (for Commodore AMIGA)
- *
- * HACKS
- * Added object library support (dummy names that inherit dates)
- * Added source library support (real parents)
- * Added direct execution capability.
- * Removed script file.
- * Added support for my macro based debugging package (fnf)
- * Ran through "indent" to change formatting (fnf)
- */
-
- #define INIT "~INIT" /* initialization macro */
- #define DEINIT "~DEINIT" /* de-init macro */
- #define BEFORE "~BEFORE" /* the per-root 'startup' method */
- #define AFTER "~AFTER" /* the per-root 'wrapup' method */
-
- char *mfiles[] = { /* default makefiles */
- "makefile",
- "Makefile",
-
- #ifdef VAXVMS
- "[-]makefile",
- "sys$login:makefile",
- #endif
-
- #ifdef MSDOS
- "..\makefile",
- #endif
- ""
- };
-
- MACRO *mroot = (MACRO *) NULL; /* root of macro-list */
- FILENODE *froot = (FILENODE *) NULL; /* root of filenode-list */
- FILENODE *firstf = (FILENODE *) NULL; /* the very first filenode */
-
- char *modnames[MAXMODS]; /* module-names mentioned in commandline */
- int execstat = 0; /* nonzero if started executing */
- int modcount = 0; /* #of module-names */
- int debug = 0; /* nonzero: turn on debugging */
- int obsolete = 0; /* nonzero: every file should be recompiled */
- int noscript = 0; /* nonzero: print methods on stdout */
- int ignore_errors = 0; /* nonzero: ignore error return codes */
- int prop_errors = 0; /* nonzero: propagate error status up tree */
- DATE bigbang; /* a date, the very earliest possible */
- DATE endoftime; /* a date, the very last possible */
-
- /*
- * The following are used to save macro definitions given on the
- * command line. In the unix make, it is very common to override
- * a definition in the makefile via a definition on the command line.
- * However, this make expands macros in each line as they are read,
- * which conflicts with the need to wait until the entire file is read
- * before adding command line macros to the table (to override those
- * from the file). Thus the code needs to be modified to delay macro
- * expansions until the last minute, before shipping the line off to
- * be executed. The alternative is to implement a macro locking or
- * precedence scheme. Either solution requires more work than I can
- * do at the moment. Fred Fish 28-Nov-85
- */
- char *cmdmacros[MAXCMDMACS]; /* Macro defs given on command line */
- int cmdmcount = 0; /* Number of macro defs on command line */
-
- static void fparse ();
- static void yankdependents ();
- static void addmanydepend ();
- static void determ ();
- static void recomp ();
-
- main (argc, argv)
- int argc;
- char *argv[];
- {
- register int arg;
- register int i;
- register char *mfile = NULL;
- extern DATE adate ();
- extern void initrootdates ();
- extern void prtree ();
-
- DBUG_ENTER ("main");
- ENABLE_ABORT;
- initrootdates ();
- for (arg = 1; arg < argc; ++arg) {
- if (*argv[arg] == '-') {
- switch (tolower (argv[arg][1])) {
- case '#':
- DBUG_PUSH (&argv[arg][2]);
- break;
- case 'f':
- if (++arg >= argc) {
- fputs ("-f needs filename argument.\n", stderr);
- DBUG_RETURN (1);
- }
- mfile = argv[arg];
- break;
- case 'a':
- obsolete = 1;
- break;
- case 'n':
- noscript = 1;
- break;
- case 'd':
- debug = 1;
- break;
- case 'i':
- ignore_errors = 1;
- break;
- case 'k':
- prop_errors = 1;
- break;
- default:
- fputs ("Unknown switch: ", stderr);
- fputc (argv[arg][1], stderr);
- fputc ('\n', stderr);
- break;
- }
- } else {
- if (strchr (argv[arg], '=') != NULL) {
- if (cmdmcount < MAXCMDMACS) {
- cmdmacros[cmdmcount++] = argv[arg];
- } else {
- fputs ("Too many command line macros.\n", stderr);
- DBUG_RETURN (1);
- }
- } else {
- if (modcount < MAXMODS) {
- modnames[modcount++] = argv[arg];
- } else {
- fputs ("Too many module names.\n", stderr);
- DBUG_RETURN (1);
- }
- }
- }
- }
- if (mfile != NULL) {
- if (fmake (mfile) == -1) {
- fputs ("Cannot open makefile '", stderr);
- fputs (mfile, stderr);
- fputs ("'.\n", stderr);
- }
- } else {
- for (i = 0; *mfiles[i]; ++i) {
- if (fmake (mfiles[i]) != -1) {
- break;
- }
- }
- if (!*mfiles[i]) {
- fputs ("Cannot open makefile.\n", stderr);
- }
- }
- if (debug) {
- prtree ();
- }
- DBUG_RETURN (0);
- }
-
-
- /*
- * Construct dependency tree from the makefile 'fn'.
- * Figure out what has to be recompiled, and write a script file to do that.
- */
-
- fmake (fn)
- char *fn;
- {
- FILE * fp;
-
- DBUG_ENTER ("fmake");
- if ((fp = fopen (fn, "r")) == (FILE *) NULL) {
- DBUG_RETURN (-1);
- }
- fparse (fp);
- determ ();
- fclose (fp);
- DBUG_RETURN (0);
- }
-
-
- /*
- * Parse the input file, defining macros and building the dependency tree.
- */
-
- static void fparse (fp)
- FILE *fp;
- {
- auto char ibuf[STRSIZ];
- auto char ebuf[STRSIZ];
- auto char *strp;
- register char *tok1;
- register char *tok2;
- register char *s;
- extern char *fgets ();
- register FILENODE *lastf = (FILENODE *)NULL;
- extern FILENODE *addfile ();
- extern void defmac ();
- extern void AddToLibrary ();
- extern void escape ();
- extern void addmeth ();
-
- DBUG_ENTER ("fparse");
- for (;;) {
- if (fgets (ibuf, STRSIZ, fp) == NULL) {
- break;
- }
- DBUG_3 ("inline", "got line '%s'", ibuf);
- mexpand (ibuf, ebuf, STRSIZ, MACCHAR);
- escape (ebuf, COMCHAR);
- s = ebuf + strlen (ebuf) - 1; /* clobber last newline in string */
- if (s >= ebuf && *s == '\n') {
- *s = '\0';
- }
- DBUG_3 ("inline2", "after macro and excape processing is '%s'", ebuf);
- if (ebuf[0] == '\t' || ebuf[0] == ' ') {
- DBUG_2 ("meth", "looks like a method line to me");
- addmeth (lastf, ebuf);
- continue;
- }
- strp = ebuf;
- if ((tok1 = token (&strp)) == NULL) {
- continue;
- }
- if ((tok2 = token (&strp)) != NULL) {
- if (STRSAME (tok2, DEFMAC)) {
- DBUG_2 ("mac", "looks like a macro definition to me");
- if (*strp) {
- defmac (tok1, strp);
- }
- #if VAXVMS || MSDOS
- /* Preserve old behavior. Unix and amiga behavior is */
- /* to never undefine any macros, in the sense used here. */
- /* This also allows macros with null expansions, which */
- /* are very useful. Fred Fish */
- if (!*strp) {
- if (undefmac (tok1) < 0) {
- fputs ("Can't undefine macro '", stderr);
- fputs (tok1, stderr);
- fputs ("'.\n", stderr);
- }
- }
- #endif
- continue;
- } else if (STRSAME (tok2, DEPEND)) {
- DBUG_2 ("mac", "looks like a dependency line to me");
- addmeth (lastf, gmacro (AFTER)); /* terminate last method */
- lastf = filenode (tok1); /* init lastf */
- if (firstf == (FILENODE *) NULL) {
- firstf = lastf;
- }
- lastf -> fmake = NULL;
- addmeth (lastf, gmacro (BEFORE));
- lastf -> fflag |= ROOTP;
- addmanydepend (strp, lastf);
- continue;
- #ifndef FUNNYLIBS
- } else if (STRSAME (tok2, ISLIB)) {
- addmeth (lastf, gmacro (AFTER));
- lastf = filenode (tok1);
- if (firstf == (FILENODE *) NULL) {
- firstf = lastf;
- }
- lastf -> fmake = NULL;
- addmeth (lastf, gmacro (BEFORE));
- lastf -> fflag |= LIBRARY;
- lastf -> fflag |= ROOTP;
-
- AddToLibrary (lastf);
- /* no archives here */
- /* archives and libraries are mutually exclusive */
- while ((tok1 = token (&strp)) != NULL) {
- (void) addfile (lastf, tok1);
- }
- continue;
- #endif
- } else {
- DBUG_2 ("uh", "what kinda line is this?");
- addmanydepend (strp, lastf);
- }
- }
- }
- addmeth (lastf, gmacro (AFTER));
- DBUG_VOID_RETURN;
- }
-
- /*
- * scan tokens from strbuf and search for libraries and archives.
- * libraries look like foo [ bar baz mumble ]
- * archives look like foo ( bar baz mumble )
- * in either case, bar, baz, and mumble have parents of foo.
- * foo is added to the parentlist, if not already on the list.
- * bar, baz, and mumble are added to the dependency list of depend.
- * the command *cannot* be split across newlines without causing errors.
- * if you don't like that, well, life's a bitch and then you die.
- */
-
- static void addmanydepend (strbuf, depend)
- char *strbuf;
- FILENODE *depend;
- {
- register char *tok1;
- register char *tok2;
- register FILENODE *parent;
- register FILENODE *child;
- extern FILENODE *addfile ();
- extern FILENODE *addparent ();
- extern void exit ();
-
- DBUG_ENTER ("addmanydepend");
- DBUG_4 ("dep", "add dependencies '%s' to '%s'", strbuf, depend -> fname);
- tok1 = token (&strbuf);
- if (tok1 == NULL) {
- DBUG_VOID_RETURN;
- }
- tok2 = token (&strbuf);
- while (tok2 != NULL) {
- #ifdef FUNNYLIBS
- if (STRSAME (tok2, BGNLIB)) {
- parent = addparent (tok1); /* add tok1 to parent list */
- for (tok1 = token (&strbuf); /* skip over token in tok2 */
- tok1 != NULL && strcmp (tok1, ENDLIB); /* go eol or end */
- tok1 = token (&strbuf)) { /* get next token */
- if (tok1 == NULL) {
- fputs ("MAKE: Error in library defn.\n", stderr);
- exit (2);
- }
- child = addfile (depend, tok1);
- child -> fflag = LIBRARY;
- child -> parent = parent;
- } /* for */
- tok1 = token (&strbuf);
- tok2 = token (&strbuf);
- continue; /* the while */
- } /* if islib */
- #endif
- if (STRSAME (tok2, BGNARC)) {
- parent = addparent (tok1); /* add tok1 to parent list */
- for (tok1 = token (&strbuf); /* skip over token in tok2 */
- tok1 != NULL && strcmp (tok1, ENDARC); /* go eol or end */
- tok1 = token (&strbuf)) { /* get next token */
- if (tok1 == NULL) {
- fputs ("MAKE: Error in archive defn.\n", stderr);
- exit (2);
- }
- child = addfile (depend, tok1);
- child -> fflag = ARCHIVE;
- child -> parent = parent;
- } /* for */
- tok1 = token (&strbuf);/* get current token */
- tok2 = token (&strbuf);/* get lookahead token */
- continue; /* the while */
- } /* if isarc */
- else { /* nothing special -- */
- (void) addfile (depend, tok1);/* add dependency */
- tok1 = tok2; /* shift token */
- tok2 = token (&strbuf);
- }
- } /* while */
- if (tok2 == NULL && tok1 != NULL) { /* last token = not special */
- (void) addfile (depend, tok1);
- }
- DBUG_VOID_RETURN;
- }
-
- /*
- * Determine sequence of recompiles from the creation dates.
- * If have anything to recompile, then create a script file full of commands.
- */
-
- static void determ ()
- {
- register FILENODE *f;
- register int i;
- register char *m;
- extern void cleanuparchives ();
-
- DBUG_ENTER ("determ");
- if (firstf == (FILENODE *) NULL) { /* empty tree */
- puts ("No changes.");
- DBUG_VOID_RETURN;
- }
- if (modcount == 0) {
- examine (firstf, endoftime);
- } else {
- for (i = 0; i < modcount; ++i) {
- if ((f = gfile (modnames[i])) == (FILENODE *) NULL) {
- fputs ("Don't know how to make ", stderr);
- fputs (modnames[i], stderr);
- fputs (".\n", stderr);
- continue;
- }
- if ((f -> fflag & ROOTP) == 0) {
- fputc ('\'', stderr);
- fputs (f -> fname, stderr);
- fputs ("' is not a root!\n", stderr);
- continue;
- }
- examine (f, endoftime);
- }
- }
- if (execstat) {
- if ((m = gmacro (DEINIT)) != NULL) {
- execute (m, noscript);
- }
- cleanuparchives ();
- } else {
- puts ("No changes.");
- }
- DBUG_VOID_RETURN;
- }
-
-
- /*
- * Examine filenode 'fnd' and see if it has to be recompiled.
- * 'date' is the last-touched date of the node's father
- * (or 'endoftime' if its a root file.)
- * Root files with NO dependencies are assumed not to be up to date.
- */
-
- examine (fnd, date)
- FILENODE *fnd;
- DATE date;
- {
- register int rebuildp = 0;
- register int rval;
- register int errcode = 0;
- register NODE *n;
- extern void getdate ();
- extern char *printdate ();
-
- DBUG_ENTER ("examine");
- DBUG_3 ("ex", "parent node date '%s'", printdate (date));
- DBUG_3 ("ex", "examine node '%s'", fnd -> fname);
- getdate (fnd);
- DBUG_3 ("ex", "modification date '%s'", printdate (fnd -> fdate));
- DBUG_3 ("ex", "parent node date '%s'", printdate (date));
- if (fnd -> fnode == (NODE *) NULL && fnd -> fflag & ROOTP) {
- DBUG_2 ("root", "node, is rootnode with no dependents, rebuild");
- rebuildp = 1;
- } else { /* see if dependents need to be recompiled */
- for (n = fnd -> fnode; n != (NODE *) NULL; n = n -> nnext) {
- if ((rval = examine (n -> nfile, fnd -> fdate)) != 0) {
- if (rval == ERROR) {
- errcode = ERROR;/* if error occurred, propagate up */
- fnd -> fflag |= ERROR;
- fputs ("Couldn't remake ", stderr);
- fputs (fnd -> fname, stderr);
- fputs (" because of errors.\n", stderr);
- }
- rebuildp = 1;
- }
- }
- }
- DBUG_3 ("ex", "parent node date '%s'", printdate (date));
- DBUG_3 ("rebuildp", "rebuild flag is %d", rebuildp);
- /* if ancestor recompiled or root, recompile, */
- /* but not if error in ancestor */
- if (rebuildp && (fnd -> fflag & ERROR) == 0) {
- DBUG_3 ("rebuild", "'%s' needs remaking", fnd -> fname);
- recomp (fnd);
- if (fnd -> fflag & ERROR) {
- DBUG_3 ("err", "got an error remaking %s", fnd -> fname);
- DBUG_RETURN (ERROR);
- }
- }
- DBUG_3 ("ex", "current node date now '%s'", printdate (fnd -> fdate));
- DBUG_3 ("ex", "parent node date '%s'", printdate (date));
- if (obsolete || laterdt (fnd -> fdate, date) >= 0) {
- DBUG_2 ("date", "looks like parent needs remaking now");
- rebuildp = 1;
- }
- if (errcode) {
- DBUG_RETURN (errcode);
- } else {
- DBUG_RETURN (rebuildp);
- }
- }
-
- /*
- * Make sure a filenode gets recompiled.
- */
-
- static void recomp (f)
- FILENODE *f;
- {
- register char *m;
-
- DBUG_ENTER ("recomp");
- if (!execstat) {
- execstat = 1;
- if ((m = gmacro (INIT)) != NULL) {
- execute (m, noscript);
- }
- }
- if (f -> fflag & REBUILT) {
- DBUG_VOID_RETURN;
- }
- if (!noscript) { /* don't extract if printing steps */
- yankdependents (f);
- }
- if (f -> fmake != NULL) {
- if (execute (f -> fmake, noscript) != 0) {
- if (!ignore_errors && !prop_errors) {
- exit (2);
- } else if (prop_errors) {
- f -> fflag |= ERROR;
- }
- }
- }
- f -> fflag |= REBUILT;
- DBUG_VOID_RETURN;
- }
-
- static void yankdependents (fnd)
- FILENODE *fnd;
- {
- register NODE *n;
- extern int extract ();
-
- DBUG_ENTER ("yankdependents");
- for (n = fnd -> fnode; n != (NODE *) NULL; n = n -> nnext) {
- #ifdef YANKDESCENDANTS
- yankdependents (n -> nfile);
- #endif
- DBUG_3 ("dep", "yanking %s", n -> nfile -> fname);
- DBUG_3 ("dep", "flags %d", n -> nfile -> fflag);
- if ((n -> nfile -> fflag & ARCHIVE) && ((n -> nfile -> fflag & EXTRACT) == 0)) {
- /* if archived and not extracted */
- fputs ("Extracting ", stdout);
- puts (n -> nfile -> fname);
- if (!noscript) {
- #ifdef LAR
- if (extract (n -> nfile) == FAILURE) {
- fputs ("Extract failed -- I think I'll die now.\n", stderr);
- exit (1);
- }
- #else
- fputs ("No support for archives, bye!\n", stderr);
- exit (1);
- #endif
- }
- n -> nfile -> fflag |= EXTRACT;
- }
- }
- DBUG_VOID_RETURN;
- }
-
- /*
- * Complain about being out of memory, and then die.
- */
-
- allerr ()
- {
- fputs ("Can't alloc -- no space left (I give up!)\n", stderr);
- exit (1);
- }
-