[comp.sources.misc] v02i044: emake - "pre-processor" for makefiles

rsalz@pineapple.bbn.com (02/06/88)

Comp.sources.misc: Volume 2, Issue 44

Submitted-By: "Rich $alz" <rsalz@pineapple.bbn.COM>

Archive-Name: emake


Comp.sources.misc: Volume 2, Issue 44
Submitted-By: "Rich $alz" <rsalz@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