home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d8xx / d810 / makedmake.lha / MakeDMake / MakeDMake.c < prev    next >
C/C++ Source or Header  |  1993-01-24  |  16KB  |  561 lines

  1. /*
  2.  *      MakeDMake v0.19 -- automatically construct a makefile for DMake
  3.  *
  4.  *      Basically it's Tim McGrath's 'MakeMake' from Fish 74,
  5.  *      -----------------------------------------------------
  6.  *
  7.  *      but it has been slightly hacked, so that it got much more
  8.  *      enterprising and workaholic, so to say.
  9.  *
  10.  *
  11.  *      HOW TO:
  12.  *
  13.  *      You give it the names of all the C-files that are to produce your
  14.  *      executable (but NOT #include'd .c or .h files!), and it will
  15.  *      automatically scan them to find all dependencies, and produce a
  16.  *      ready to use (in many cases) DMakeFile calling DCC with options
  17.  *      you will need for normal compilation and linking. Other things
  18.  *      can be added if needed by editing that file. In most cases it
  19.  *      should be enough to give MakeDMake '#? or '*' on command-line.
  20.  *      You will be than asked about the name of the executable, if you
  21.  *        just press RETURN -- you will get the default name, being EITHER
  22.  *        the NAME of the input file without the extension (if it was the
  23.  *        ONLY name MakeDMake got), or 'main' (without quotes).
  24.  *
  25.  *      For exemple, if you call it with the command-line like this:
  26.  *
  27.  *      MakeDMake file1.c file2.c file3.c ... filen.c
  28.  *
  29.  *      and choose 'ex_file' as the name for the executable,
  30.  *
  31.  *      you will get something like that in your DMakeFile:
  32.  *
  33.  *      OPT1 =
  34.  *      OPT2 =
  35.  *
  36.  *      ex_file         : file1.o file2.o file3.o ... filen.o
  37.  *          dcc $(OPT1) -o ex_file  file1.o file2.o file3.o ... filen.o
  38.  *
  39.  *      filex.o :    filex.c ... header file list ...
  40.  *          dcc $(OPT1) filex.c
  41.  *
  42.  *      where the header file list is the transitive closure of all
  43.  *      files #included in filex.c (i.e. if filex.c #includes "header.h",
  44.  *      and "header.h" #includes "subheader.h", both "header.h" and
  45.  *      "subheader.h" appear in the list of header files). MakeDMake only
  46.  *      examines header files delimited by double quotes ("). System header
  47.  *      files enclosed in angle brackets (< and >) are not examined.
  48.  *      (That last bit of beautiful native-speaker English was almost
  49.  *      literally taken from Tim's original comment. Now, back to my own
  50.  *      personal variant...)
  51.  *
  52.  *      As you probably know, if you have all properly set, 'dmake'
  53.  *      typed in the CLI with no parameters, will look for the
  54.  *      file called 'DMakeFile' in current directory and use its information
  55.  *      to update/recompile etc. your files. And it can be made even easier
  56.  *      with aliases. With two csh aliases like that:
  57.  *
  58.  *          alias mdm "MakeDMake *.c"   ## for all C files in current dir
  59.  *          alias go "DMake"            ## not so much really, but...
  60.  *
  61.  *              ## alternatively -- if you may have 'unique' makefile names
  62.  *                ## in the form <something>.make, you may use:
  63.  *
  64.  *            "alias go DMake -f *make"
  65.  *
  66.  *      I can compile most of the C programs with no effort whatsoever,
  67.  *      as most of the DCC I need use are already set in the ENV:DCCOPTS.
  68.  *      You can, however, recompile with your own standard set of options.
  69.  *      Many other things here can be easilly changed, adjusted and
  70.  *      then recompiled; also for other compilers than DICE. Try to do
  71.  *      that by changing the #defines first!
  72.  *
  73.  *      The original 'MakeMake' was in Public Domain, and this stuff here
  74.  *      also is.
  75.  *
  76.  *                                  Piotr Obminski, January 1993
  77.  */
  78.  
  79. #include <stdio.h>
  80.  
  81. #ifndef    void
  82. #define void int
  83. #endif
  84.  
  85. #define LINEMAX            80            /* length of line in created DMakeFile */
  86. #define CC_NAME         "dcc"        /* or e.g. "lc" for Lattice */
  87.  
  88. /* default name for executable (only if if given two or more C files!).
  89.  * NO '.' (like in 'a.out') PLEASE! This is still no UNIX!
  90.  */
  91. #define MAIN_NAME       "main"
  92.  
  93. /* this program doesn't NECESSARILY need to call itself MakeDMake...!
  94.  */
  95. #define OUTFILE_NAME    "DMakeFile"
  96.  
  97. /* default OPT1 & OPT2 strings
  98.  */
  99. #define O2X_OPT         NULL            /* 'object to executable' options */
  100. #define C2O_OPT         NULL            /* 'C to object' options */
  101.  
  102. /* zero if we want call outfile 'DMakeFile' instead of the individualized
  103.  * (unique) name in the form '<executable>.make'
  104.  */
  105. #define UNIQUE_OUTNAME    0
  106.  
  107. /*    ANSI sequences, #define them as '""' (no single quotes!)
  108.  *    if you hate 'em!
  109.  */
  110. #define    ANSI_BOLD    "›1m"
  111. #define    ANSI_WHITE    "›2m"                /* more or less... */
  112. #define ANSI_NORMAL    "›0m"
  113.  
  114. char main_name[ 40 ] = MAIN_NAME;
  115.  
  116. FILE *OutFile;
  117.  
  118.  
  119.     void
  120. main( argc, argv )
  121.     char **argv;
  122.     int argc;
  123. {
  124.     int dependent_count;
  125.     char **dependents;
  126.     char outfile_name[ 40 ] = OUTFILE_NAME;
  127.     char temp[ 40 ] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
  128.     int good_file_count = 0;
  129.     int i;
  130.  
  131.     /* no input or '?', 'h', 'H' or something beginning with '-', so
  132.      * he needs help!
  133.      */
  134.     if (     argc == 1 || ( *argv[ 1 ] == '?' ) || ( *argv[ 1 ] == '-' ) ||
  135.             ( ( *argv[ 1 ] == 'h' ) && ( *( argv[ 1 ] + 1 ) == '\0' ) )    ||
  136.             ( ( *argv[ 1 ] == 'H' ) && ( *( argv[ 1 ] + 1 ) == '\0' ) ) ) {
  137.  
  138.         puts( "------------------------------------------------------------\n"
  139.                 ANSI_BOLD "MakeDMake v0.19" ANSI_NORMAL ANSI_WHITE
  140.                 "  PD Utility for creating" ANSI_NORMAL ANSI_BOLD
  141.                 " DMakeFile" ANSI_NORMAL "'s\n"
  142.                 "------------------------------------------------------------\n"
  143.                 ANSI_WHITE "USAGE:  " ANSI_NORMAL "Give me names of your "
  144.                 ANSI_BOLD "source files " ANSI_NORMAL
  145.                 "and then we'll\n"
  146.                 "        get interactive.  If I'm successful, the resulting\n"
  147.                 "        " ANSI_BOLD "DMakeFile" ANSI_NORMAL
  148.                 " will be in the current directory.\n"
  149.                 "        Now that you know -- "
  150.                 ANSI_BOLD "START AGAIN" ANSI_WHITE " !\n" ANSI_NORMAL
  151.                 "------------------------------------------------------------" );
  152.  
  153.         exit ( 1 );
  154.     }
  155.  
  156.     /* if we got only one command-line argument, and it ends in '.c'
  157.      * we start by cutting off this '.c', then we look...
  158.      */
  159.     if ( argc == 2 ) {
  160.         register char *p;
  161.         strcpy( temp, argv[ 1 ] );
  162.         if ( ( p = (char *)strstr( temp, ".c" ) ) && ( *( p + 2 ) == '\0' ) ) {
  163.             *p = '\0';
  164.             strcpy( main_name, temp );
  165.         }
  166.         else {
  167.             puts( ANSI_BOLD "This stuff you gave me ain't no C code!. "
  168.                     ANSI_NORMAL ANSI_WHITE "Aborting!" ANSI_NORMAL );
  169.             exit( 1 );
  170.         }
  171.     }
  172.  
  173.     printf( "Name for the executable? (or "
  174.             ANSI_BOLD "RETURN" ANSI_NORMAL " for "
  175.                 "'" ANSI_WHITE "%s" ANSI_NORMAL "'): ", main_name );
  176.  
  177.     gets( temp );
  178.  
  179.     if ( isalpha( *temp ) ) {
  180.         strcpy( main_name, temp );
  181.  
  182. #if UNIQUE_OUTNAME
  183.            /* change name of the generated MakeFile to
  184.          * <executable>.make
  185.          */
  186.          strcpy( outfile_name, temp );
  187.          strcat( outfile_name, ".make" );
  188. #endif
  189.  
  190.     }
  191.  
  192.     printf( "------------------------------------------------------" );
  193.  
  194.     {
  195.          register short i = strlen( temp );
  196.         if ( i )
  197.              putchar( '-' );
  198.  
  199.          while ( i-- )
  200.                  putchar( '-' );
  201.     }
  202.  
  203.     putchar( '\n' );
  204.  
  205.     if ( ( OutFile = fopen( outfile_name, "w" ) ) == 0L ) {
  206.         printf( ANSI_BOLD "CAN'T CREATE " ANSI_NORMAL ANSI_WHITE
  207.                     "%s" ANSI_NORMAL ANSI_BOLD " !\n\07" ANSI_NORMAL,
  208.                     outfile_name );
  209.         exit( 1 );
  210.     }
  211.  
  212.     fputs( "# DMakeFile generated by MakeDMake v0.19\n\n", OutFile );
  213.     fprintf( OutFile, "OPT1 = %s\nOPT2 = %s\n\n", O2X_OPT, C2O_OPT );
  214.  
  215.     depend_file( argc, argv, main_name, "  ", ".o" );
  216.  
  217.     for ( i = 1; i < argc; i++ ) {
  218.         /* we don't want MakeDMake to make a fool of himself by
  219.          * trying to compile various ReadMe's and Trashcan.icon!
  220.          * So we are skipping such stuff! However it's not always totally
  221.          * successful, so far...
  222.          */
  223.         register char *p;
  224.         if ( ( p = (char *)strstr( argv[ i ], ".c" ) ) &&
  225.                                             ( *( p + 2 ) == '\0' ) ) {
  226.             get_dependents( argv[ i ], &dependents, &dependent_count );
  227.             depend_file( dependent_count, dependents, argv[ i ], ".o", "" );
  228.             free_space( dependents, dependent_count );
  229.             good_file_count++;
  230.         }
  231.         else {
  232.             printf( "%s", argv[ i ] );
  233.             if ( strlen( argv[ i ] ) < 16 )
  234.                 putchar( '\t' );
  235.             if ( strlen( argv[ i ] ) < 8 )
  236.                 putchar( '\t' );
  237.             puts( "\t" ANSI_NORMAL ANSI_WHITE "<" ANSI_NORMAL ANSI_BOLD
  238.                     "ignored" ANSI_NORMAL ANSI_WHITE ">" ANSI_NORMAL );
  239.         }
  240.     }
  241.  
  242.     fclose( OutFile );
  243.     if ( good_file_count == 0 ) {
  244.         puts( ANSI_WHITE "Not even one good C file!" ANSI_NORMAL ANSI_BOLD
  245.                 " Who" ANSI_NORMAL "(m) " ANSI_BOLD
  246.                 "do you think you're kidding?" ANSI_NORMAL );
  247.         exit( 1 );
  248.     }
  249.     exit( 0 );
  250. }
  251.  
  252.  
  253.     int
  254. free_space( dp, dc )
  255. /*
  256.  * Purpose: free up list of file names
  257.  * Inputs:  dp - points to list of pointers to strings
  258.  *          dc - number of pointers in the list
  259.  */
  260.     char **dp;
  261.     int dc;
  262. {
  263.     while ( dc > 0 ) {
  264.         free( *dp++ ); dc--;
  265.     }
  266.     /* free( dp );  <------ here is the offender in the original
  267.      *                         which makes the program to hang.
  268.      */
  269. }
  270.  
  271.  
  272.     char *
  273. file_exten( pgm_name, xtension, bufout )
  274. /*
  275.  * Purpose: append new extension onto file name
  276.  * Inputs:  pgm_name - pointer to name of file
  277.  *          xtension - pointer to new file name extension (2 chars only)
  278.  * Outputs: bufout - points to area for new file name
  279.  * Returns: bufout
  280.  */
  281.      char *pgm_name, *xtension, *bufout;
  282. {
  283.     int i = 0;
  284.  
  285.     while ( *pgm_name ) {
  286.         bufout[ i++ ] = *pgm_name;
  287.         if ( *pgm_name++ == '.' && xtension[ 0 ] != '\0' ) {
  288.             bufout[ i++ ] = xtension[ 1 ];
  289.             break;
  290.         }
  291.     }
  292.     bufout[ i ] = '\0';  return( bufout );
  293. }
  294.  
  295.  
  296.     void
  297. depend_file( ct, flist, pgm_name, pgmx, filex )
  298. /*
  299.  * Purpose: print file name and list of dependents
  300.  * Inputs:  ct - number of dependents in the list
  301.  *          flist - pointer to a list of pointers to dependent names
  302.  *          pgm_name - name of file whose dependents are being printed
  303.  *          pgmx - extension for pgm_name file (or "" if none)
  304.  *          filex - extension for dependent file names (or "" if none)
  305.  */
  306.     char **flist, *pgm_name, *pgmx, *filex;
  307.     int ct;
  308. {
  309.     int i;
  310.     char buf[ LINEMAX ], add_name[ LINEMAX ], pname[ LINEMAX ];
  311.     char bare_name[ LINEMAX ];
  312.  
  313.     {
  314.         register short i = 0;
  315.  
  316.         do {
  317.             bare_name[ i ] = pgm_name[ i ];
  318.         } while ( pgm_name[ i ] && ( pgm_name[ i++ ] != '.' ) );
  319.  
  320.         bare_name[ --i ] = '\0';
  321.     }
  322.  
  323.     start_line( file_exten( pgm_name, pgmx, pname ), buf );
  324.  
  325.     if ( strcmp( pgm_name, main_name ) )
  326.         strcat( strcat( buf, " " ), pgm_name );
  327.  
  328.     for ( i = 1; i < ct; i++ ) {
  329.  
  330.         file_exten( flist[ i ], filex, add_name );
  331.  
  332.         if ( columns( buf ) + strlen( add_name ) + 1 >= LINEMAX - 1 ) {
  333.             fputs( buf, OutFile ); fputs( "\\\n", OutFile );
  334.             continue_line( file_exten( NULL, NULL, pname ), buf );
  335.         }
  336.         strcat( strcat( buf, " " ), add_name );
  337.     }
  338.     fputs( buf, OutFile ); fputc( '\n', OutFile );
  339.  
  340.     if ( strcmp( pgm_name, main_name ) ) {
  341.         fprintf( OutFile,"\t%s $(OPT2) -c %s.o %s.c\n\n",
  342.                                         CC_NAME, bare_name, bare_name );
  343.     }
  344.     else {
  345.         fprintf( OutFile, "\t%s $(OPT1) -o %s ", CC_NAME, main_name );
  346.         output_objects( flist, ct );
  347.         fputs( "\n\n", OutFile );
  348.     }
  349. }
  350.  
  351.  
  352.     void
  353. output_objects( files, count )
  354.     char **files;
  355.     int count;
  356. {
  357.     unsigned short i, col, len;
  358.     char arr[ 40 ], *brk1, *brk2;
  359.     register char *p;
  360.  
  361.     col = 32;       /* more or less initial column */
  362.  
  363.     for ( i = 1; i < count; i++ ) {
  364.         /* see if file has legal name ending in '.c'
  365.          */
  366.         if ( ( p = (char *)strstr( files[ i ], ".c" ) ) &&
  367.                                             ( *( p + 2 ) == '\0' ) ) {
  368.  
  369.             strcpy( arr, files[ i ] );
  370.  
  371.             len = strlen( arr );
  372.             col += len;
  373.  
  374.             if ( col >= LINEMAX - 2 ) {
  375.                 brk1 = "\\\n\t\t\t";
  376.                 brk2 = "";
  377.                 col = 32 + len + 1;
  378.             }
  379.             else {
  380.                 brk1 = " ";
  381.                 brk2 = "";
  382.                }
  383.  
  384.             p = (char *)strchr( arr, '.' );
  385.             *++p = 'o';
  386.  
  387.             fprintf( OutFile, "%s%s%s", brk1, arr, brk2 );
  388.         }
  389.     }
  390. }
  391.  
  392.  
  393.     void
  394. start_line( with_name, buf )
  395. /*
  396.  * Purpose: give each line a standard indentation
  397.  * Inputs:  with_name - name of root file on each line
  398.  *          buf - place to put indented line
  399.  */
  400.     char *with_name, *buf;
  401. {
  402.     strcpy( buf, with_name ); strcat( buf, "\t" );
  403.  
  404.     if ( columns( buf ) < 16 )
  405.         strcat( buf, "\t" );
  406.  
  407.     if ( columns( buf ) < 24 )
  408.         strcat( buf, "\t" );
  409.  
  410.     strcat( buf, ":" );
  411. }
  412.  
  413.  
  414.     void
  415. continue_line( with_name, buf )
  416. /*
  417.  * Purpose: let the line continue after '\' and '\n'
  418.  * Inputs:  with_name - name of root file on each line
  419.  *          buf - place to put indented line
  420.  */
  421.     char *with_name, *buf;
  422. {
  423.     strcpy( buf, with_name );
  424.     strcat( buf, "\t " );
  425.  
  426.     if ( columns( buf ) < 16 )
  427.         strcat( buf, "\t " );
  428.  
  429.     if ( columns( buf ) < 24 )
  430.         strcat( buf, "\t " );
  431. }
  432.  
  433.  
  434.     int
  435. columns( s )
  436. /*
  437.  * Purpose: count the number of columns a line spans
  438.  * Inputs:  s - the characters in a line
  439.  * Returns: the number of columns ( including tab expansion )
  440.  */
  441.     char *s;
  442. {
  443.     int col = 0;
  444.  
  445.     while ( *s ) {
  446.         if ( *s++ == '\t' )
  447.             while ( ++col & 7 )
  448.                 ;
  449.         else ++col;
  450.     }
  451.     return( col );
  452. }
  453.  
  454.  
  455.     void
  456. get_dependents( fn, depv, depc )
  457. /*
  458.  * Purpose: return a list of files depending on a C source file
  459.  * Inputs:  fn - name of the c source file
  460.  * Outputs: depv - list of dependents (an array of pointers to filenames)
  461.  *          depc - number of dependents
  462.  */
  463.      char *fn, ***depv;
  464.      int *depc;
  465. {
  466.     char **lst;
  467.     int i;
  468.  
  469.     lst = (char **)malloc( 1024 * sizeof( char * ) );
  470.     move_name( &lst[ 0 ], fn );
  471.     fputs( fn, stdout ); fputc( '\n', stdout ); i = 0;
  472.  
  473.     scan_file( lst, &i, fn ); *depv = lst; *depc = i + 1;
  474. }
  475.  
  476.  
  477.     void
  478. move_name( p, s )
  479. /*
  480.  * Purpose: Allocate space for a new filename and copy it
  481.  * Inputs:  p - location for new pointer to filename
  482.  *          s - pointer to file name
  483.  */
  484.      char **p, *s;
  485. {
  486.     *p = (char *)malloc( strlen( s ) + 1 );
  487.     strcpy( *p, s );
  488. }
  489.  
  490.  
  491.     void
  492. scan_file( file_name_list, last_list_used, fn )
  493. /*
  494.  * Purpose: search a C source file file #includes, and search the #includes
  495.  *          for nested #includes
  496.  * Inputs:  fn - name of file to scan
  497.  * Outputs: file_name_list - list of included files
  498.  *          last_list_used - last used filename position in file_name_list
  499.  */
  500.     char **file_name_list, *fn;
  501.     int *last_list_used;
  502. {
  503.     FILE *fp;
  504.     char buf[ 1024 ], ifn[ LINEMAX ];
  505.     int j,k;
  506.  
  507.     fp = fopen( fn, "r" );
  508.  
  509.     if ( ! fp ) {
  510.         fprintf( stdout, ANSI_BOLD "\tcouldn't open file "
  511.                     ANSI_NORMAL ANSI_WHITE "%s\n" ANSI_NORMAL, fn );
  512.         return;
  513.     }
  514.  
  515.     while ( fgets( buf, 1024, fp ) ) {
  516.         if ( strncmp( buf, "#include", 8 ) == 0 ) {
  517.             j = 8;
  518.             while ( buf[ j ] == ' ' || buf[ j ] == '\t' )
  519.                 j++;
  520.             if ( buf[ j++ ] != '"' )
  521.                 continue;
  522.             k = 0;
  523.             while ( buf[ j ] ) {
  524.                 if ( buf[ j ] == '"' || buf[ j ] == '\n' )
  525.                     break;
  526.                 else
  527.                     ifn[ k++ ] = buf[ j++ ];
  528.             }
  529.             ifn[ k ] = '\0';
  530.             if ( add_name( file_name_list, last_list_used, ifn ) )
  531.                 scan_file( file_name_list, last_list_used, ifn );
  532.         }
  533.     }
  534.     fclose( fp );
  535. }
  536.  
  537.  
  538.     int
  539. add_name( file_name_list, last_list_used, fn )
  540. /*
  541.  * Purpose: Add a file name to the list if it's not there already
  542.  * Inputs:  file_name_list - pointer to array of pointers to file names
  543.  *          last_list_used - last element in array with a filename
  544.  *          fn - name of file
  545.  * Returns: 1 if file name added, 0 otherwise
  546.  */
  547.     char **file_name_list, *fn;
  548.     int *last_list_used;
  549. {
  550.     int i;
  551.  
  552.     for ( i = 0; i <= *last_list_used; i++ )
  553.         if ( ! strcmp( file_name_list[ i ], fn ) )
  554.             return( 0 );
  555.  
  556.     *last_list_used += 1;
  557.     move_name( &file_name_list[ *last_list_used ], fn );
  558.     return( 1 );
  559. }
  560.  
  561.