home *** CD-ROM | disk | FTP | other *** search
/ BCI NET 2 / BCI NET 2.iso / archives / programming / utilities / makemake1.02.lha / makemake.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1994-07-04  |  11.1 KB  |  365 lines

  1. // MakeMake (C)1994 by Jeff Shepherd
  2. // a makefile compiler for use in C and C++ programs
  3.  
  4. // Originally Compiled with g++ version 2.5.8 on an Amiga 4000/030/6/120
  5. // Also tested with g++ version 2.3 on SunOS V4.1.3 revision 2
  6. // compile me by typing g++ -O2 makemake.cc -o makemake
  7.  
  8. // to see the command line just type "makemake"
  9.  
  10. // COMMAND LINE: makemake [-f makefilename] [-s] [-I directory] 
  11. //               [-C|-C++] files.[c,cc] ...
  12. // -f output results to makefilename. 'Makefile' is the default name
  13. // -I specifies other directories to search for dependency files
  14. //    default is the current directory.
  15. // -s shortcut. Don't compile .o files. Used when you have only one file. 
  16. //    Default false
  17. // -C|C++ tells makemake you are compiling C or C++ files. Default -C
  18. // files.[c,cc] a list of the .c or .cc files.
  19. // EG. makemake *.cc will supply all the C++ files in this directory.
  20.  
  21. // NOTE: makemake assumes that 'compilername' can invoke the compiler and the 
  22. //       linker
  23. //     ( this is the case for g++ and u++?? )
  24. //     if this is not the case, just add lines to get the linker name
  25. //     and change the line '$(CC) -o $(OUTPUT) $(OBJ)' to something like
  26. //     '$(LINK) -o $(OUTPUT) $(OBJ)'
  27. //     ALSO YOU CANNOT USE THE -s OPTION IF YOU MAKE THIS CHANGE
  28.  
  29. // TODO: Allow multiple executable files to be compiled
  30.  
  31. #include <iostream.h>
  32. #include <iomanip.h>
  33. #include <fstream.h>
  34. #include <string.h>
  35. #include <stdlib.h>
  36. #include <curses.h>
  37.  
  38. // change MAX_LENGTH if the program line length is bigger or the length of
  39. // options is bigger
  40. // total length of all .c files is 3*MAX_LENGTH
  41. #define MAX_LENGTH 256
  42.  
  43. // Column width
  44. // when each line of OBJ goes past COL_WIDTH, '\' is appended
  45. // and the list is continued on the next line
  46. #define COL_WIDTH 75
  47.  
  48.  
  49. // amiga version string
  50. // to find out the version of makemake just type verson makemake
  51. char *VERSION = "$VER: makemake version 1.02 (06/03/94)";
  52.  
  53. // Linked list class
  54. // NOTE: you have to initialize the list to NULL
  55. // eg. LList *test = NULL;
  56. // This list could be generalized using templates but I don't know how to use 
  57. // them
  58. class LList {
  59.   char *Listoption;
  60.   LList *NextList;
  61. public:
  62.   LList *Add(char *toption);          // Add an item to the linked list
  63.   void PrintList();                   // Print the list
  64.   LList *Next() {return NextList;}    // Get the Next pointer
  65.   char *option() {return Listoption;} // Get the linked list item
  66.   ~LList();                           // Destructor
  67.   long Len();                         // Length of list
  68. };
  69.  
  70. void LList :: PrintList() {
  71.   LList *list = this;
  72.   while(list->NextList) {
  73.     cout << list->Listoption << endl;
  74.     list=list->NextList;
  75.   }
  76. }
  77.  
  78. LList * LList::Add(char *toption) {
  79.     LList *result = new LList;
  80.     result->Listoption=strdup(toption);
  81.     result->NextList = this;
  82.     return result;
  83. }
  84.  
  85. LList ::~LList() {
  86.     LList *temp, *temp2 = this;
  87.     while(temp2) {
  88.       temp = temp2;
  89.       temp2 = temp2->NextList;
  90.       delete(temp);
  91.    }
  92. }
  93.  
  94. long LList :: Len() {
  95.  long result = 0;
  96.  LList *temp = this;
  97.     while(temp->NextList) {
  98.     result++;
  99.     temp=temp->NextList;
  100.     }
  101.     return result;
  102. }
  103.  
  104. // program prototypes
  105. void basename(char **,char *);
  106. void checkfile(ofstream &makefile, char *, LList *, char *);
  107.  
  108. main(int argc, char **argv) {
  109.  
  110.   char compiler[MAX_LENGTH];           // compiler name
  111.   char base[35], *baseptr = base;      // used when calling basename
  112.   char compileroptions[MAX_LENGTH];    // options invoked when the compiler is 
  113.                        //called
  114.   char outname[MAX_LENGTH];           // executable name
  115.   char linkeroptions[MAX_LENGTH];      // options passed to the linker
  116.   char makefilename[MAX_LENGTH] = "Makefile";
  117.                        // name of makefile 'Makefile' is default
  118.   char objects[3 * MAX_LENGTH] = {0,}; // list of .o files
  119.   LList *source=NULL;               // list of source files
  120.   LList *includedir=NULL;           // list of other include directories to 
  121.                        // search
  122.   short shortcut = FALSE;           // if TRUE then don't compile .o files
  123.   short CFiles = TRUE;               // TRUE means C files, FALSE- C++ files
  124.   char suffix[4]=".c";               // .c or .cc
  125.   // print command line
  126.   if (argc == 1) {
  127.     cout << "makemake: a Makefile compiler. Version 1.02" << endl;
  128.     cout << "(C)1994 by Jeff Shepherd" << endl;
  129.     cout << "COMMAND LINE: " << endl;
  130.     cout << "makemake [-f makefilename] [-s] [-I directory]"
  131.      << "[-C|-C++] files.[c,cc] ..." << endl;
  132.     cout << "-f output results to makefilename. 'Makefile' is the default name"
  133.      << endl;
  134.     cout << "-s shortcut. Don't compile .o files. Used when you have only one "
  135.      << "file." << endl <<"Default FALSE" << endl;
  136.     cout << "   If you use this option, put the compiler options in the linker"
  137.      << " options" << endl;
  138.  
  139.     cout << "-I specifies other directories to search for dependency files" 
  140.      << endl;
  141.     cout << "   the default directory is the current directory." << endl;
  142.     cout << "[-C|-C++] specifies compiling of C++ or C files. Default -C.";
  143.     cout << "files.[c,cc] a list of .c or .cc files." << endl;
  144.     cout << "eg. makemake *.cc will supply all the C++ files in this directory."
  145.          << endl << endl;
  146.     exit(0);
  147.   }
  148.  
  149.   // get source files and parse arguments
  150.   for (int i=1; i < argc;i++) {
  151.  
  152.     // do command line options
  153.     // structure allows for expansion
  154.     // commands could be written with or without spaces
  155.     // eg -ftestfile -f testfile
  156.     if (argv[i][0] == '-') {
  157.     switch (argv[i][1]) {
  158.         case 'f' :
  159.         if (argv[i][2]) {
  160.             strcpy(makefilename,&argv[i][2]);
  161.         }
  162.         else {
  163.           i++;
  164.           strcpy(makefilename,argv[i]);
  165.         }
  166.         break;
  167.  
  168.         case 's' :
  169.         shortcut = TRUE;
  170.         break;
  171.  
  172.         case 'I' :
  173.         if (argv[i][2]) {
  174.            includedir=includedir->Add(&argv[i][2]);
  175.         }
  176.         else {
  177.            i++;
  178.            includedir=includedir->Add(argv[i]);
  179.         }
  180.         break;
  181.  
  182.         case 'C' :
  183.         if (!argv[i][2]) {
  184.            CFiles = TRUE;
  185.                    strcpy(suffix,".c");
  186.                 }
  187.                 else if (!strcmp(&argv[i][2],"++")) {
  188.            CFiles = FALSE;
  189.                    strcpy(suffix,".cc");
  190.                 }
  191.             break;
  192.  
  193.         // other cases go here
  194.         default:
  195.         cout << "illegal option: " << argv[i] << endl;
  196.         exit(3);
  197.          break;
  198.     } //switch
  199.     }// if
  200.     else {
  201.        source=source->Add(argv[i]);
  202.        basename(&baseptr,argv[i]);
  203.  
  204.        // put a '\' on the end of the line if the new line length > COL_WIDTH
  205.        if ( (strlen(objects) / COL_WIDTH) <
  206.         ((strlen(objects)+strlen(baseptr)+2) / COL_WIDTH) ) {
  207.        strcat(objects,"\\\n      ");
  208.        } // if
  209.        strcat(objects,baseptr);
  210.        strcat(objects,".o ");
  211.     } // else
  212.   } // for
  213.  
  214.   cout << "Compiler Name: ";
  215.   cin >> compiler;
  216.  
  217.   cout <<  "Compiler options: ";
  218.   cin.getline(compileroptions,MAX_LENGTH);
  219.   // ?? I need to do this twice WHY??
  220.   cin.getline(compileroptions,MAX_LENGTH);
  221.  
  222.   cout    << "Linker options: ";
  223.   cin.getline(linkeroptions,MAX_LENGTH);
  224.  
  225.   cout << "Output filename: ";
  226.   cin >> outname;
  227.  
  228.   // start writing out the makefile
  229.   ofstream makefile(makefilename);
  230.  
  231.   makefile << "#Makefile generated by Makemake (C)1994 Jeff Shepherd" << endl;
  232.   makefile << "CC = " << compiler << endl;
  233.   makefile << "CFLAGS = " << compileroptions << endl;
  234.   makefile << "LFLAGS = " << linkeroptions << endl;
  235.  
  236.   // list .o files only if shortcut is false
  237.   // else list the .c or .cc file
  238.   // NOTE: I assume there is only 1 .c[c] file
  239.   if (!shortcut) {
  240.      makefile << "OBJ = " << objects << endl ;
  241.   }
  242.   else {
  243.      makefile << "OBJ = " << source->option() << endl;
  244.   }
  245.  
  246.   makefile << "OUTPUT = " << outname << endl << endl;
  247.  
  248.   // compile the .c or .cc files
  249.   if (!shortcut) {
  250.      makefile <<".SUFFIXES: " << suffix << " .h .o" << endl;
  251.      makefile << suffix << ".o:" << endl;
  252.      makefile << "\t$(CC) $(CFLAGS) -c $< -o $@" << endl << endl;
  253.   }
  254.   makefile << "all : $(OUTPUT)\n" << endl;
  255.   makefile << "$(OUTPUT) : $(OBJ)" << endl;
  256.   makefile << "\t$(CC) -o $(OUTPUT) $(OBJ) $(LFLAGS)" << endl << endl;
  257.  
  258.   // check and print out any file dependencies
  259.   for (LList *sourcelist=source; sourcelist; sourcelist=sourcelist->Next()) {
  260.  
  261.     // UPDATE 06/03/94: Don't check .o files
  262.     char *dotpos;
  263.     if ( !( (dotpos=strrchr(sourcelist->option(),'.')) && 
  264.             (*(++dotpos) == 'o') ) ) {
  265.  
  266.        // UPDATE 05/??/94: Should list .o file instead of .cc or .c file
  267.        char objectname[35]; char *object = objectname;
  268.        basename(&object,sourcelist->option());
  269.        strcat(object,".o");
  270.        checkfile(makefile,object,includedir,sourcelist->option());
  271.     }
  272.   }
  273.   makefile.close();
  274. }
  275.  
  276. // strips the suffix from a filename
  277. // eg tempfile.c -> tempfile
  278. // result is returned in base
  279. // very similar to the unix 'basename' command
  280. void basename(char **base, char *name) {
  281.    char *pos = strchr(name,'.');
  282.    int length;
  283.  
  284.    if (pos) {
  285.        length = strlen(name)-strlen(pos);
  286.        strncpy(*base,name,length);
  287.        (*base)[length]='\0';
  288.    }
  289.    else {
  290.        strcpy(*base,name);
  291.    }
  292. }
  293.  
  294. // search through .c or .cc file and find dependencies
  295. // The method I will use is #include "file.h" where the file.h is enclosed
  296. // in quotes instead of '<' and '>'.
  297. // Usually compiler includes are enclosed in '<' and '>'.
  298. // In the makefile you will see:
  299. // <infile.c[c]> : file.h
  300. // checkfile also recurses into file.h to find any other dependencies
  301. void checkfile(ofstream &makefile, char *cfile, LList *dir, char *includefile) {
  302.     ifstream file(includefile);   // open file for reading
  303.     char line[MAX_LENGTH];      // line inputted from file
  304.     char dependname[MAX_LENGTH];  // name of dependent include file
  305.     char completepath[MAX_LENGTH]; // path and filename
  306.     char *firstquote, *secondquote; // position of "'s in the line
  307.     int length;                    // length of completepath
  308.     LList *includedir = dir;
  309.  
  310.     // check include directories for include file
  311.     while (!file && includedir) {
  312.  
  313.        // tack the directory name held in directory L-List onto the filename 
  314.        // and check again
  315.        strcpy(completepath,includedir->option());
  316.        length = strlen(completepath);
  317.        if ( !(completepath[--length] == ':' || completepath[length] == '/') ) {
  318.       strcat(completepath,"/");
  319.        }
  320.        strcat(completepath,includefile);
  321.        includedir=includedir->Next();
  322.        file.open(completepath);
  323.     }
  324.  
  325.     // file is not found
  326.     if (!file) {
  327.        cerr << includefile << " not found." << endl;
  328.        exit(5);
  329.     }
  330.  
  331.     file.getline(line,MAX_LENGTH);
  332.     // search the whole file
  333.     while (file.eof() == FALSE) {
  334.        // search for '"#include "' or '#    include "'
  335.        if ( (line[0] == '#') && (strstr(line,"include")) && 
  336.         (firstquote=strchr(line,'\"')) ) {
  337.        firstquote++;
  338.        if (secondquote=strchr(firstquote,'\"')) {
  339.  
  340.           // blank out dependname first
  341.           // This needs to be done for some reason
  342.           for (int i=0;i < MAX_LENGTH; i++) {
  343.           dependname[i] =0;
  344.           }
  345.  
  346.           // copy the dependency file name
  347.           strncpy(dependname,firstquote,strlen(firstquote)-
  348.               strlen(secondquote));
  349.  
  350.           // write out the dependency
  351.           makefile << cfile << " : " << dependname << endl;
  352.  
  353.           // recurse into the dependent file
  354.           checkfile(makefile,cfile,dir,dependname);
  355.  
  356.        } // if
  357.        } // if
  358.  
  359.        // do line-by-line parsing
  360.        file.getline(line,MAX_LENGTH);
  361.     } // while
  362.     file.close();
  363. }
  364.  
  365.