v02i044: emake - "pre-processor" for makefiles
rsalz at pineapple.bbn.com
rsalz at pineapple.bbn.com
Sat Feb 6 11:24:10 AEST 1988
Comp.sources.misc: Volume 2, Issue 44
Submitted-By: "Rich $alz" <rsalz at pineapple.bbn.COM>
Archive-Name: emake
Comp.sources.misc: Volume 2, Issue 44
Submitted-By: "Rich $alz" <rsalz at pineapple.bbn.COM>
Archive-Name: emake
[Cross-pollination? ;-) ++bsa]
One easy way to have standard make appear to be more powerful, is to use
m4 or the C preprocessor so you can define parameterized macros for things
like simple programs, and the like. Our project used to do this, and I
notice that the X software uses something called "imake" which does this
too.
We used to use shell scripts, then I wrote this. (Then we rewrote all our
Makefiles so we don't need this at all.) It basically runs cpp over a
file named Dmakefile to create something called MakeAuto, it then invokes
make on that. If MakeAuto is more recent then Dmakefile, you save the cpp
overhead (this is the big problem with imake). A -D option also forces a
rebuild.
This lets you do cute things like put #ifdef's in your Makefile for
different unices. Nice. It doesn't really handle multi-line #define's as
the X imake does, but you could probably slip that in by putting in a
filter pass to translate @@ into \n.
Known to work on BSD derivatives, should take five or so minutes to port
to others (e.g., no sys/file.h, or using cc -E instead of /lib/cpp.)
Hope others find it useful.
/r$
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g
PATH=/bin:/usr/bin: ; export PATH
if test -f 'emake.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'emake.c'\"
else
echo shar: Extracting \"'emake.c'\" \(5585 characters\)
sed "s/^X//" >'emake.c' <<'END_OF_FILE'
X/*
X** EMAKE
X** Run /lib/cpp over Dmakefile (if necessary), then call make.
X** There is a prolog file, the Dmakefile, and the epilog file;
X** see the List variable to set these.
X**
X** This creates a makefile called MakeAuto, so you don't have to
X** spend all that silly time in cpp if the Dmakefile hasn't changed.
X*/
X#include <stdio.h>
X#include <errno.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/file.h>
X
X#ifdef WAIT_UNION
X#include <sys/wait.h>
X#define WAITVALUE(W) ((W).w_retcode)
Xtypedef union wait WAITER;
X#else
X#define WAITVALUE(W) ((W) >> 8)
Xtypedef int WAITER;
X#endif /* WAIT_UNION */
X
X
X/*
X** Handy shorthands.
X*/
X#define STDOUT 1
X#define STDERR 2
X#define WHITE(c) ((c) == ' ' || (c) == '\t')
X#define WRITE(s) (void)write(STDERR, s, sizeof s - 1);
X
X
X/*
X** Datatype and variable to hold the list of CPP directives that we should
X** pass through (make's comment character is '#', which clashes).
X*/
Xtypedef struct {
X char Value[8];
X int Length;
X} ALIST;
X
XALIST Directives[] = {
X { "include", 7 }, { "define", 6 }, { "ifndef", 6 },
X { "ifdef", 5 }, { "undef", 5 }, { "endif", 5 },
X { "else", 4 }, { "line", 4 }, { "if", 2},
X { "", -1 }
X};
X
X/* Other globals. */
Xchar TempInput[] = "MakeIXXXXXX"; /* CPP input file */
Xchar TempOutput[] = "MakeOXXXXXX"; /* CPP output file */
Xchar DMAKEFILE[] = "Dmakefile"; /* Emake's makefile */
Xchar MAKEFILE[] = "MakeAuto"; /* Generated makefile */
Xchar *List[] = { /* Sources for emake */
X "/usr/cronus/clib/dmakedefs", DMAKEFILE, "/usr/cronus/clib/dtargets", NULL
X};
X
X/* Linked in later. */
Xextern int errno;
Xextern char *mktemp();
Xextern char *malloc();
X
X
X/*
X** Print error message, clean up, and die.
X*/
Xvoid
XQuit(s)
X char *s;
X{
X perror(s);
X#ifndef DEBUG
X if ((unlink(TempInput) < 0 && errno != ENOENT)
X || (unlink(TempOutput) < 0 && errno != ENOENT))
X perror("Error in QUIT cleanup");
X#endif /* DEBUG */
X _exit(1);
X}
X
X
X/*
X** Pre-process the input files, building the make control file.
X*/
Xvoid
XPrepare(Cargv)
X char **Cargv;
X{
X register ALIST *D;
X register FILE *F;
X register FILE *In;
X register char *p;
X register int i;
X register int j;
X WAITER W;
X char **Name;
X char buff[BUFSIZ];
X
X /* Create tempfile for CPP input. */
X if ((F = fopen(TempInput, "w")) == NULL)
X Quit(TempInput);
X
X /* Write each input file to the temporary output. */
X for (Name = List; *Name; Name++) {
X if ((In = fopen(*Name, "r")) == NULL)
X Quit(*Name);
X
X /* Read input, eliding #foo lines if foo is not a cpp directive. */
X while (fgets(buff, sizeof buff, In))
X if (buff[0] != '#')
X (void)fputs(buff, F);
X else {
X for (p = &buff[1]; *p && WHITE(*p); p++)
X p++;
X for (i = strlen(p), D = Directives; D->Length >= 0; D++)
X if (i > D->Length
X && strncmp(p, D->Value, D->Length) == 0
X && WHITE(p[D->Length])) {
X (void)fputs(buff, F);
X break;
X }
X }
X
X (void)fclose(In);
X }
X (void)fclose(F);
X
X /* Create a file to hold the cpp output. */
X i = open(TempOutput, O_WRONLY | O_TRUNC | O_CREAT, 0666);
X
X /* Call the pre-processor. */
X if ((j = fork()) == 0) {
X /* I tried to use dup() and dup2(), but they didn't work... */
X if (close(STDOUT) < 0 || dup(i) != STDOUT)
X perror("Error in CPP redirection");
X execv(Cargv[0], Cargv);
X perror(Cargv[0]);
X _exit(1);
X }
X
X /* Wait for it. */
X while (wait(&W) != j)
X ;
X if (WAITVAL(W))
X Quit("CPP failure");
X
X /* Copy cpp output to MAKEFILE, eliding all "#" lines. */
X (void)close(i);
X if ((In = fopen(TempOutput, "r")) == NULL
X || (F = fopen(MAKEFILE, "w")) == NULL)
X Quit("Scanning cpp output");
X (void)fputs("## HANDS OFF THIS FILE--IT WAS AUTOMATICALLY CREATED!!\n", F);
X while (fgets(buff, sizeof buff, In))
X if (buff[0] != '#')
X for (p = buff; *p && *p != '\n'; p++)
X if (!WHITE(*p)) {
X (void)fputs(buff, F);
X break;
X }
X (void)fclose(In);
X (void)fclose(F);
X if (unlink(TempInput) < 0 || unlink(TempOutput) < 0)
X perror("Error in cleaning up temp files");
X}
X
X
Xmain(ac, av)
X int ac;
X register char *av[];
X{
X register char **Margv;
X register char **Cargv;
X register char *p;
X register int Mcount;
X register int Ccount;
X register int Force;
X struct stat Sb1;
X struct stat Sb2;
X
X /* Is it all there? */
X if (stat(DMAKEFILE, &Sb1) < 0)
X Quit("Required file Dmakefile is missing");
X
X /* Is Dmakefile newer than MakeFile? */
X Force = stat(MAKEFILE, &Sb2) < 0 || Sb1.st_mtime >= Sb2.st_mtime;
X
X /* Build argument list stubs. */
X Margv = (char **)malloc((unsigned int)(ac + 4) * sizeof (char *));
X Margv[0] = "make";
X Margv[1] = "-f";
X Margv[2] = MAKEFILE;
X Cargv = (char **)malloc((unsigned int)(ac + 3) * sizeof (char *));
X Cargv[0] = "/lib/cpp";
X Cargv[1] = "-I/usr/cronus/include";
X
X /* Create spool files. */
X (void)mktemp(TempInput);
X (void)mktemp(TempOutput);
X
X /* Scan arg list, moving "-Dxxx" to cpp, all other stuff to make. */
X for (Mcount = 3, Ccount = 2; p = *++av; )
X if (p[0] == '-' && p[1] == 'D') {
X Force++;
X Cargv[Ccount++] = p;
X }
X else
X Margv[Mcount++] = p;
X Cargv[Ccount++] = TempInput;
X Cargv[Ccount] = NULL;
X Margv[Mcount] = NULL;
X
X /* Rebuild MAKEFILE if necessary. */
X if (Force) {
X static char REBUILD[] = "Rebuilding...";
X static char DONE[] = " done\n";
X
X WRITE(REBUILD);
X Prepare(Cargv);
X WRITE(DONE);
X }
X
X /* Now have make do the real work. */
X (void)execvp(Margv[0], Margv);
X Quit(Margv[0]);
X}
END_OF_FILE
if test 5585 -ne `wc -c <'emake.c'`; then
echo shar: \"'emake.c'\" unpacked with wrong size!
fi
# end of 'emake.c'
fi
echo shar: End of shell archive.
exit 0
More information about the Comp.sources.misc
mailing list