home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_100 / 186_01 / make11.c < prev    next >
Text File  |  1985-08-21  |  12KB  |  418 lines

  1. /* MAKE: CP/M version of the UNIX utility.  Compiles and links files
  2.  * as necessary, based on dependancy information in a "makefile".
  3.  * For usage, see MAKE.DOC.
  4.  *
  5.  * This code is an adaptation and expansion of the program "mk" given
  6.  * by Allen Holub in Dr. Dobb's Journal, August 1985 (#106).  
  7.  *
  8.  * To compile:
  9.  *      CC MAKE -E3000
  10.  *      CC MAKEIO -E3000
  11.  *      L2 MAKE MAKEIO
  12.  *
  13.  * If DEBUG is #defined in MAKE.H, then the file MDEBUG.C must be
  14.  * compiled and linked in.
  15.  *
  16.  * CREDITS:
  17.  * 
  18.  * -- dependancies(), tree(), find(), and gmem() functions by 
  19.  *    Allen Holub (DDJ #106).
  20.  * -- main(), make(), and makenode() functions based on code by
  21.  *    Allen Holub (DDJ #106), with significant revisions by
  22.  *    James Pritchett.
  23.  * -- init() and macro() functions by James Pritchett.
  24.  * -- All code by Allen Holub adapted for BDS C (where necessary)
  25.  *    by James Pritchett.
  26.  *
  27.  * Version 1.0 -- 10/28/85
  28.  * Version 1.1 -- 12/06/85
  29.  */
  30.  
  31. #include <bdscio.h>
  32. #include "make.h"
  33.  
  34. #define VERSION "1.1"
  35. #define DATE    "12/06/85"
  36.  
  37. main(argc,argv)
  38. int     argc;
  39. char    **argv;
  40. {
  41.     init(argc,argv);    /* messages, variable initializers, command parsing */
  42.  
  43.     if (fopen(filename,iobuff) == ERROR)    /* Open up the makefile */
  44.         serr("Can't open %s\n",filename);
  45.     if (!dependancies())                    /* Build the object tree */
  46.         err("File empty!\n",0);
  47.     else {
  48.         if (!qopt) {
  49.             printf("Making target object -> %s\n",first);
  50.             printf("Commands needed:\n");
  51.         }
  52.         make(first);                        /* Evaluate all objects */
  53.         if (stackp && !nopt && !topt) {     /* Batch the commands */
  54.             if (!qopt)
  55.                 prompt();
  56.             batch();
  57.         }
  58.         else if (!stackp)
  59.             printf("\tNothing needs making\n");
  60.     }
  61. }
  62.  
  63. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  64.  
  65. void init(argc,argv)    /* Opening message, help message, variable
  66.                          * initialization, and command line parsing.
  67.                          */
  68. int     argc;
  69. char    **argv;
  70. {
  71.     char c;
  72.     int i;
  73.  
  74.     printf("\nMAKE  Version %s   James Pritchett, %s\n\n",VERSION,DATE);
  75.  
  76. /* Answer a cry for help */
  77.  
  78.     if (*argv[1] == '?') {
  79.         printf("Usage: MAKE [target] [-options]\n");
  80.         printf("Makes target or first in file ");
  81.         printf("(if target not specified)\n\n");
  82.         printf("\tOptions:\n");
  83.         printf("\t\t-f filename: Use filename as makefile\n");
  84.         printf("\t\t-a:          Make all targets\n");
  85.         printf("\t\t-n:          Make no targets\n");
  86.         printf("\t\t-q:          Supress all messages\n");
  87.         printf("\t\t-r:          Do not set file attributes\n");
  88.         printf("\t\t-t:          Set file attributes, but do not make\n");
  89.         exit();
  90.     }
  91.  
  92. /* Global variable initialization */
  93.  
  94.     root = NULL;    /* These should be zeroed anyway, */
  95.     first = NULL;   /*    but better safe than sorry  */
  96.     inputline = 0;
  97.     stackp = NULL;
  98.     aopt = nopt = qopt = ropt = topt = FALSE;
  99.     filename = "MAKEFILE";      /* Default filename for makefile */
  100.     defdsk = 0;
  101.     defuser = UNKNOWN;
  102.  
  103. /* Parse the command line, beginning with argv[1] */
  104.  
  105.     i = 1;
  106.  
  107. /* If the first arg is not an option, it must be the target */
  108.  
  109.     if ((--argc) && (*argv[1] != '-')) {
  110.         first = argv[1];
  111.         argc--;
  112.         i++;
  113.     }
  114.  
  115. /* Parse the options */
  116.  
  117.     while (argc--) {
  118.         if (*argv[i] != '-')    /* Ignore anything that ain't an option */
  119.             continue;
  120.         switch (c = *(argv[i++]+1)) {
  121.             case 'A':
  122.                 aopt = TRUE;
  123.                 break;
  124.             case 'N':
  125.                 nopt = TRUE;
  126.                 break;
  127.             case 'Q':
  128.                 qopt = TRUE;
  129.                 break;
  130.             case 'R':
  131.                 ropt = TRUE;
  132.                 break;
  133.             case 'T':
  134.                 topt = TRUE;
  135.                 break;
  136.             case 'F':           /* Alternate makefile name follows */
  137.                 argc--;
  138.                 filename = argv[i++];
  139.                 break;
  140.             default:
  141.                 printf("ERROR: Unknown option -%c ignored.\n",c);
  142.                 break;
  143.         }
  144.     }
  145. }
  146.  
  147. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  148.  
  149. void make(target)       /* Main routine for making.  This function
  150.                          * is recursive: it calls itself if it finds
  151.                          * another object listed in the dependancy_list.
  152.                          */
  153. char *target;   /* Name of object to make */
  154. {
  155.     TNODE   *snode;     /* current target node */
  156.     TNODE   *dnode;     /* dependancy node */
  157.     char    **linev;    /* for handling dependancy vector */
  158.  
  159. #ifdef  DEBUG
  160. debug("DEBUG make: making <%s>\n",target);
  161. #endif
  162.  
  163.  
  164. /* Find the node for this object and abort if not found */
  165.  
  166.     if (!(snode = find(target,root)))
  167.         serr("Don't know how to make <%s>\n",target);
  168.     snode->changed = FALSE;     /* start from unchanged */
  169.  
  170.  
  171. /* Evaluate all the dependancies */
  172.  
  173.     for(linev = snode->depends_on; *linev; linev++) {
  174.  
  175. #ifdef  DEBUG
  176. debug("    DEBUG make: Dependancy = %s\n",*linev);
  177. #endif
  178.  
  179. /* Find the node for the dependancy.  If the dependancy is not
  180.  * in the tree, it must be a file, and should be evaluated and
  181.  * added to the tree.
  182.  */
  183.  
  184.         if (!(dnode = find(*linev,root))) {
  185.             dnode = addfile(*linev);
  186.         }
  187.  
  188. /* If the state of the dependancy is UNKNOWN, then it must be
  189.  * an object that has yet to be made.  If so, then make the
  190.  * dependancy first.
  191.  */
  192.         if (dnode->changed == UNKNOWN) {
  193.             make(*linev);
  194.         }
  195.  
  196. /* If the status of the dependancy is TRUE, then set the object
  197.  * status to TRUE.
  198.  */
  199.         if (dnode->changed) {
  200.             (snode->changed)++;
  201.  
  202. #ifdef  DEBUG
  203. debug("\tDEBUG make (%s): Dependency state TRUE\n",target);
  204. #endif
  205.  
  206.         }
  207.     } /* Close of "for" -- Loop for all dependancies in list. */
  208.  
  209.  
  210. /* If, after evaluating dependancies, the object has been set TRUE,
  211.  * then push the actions onto the stack.  If -a option has been
  212.  * specified, push the actions no matter what the object's state.
  213.  */
  214.     if (snode->changed || aopt) {
  215.  
  216. #ifdef  DEBUG
  217. debug("DEBUG make (%s): Pushing actions\n",target);
  218. #endif
  219.  
  220.         for (linev = snode->do_this; *linev; linev++) {
  221.             if (!qopt)  /* skip the echo if -q is given */
  222.                 printf("\t%s\n",*linev);
  223.             push(*linev);
  224.         }
  225.     }
  226. }
  227.  
  228. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  229.  
  230. int dependancies()  /* Build the objectname tree */
  231. {
  232.     TNODE   *node;
  233.  
  234. /* Set up root of tree */
  235.  
  236.     if (node = makenode()) {
  237.  
  238. /* If a target was not given on the command line, then use the first
  239.  * object in the makefile as the target.
  240.  */
  241.         if (!first)
  242.             first = node->being_made;
  243.         if (!tree(node,&root))
  244.             err("Can't insert first node into tree.\n");
  245.  
  246. /* Makes nodes for all other objects and place into tree. */
  247.  
  248.         while (node = makenode()) {
  249.             if (!tree(node,&root))
  250.                 free(node);
  251.         }
  252.  
  253. #ifdef  DEBUG
  254. printf("DEBUG dependancies: Tree made.\n\n");
  255. trav(root);     /* Print tree */
  256. #endif
  257.  
  258.         fclose(iobuff);
  259.         return TRUE;
  260.     }
  261.     return FALSE;   /* Return FALSE if the makefile is empty */
  262. }
  263.  
  264. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  265.  
  266. int tree(node,rootp)    /* Place a node into the tree.  This function
  267.                          * is recursive, calling itself with the left
  268.                          * or right branches of the tree until it
  269.                          * finds the appropriate spot.
  270.                          */
  271. TNODE   *node;          /* Pointer to node to place in tree */
  272. TNODE   **rootp;        /* Pointer to pointer to current node in 
  273.                          * the tree traversal.
  274.                          */
  275. {
  276.     int notequal;       /* For evaluating < and > */
  277.  
  278. /* If the branch we were passed is a leaf node, then this is where
  279.  * the node goes.
  280.  */
  281.  
  282.     if (*rootp == NULL) {
  283.         *rootp = node;
  284.         return TRUE;
  285.     }
  286.  
  287. /* Otherwise, if the objectnames are equal, don't do anything
  288.  * and return FALSE so that the space allocated for this node
  289.  * will be freed up.
  290.  */
  291.  
  292.     if (!(notequal = strcmp((*rootp)->being_made, node->being_made)))
  293.         return FALSE;
  294.  
  295. /* Else, call tree() again with either the left (if <) or right (if >)
  296.  * branches.
  297.  */
  298.     return(tree(node,(notequal > 0) ? &((*rootp)->lnode)
  299.                                     : &((*rootp)->rnode)));
  300. }
  301.  
  302. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  303.  
  304. TNODE   *find(key,root) /* Find an object in the tree.  This function
  305.                          * is recursive, in a manner similar to tree()
  306.                          */
  307. char *key;              /* Object name to find */
  308. TNODE *root;            /* Current node in tree traversal */
  309. {
  310.     int notequal;
  311.  
  312. /* If the root passed was a leaf node, then find has failed */
  313.  
  314.     if (!root)
  315.         return FALSE;
  316.  
  317. /* Else, if objectnames are same, this is a match */
  318.  
  319.     if (!(notequal = strcmp(root->being_made,key))) {
  320.         return(root);
  321.     }
  322.  
  323. /* Else, call again with left or right branch. */
  324.  
  325.     return(find(key,(notequal > 0) ? root->lnode : root->rnode));
  326. }
  327.  
  328. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  329.  
  330. TNODE   *makenode()     /* Make an object node */
  331. {
  332.     char    *line;      /* Line of text from makefile */
  333.     char    *lp;        /* 2nd pointer to text line */
  334.     TNODE   *nodep;     /* Pointer to node made */
  335.  
  336.  
  337. /* Get the first line of the next statement in makefile */
  338.  
  339.  
  340. /* Skip blank lines, comments, and macros. Return on EOF. */
  341.  
  342.     do {
  343.         if ((line = getline(MAXLINE)) == NULL)  /* End of file */
  344.             return NULL;
  345.         if (*line == '$')               /* Handle a macro */
  346.             macro(line+1);
  347.     } while (!(*line) || *line == COMMENT || *line == '$');
  348.  
  349.  
  350. /* Be sure there's a colon after the objectname */
  351.  
  352.     for (lp = line; *lp && *lp != ':'; lp++)
  353.         ;
  354.     if (*lp != ':')
  355.         err("Missing ':'",0);
  356.  
  357.  
  358. /* Skip white space and set lp to point to the dependancies */
  359.  
  360.     else
  361.         for(*lp++ = 0; iswhite(*lp); lp++)
  362.             ;
  363.  
  364.  
  365. /* Allocate space for the node, and fill 'er up */
  366.  
  367.     nodep = gmem(NODESIZE);
  368.     nodep->lnode = nodep->rnode = 0;
  369.     nodep->being_made = line;
  370.     nodep->changed = UNKNOWN;
  371.     nodep->depends_on = stov(lp,MAXDEP);
  372.     nodep->do_this = getblock();
  373.  
  374. #ifdef  DEBUG
  375. debug("DEBUG makenode: Node made for %s\n",nodep->being_made);
  376. #endif
  377.  
  378.     return nodep;
  379. }
  380.  
  381. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  382.  
  383. char    *gmem(numbytes) /* Allocates memory and aborts on error */
  384. int numbytes;   /* Space to allocate */
  385. {
  386.     char *p;
  387.  
  388.     if (!(p = alloc(numbytes)))
  389.         err("Out of memory.",0);
  390.     return p;
  391. }
  392.  
  393. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  394.  
  395. void macro(line)        /* Handle macros. Only macros currently
  396.                          * supported are for default disk ($:d)
  397.                          * and default user ($/).
  398.                          */
  399. char    *line;          /* Body of macro */
  400. {
  401.     switch(*(line++)) {
  402.         case ':':                             /* Default disk */
  403.             if (*line >= 'A' && *line <= 'P')
  404.                 defdsk = *line - 'A' + 1;
  405.             break;
  406.         case '/':                             /* Default user */
  407.             if ((defuser = atoi(line)) > MAXUSER)
  408.                 defuser = UNKNOWN;
  409.             break;
  410.         default:
  411.             printf("ERROR: Unknown macro $%s ignored.\n",line-1);
  412.             break;
  413.     }
  414. }
  415.  
  416. /* end */
  417.  
  418. Allocates memor