mann@Navajo.ARPA (10/06/84)
#! /bin/sh : This is a shar archive. Extract with sh, not csh. echo x - buildmake.1 cat > buildmake.1 << '926!Funky!Stuff!' .TH BUILDMAKE 1 "4 April 1984" .SU .SH NAME buildmake \- preprocessor to provide extended syntax for makefiles .SH SYNOPSIS .B buildmake [ -f filename -Dx1=y1 -Dx2=y2 ... .B ] .SH DESCRIPTION .I Buildmake is a preprocessor that translates "buildfiles" into makefiles for use by the .IR make (1) program. A buildfile has the same syntax as a makefile, with the addition of two features. A line of the form "#include \fIfilename\fP" causes the named file to be inserted in the output, replacing the #include directive. An #ifdef/#else/#endif construct is also available, allowing sections of a buildfile to be conditionally included in the constructed makefile depending on whether it has defined a given symbol at the point the #ifdef is seen, as shown below. .PP .IP .nf NAME1=yes #ifdef NAME1 This will be included in the output #else NAME1 This will not be included #endif NAME1 #ifdef NAME2 This will not be included unless NAME2 is defined elsewhere #endif NAME2 .fi .PP The \-D command line option is used to define symbols on the command line. The option -DNAME=value causes the line "NAME=value" to be inserted in the constructed makefile and causes NAME to be considered "defined" in subsequent #ifdef statements. .PP The \-f option is used to specify the name of the input file, which defaults to "buildfile." The output file is always named "makefile." .SH "SEE ALSO" make(1) .SH AUTHOR Marvin Theimer, Stanford. 926!Funky!Stuff! echo x - buildfile cat > buildfile << '926!Funky!Stuff!' INSTALLDIR = /usr/stanford/bin buildmake: buildmake.c cc -o buildmake buildmake.c install: install -q -s buildmake $(INSTALLDIR) install -q -c buildmake.1 /usr/man/man1/buildmake.1 clean: rm -f *BAK *CKP buildmake cleanbak: rm -f *BAK *CKP build: buildmake xbuild: buildmake -DX=1 926!Funky!Stuff! echo x - buildmake.c cat > buildmake.c << '926!Funky!Stuff!' /* * Program to output a makefile from a buildfile. * * Written by Marvin Theimer 6/83 * * Modified -- RJN -- 9/20/83 * - Added '-f' option * - Removed the need for "buildmake.sh" by having buildmake * create the temporary makefile and link it to "makefile" * upon success. * Marvin Theimer, 11/25/83 * Changed MaxLine to 200 from 82 to allow handling of long input * lines. Fixed fprintf statement in ErrorExit to contain the correct * number of arguments. Added a string arg. to ErrorExit to allow * more informational error exits. ErrorExit now unlinks the tmp * file when exitting. * Marvin Theimer, 1/10/83 * Added #include capability. */ #include <stdio.h> #include <errno.h> #define MaxLine 200 #define MaxBf 32 #define BlankType 0 #define CommentType 1 #define MacroDefnType 2 #define IfdefType 3 #define ElseType 4 #define EndifType 5 #define IncludeType 6 #define OtherType 7 struct MacroRec { char *macName; char *macDefn; struct MacroRec *next; }; char TmpMakeFileName[] = "BUILDXXXXXXXX"; /* X's are for mktemp() */ FILE *Bf[MaxBf], *fopen(); int CurrentBf = 0; char Line[MaxLine]; int LineNo = 0; struct MacroRec *Macros = NULL; char *GetLine(); char *substr(); extern char *malloc(); extern int errno; main(argc, argv) int argc; char **argv; { int nArg = 1; char *line; int indx; char *BuildFileName = "buildfile"; /* Reopen stdout as "makefile" */ if ( freopen( mktemp( TmpMakeFileName ), "w", stdout ) == 0 ) { fprintf( stderr, "Cannot open for writing " ); perror( TmpMakeFileName ); exit( errno ); } /* * Process input arguments to build program. */ while (nArg < argc) { if ( argv[nArg][0] == '-' ) switch (argv[nArg][1] ) { case 'D': strcpy(Line, &(argv[nArg][2]) ); indx = strlen(Line); Line[indx++] = '\n';/* Make it look like an input line. */ Line[indx] = '\0'; ProcessStatement(Line); break; case 'f': nArg++; BuildFileName = argv[nArg]; break; } nArg++; } /* * Open the buildfile for reading. */ Bf[0] = fopen(BuildFileName, "r"); if (Bf[0] == NULL) { perror( BuildFileName ); exit( errno ); } /* * Print out a warning about automated output. */ printf("# WARNING: DO NOT EDIT.\n"); printf("# THIS FILE WAS AUTOMATICALLY GENERATED USING THE\n"); printf("# BUILDMAKE PROGRAM.\n\n"); /* * Parse the file. */ line = GetLine(); while (line != NULL) { ProcessStatement(line); line = GetLine(); } /* * Close the buildfile. */ fclose( Bf[0] ); if ( link( TmpMakeFileName, "makefile" ) != 0 ) if ( link( TmpMakeFileName, "makefile" ) != 0 ) if ( errno != EEXIST || unlink( "makefile" ) != 0 || link( TmpMakeFileName, "makefile" ) != 0 ) { fprintf( stderr, "Error linking to " ); perror( "makefile" ); fprintf( stderr, "Makefile exists as \"%s\"\n", TmpMakeFileName ); exit( errno ); } if ( unlink( TmpMakeFileName ) != 0 ) { fprintf( stderr, "Cannot unlink " ); perror( TmpMakeFileName ); exit( errno ); } } LineType(line) char *line; { int t; char *p = line; while ((*p == ' ') || (*p == '\t')) p++; if (*p == '\n') return(BlankType); if (*p == '#') { if (substr(p, "#ifdef") != NULL) return(IfdefType); else if (substr(p, "#else") != NULL) return(ElseType); else if (substr(p, "#endif") != NULL) return(EndifType); else if (substr(p, "#include") != NULL) return(IncludeType); else return(CommentType); } if (MacroDefn(p)) return(MacroDefnType); return(OtherType); } MacroDefn(line) char *line; /* Assumed to be pointing to first non-white-space character in the line. */ { char *p = line; while ((*p != '\n') && (*p != '=')) p++; if (*p == '=') { AddMacroDefn(line, p); return(1); } else return(0); } AddMacroDefn(line, eqPtr) char *line; /* Assumes line points to start of macro defn. I.e. leading white space is gone. */ char *eqPtr; /* Pts. to '=' sign. */ { struct MacroRec *recPtr; char *strPtr, *str1Ptr; char *p = eqPtr - 1, *p1 = eqPtr + 1, *p2 = line + strlen(line) - 1; recPtr = (struct MacroRec *) malloc(sizeof(struct MacroRec)); while ((*p == ' ') || (*p == '\t')) p--; strPtr = malloc(p+1 - line + 1); while ((*p1 == ' ') || (*p1 == '\t')) p1++; while ((*p2 == '\n') && (*p2 == ' ') && (*p2 == '\t')) p2--; str1Ptr = malloc(p2+1 - p1 + 1); if ((recPtr == NULL) || (strPtr == NULL) || (str1Ptr == NULL)) { fprintf(stderr, "Ran out of memory!\n"); exit(1); } recPtr->macName = strPtr; recPtr->macDefn = str1Ptr; while (line <= p) *strPtr++ = *line++; *strPtr = '\0'; while (p1 <= p2) *str1Ptr++ = *p1++; *str1Ptr = '\0'; recPtr->next = Macros; Macros = recPtr; } Defined(line) char *line; { char *ptr, *ptr1, buf[MaxLine]; struct MacroRec *p = Macros; strcpy(buf, line); ptr = substr(buf, "#ifdef") + 6; while ((*ptr == ' ') || (*ptr == '\t')) ptr++; ptr1 = ptr; while ((*ptr1 != ' ') && (*ptr1 != '\t') && (*ptr1 != '\n') && (*ptr1 != '\0')) ptr1++; *ptr1 = '\0'; while (p != NULL) { if (strcmp(p->macName, ptr) == 0) return(1); p = p->next; } return(0); } ProcessStatement(line) char *line; { switch (LineType(line)) { case IfdefType: ProcessIfdef(line); break; case ElseType: case EndifType: ErrorExit("Unexpected #else of #endif encountered"); case IncludeType: ProcessInclude(line); break; case BlankType: case CommentType: case MacroDefnType: /* Note: Macro defn. is automatically put in local table. */ default: printf("%s", line); break; } } ProcessIfdef(line) char *line; { int inclFlag, state = 0; inclFlag = Defined(line); line = GetLine(); while (line != NULL) { switch (LineType(line)) { case ElseType: if (state != 0) { ErrorExit("Unexpected #else encountered"); } state = 1; inclFlag = !inclFlag; break; case EndifType: return; default: if (inclFlag) ProcessStatement(line); break; } line = GetLine(); } ErrorExit("Missing #endif"); } ProcessInclude(line) char *line; { char *includeFileName, *ptr; CurrentBf++; if (CurrentBf == MaxBf) { ErrorExit("Include files too deeply"); } /* Find name of include file. */ includeFileName = substr(line, "#include"); includeFileName += 8; while ((*includeFileName == ' ') || (*includeFileName == '\t')) { includeFileName++; } /* Delimit the include file name. */ ptr = includeFileName; while ((*ptr != ' ') && (*ptr != '\t') && (*ptr != '\n')) { ptr++; } *ptr = '\0'; Bf[CurrentBf] = fopen(includeFileName, "r"); if (Bf[CurrentBf] == NULL) { perror( includeFileName ); exit( errno ); } line = GetLine(); while (line != NULL) { ProcessStatement(line); line = GetLine(); } fclose( Bf[CurrentBf] ); CurrentBf--; } char *GetLine() { char *ptr; LineNo++; ptr = fgets(Line, MaxLine, Bf[CurrentBf]); return(ptr); } char *substr(s, t) char *s, *t; { int i, j, k; for (i = 0; s[i] != '\0'; i++) { for (j = i, k = 0; t[k] != '\0' && s[j] == t[k]; j++, k++) ; if (t[k] == '\0') return(&s[i]); } return(NULL); } ErrorExit(s) char *s; { fprintf(stderr, "ERROR on line %d. %s.\n", LineNo, s); unlink(TmpMakeFileName); exit(errno); } 926!Funky!Stuff!