3 * Copyright 1985, 1986, 1987 by the Massachusetts Institute of Technology
5 * Permission to use, copy, modify, and distribute this
6 * software and its documentation for any purpose and without
7 * fee is hereby granted, provided that the above copyright
8 * notice appear in all copies and that both that copyright
9 * notice and this permission notice appear in supporting
10 * documentation, and that the name of M.I.T. not be used in
11 * advertising or publicity pertaining to distribution of the
12 * software without specific, written prior permission.
13 * M.I.T. makes no representations about the suitability of
14 * this software for any purpose. It is provided "as is"
15 * without express or implied warranty.
23 * While a guest engineer at Project Athena, MIT
25 * imake: the include-make program.
27 * Usage: imake [-Idir] [-Ddefine] [-T] [-f imakefile ] [-s] [-v] [make flags]
29 * Imake takes a template makefile (Imake.template) and runs cpp on it
30 * producing a temporary makefile in /usr/tmp. It then runs make on
31 * this pre-processed makefile.
33 * -D define. Same as cpp -D argument.
34 * -I Include directory. Same as cpp -I argument.
35 * -T template. Designate a template other
37 * -s show. Show the produced makefile on the standard
38 * output. Make is not run is this case. If a file
39 * argument is provided, the output is placed there.
40 * -v verbose. Show the make command line executed.
42 * Environment variables:
44 * IMAKEINCLUDE Include directory to use in addition to
45 * "." and "/usr/lib/local/imake.include".
46 * IMAKECPP Cpp to use instead of /lib/cpp
47 * IMAKEMAKE make program to use other than what is
48 * found by searching the $PATH variable.
50 * imake reads the entire cpp output into memory and then scans it
51 * for occurences of "@@". If it encounters them, it replaces it with
52 * a newline. It also trims any trailing white space on output lines
53 * (because make gets upset at them). This helps when cpp expands
54 * multi-line macros but you want them to appear on multiple lines.
56 * The macros MAKEFILE and MAKE are provided as macros
57 * to make. MAKEFILE is set to imake's makefile (not the constructed,
58 * preprocessed one) and MAKE is set to argv[0], i.e. the name of
61 * Theory of operation:
62 * 1. Determine the name of the imakefile from the command line (-f)
63 * or from the content of the current directory (Imakefile or imakefile).
64 * Call this <imakefile>. This gets added to the arguments for
65 * make as MAKEFILE=<imakefile>.
66 * 2. Determine the name of the template from the command line (-T)
67 * or the default, Imake.template. Call this <template>
68 * 3. Start up cpp an provide it with three lines of input:
69 * #define IMAKE_TEMPLATE "<template>"
70 * #define INCLUDE_IMAKEFILE "<imakefile>"
71 * #include IMAKE_TEMPLATE
72 * Note that the define for INCLUDE_IMAKEFILE is intended for
73 * use in the template file. This implies that the imake is
74 * useless unless the template file contains at least the line
75 * #include INCLUDE_IMAKEFILE
76 * 4. Gather the output from cpp, and clean it up, expanding @@ to
77 * newlines, stripping trailing white space, cpp control lines,
78 * and extra blank lines. This cleaned output is placed in a
79 * temporary file. Call this <makefile>.
80 * 5. Start up make specifying <makefile> as its input.
82 * The design of the template makefile should therefore be:
83 * <set global macros like CFLAGS, etc.>
84 * <include machine dependent additions>
85 * #include INCLUDE_IMAKEFILE
86 * <add any global targets like 'clean' and long dependencies>
90 #include <sys/types.h>
97 #include <sys/signal.h>
101 #define dup2(fd1,fd2) ((fd1 == fd2) ? fd1 : \
102 (close(fd2), fcntl(fd1, F_DUPFD, fd2)))
108 #if defined(sun) || defined(hpux)
109 #define REDUCED_TO_ASCII_SPACE
114 * Some versions of cpp reduce all tabs in macro expansion to a single
115 * space. In addition, the escaped newline may be replaced with a
116 * space instead of being deleted. Blech.
118 #ifndef REDUCED_TO_ASCII_SPACE
119 #define KludgeOutputLine(arg)
120 #define KludgeResetRule()
123 typedef u_char boolean;
125 #include <sys/param.h>
127 char *cpp = "/usr/bin/cpp";
130 char *cpp = "/usr/lib/cpp";
132 char *cpp = "/lib/cpp";
136 char *tmpMakefile = "/usr/tmp/tmp-make.XXXXXX";
137 char *tmpImakefile = "/usr/tmp/tmp-imake.XXXXXX";
138 char *make_argv[ ARGUMENTS ] = { "make" };
139 char *cpp_argv[ ARGUMENTS ] = {
152 char *Imakefile = NULL;
153 char *Makefile = NULL;
154 char *Template = "Imake.template";
156 char *FindImakefile();
158 char *CleanCppInput();
161 boolean verbose = FALSE;
162 boolean show = FALSE;
164 extern char *Emalloc();
165 extern char *realloc();
166 extern char *getenv();
167 extern char *mktemp();
174 char makeMacro[ BUFSIZ ];
175 char makefileMacro[ BUFSIZ ];
180 AddCppArg("-I/usr/lib/local/imake.includes");
182 Imakefile = FindImakefile(Imakefile);
184 tmpMakefile = Makefile;
186 tmpMakefile = mktemp(strdup(tmpMakefile));
188 AddMakeArg( tmpMakefile );
189 sprintf(makeMacro, "MAKE=%s", program);
190 AddMakeArg( makeMacro );
191 sprintf(makefileMacro, "MAKEFILE=%s", Imakefile);
192 AddMakeArg( makefileMacro );
194 if ((tmpfd = fopen(tmpMakefile, "w+")) == NULL)
195 LogFatal("Cannot create temporary file %s.", tmpMakefile);
197 cppit(Imakefile, Template, tmpfd);
200 if (Makefile == NULL)
215 while ((red = fread(buf, 1, BUFSIZ, fd)) > 0)
216 fwrite(buf, red, 1, stdout);
218 LogFatal("Cannot write stdout.", "");
223 if (tmpMakefile != Makefile)
225 unlink(tmpImakefile);
232 LogFatalI("Signal %d.", sig);
236 * Initialize some variables.
243 while (make_argv[ make_argindex ] != NULL)
246 while (cpp_argv[ cpp_argindex ] != NULL)
250 * See if the standard include directory is different than
251 * the default. Or if cpp is not the default. Or if the make
252 * found by the PATH variable is not the default.
254 if (p = getenv("IMAKEINCLUDE")) {
255 if (*p != '-' || *(p+1) != 'I')
256 LogFatal("Environment var IMAKEINCLUDE %s\n",
257 "must begin with -I");
265 if (p = getenv("IMAKECPP"))
267 if (p = getenv("IMAKEMAKE"))
270 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
271 signal(SIGINT, catch);
278 if (make_argindex >= ARGUMENTS-1)
279 LogFatal("Out of internal storage.", "");
280 make_argv[ make_argindex++ ] = arg;
281 make_argv[ make_argindex ] = NULL;
288 if (cpp_argindex >= ARGUMENTS-1)
289 LogFatal("Out of internal storage.", "");
290 cpp_argv[ cpp_argindex++ ] = arg;
291 cpp_argv[ cpp_argindex ] = NULL;
300 * Now gather the arguments for make
303 for(argc--, argv++; argc; argc--, argv++) {
305 * We intercept these flags.
307 if (argv[0][0] == '-') {
308 if (argv[0][1] == 'D') {
310 } else if (argv[0][1] == 'I') {
312 } else if (argv[0][1] == 'f') {
314 Imakefile = argv[0]+2;
318 LogFatal("No description arg after -f flag\n", "");
321 } else if (argv[0][1] == 's') {
323 Makefile = argv[0]+2;
324 else if (argc > 1 && argv[1][0] != '-') {
329 } else if (argv[0][1] == 'T') {
331 Template = argv[0]+2;
335 LogFatal("No description arg after -T flag\n", "");
338 } else if (argv[0][1] == 'v') {
347 char *FindImakefile(Imakefile)
353 if ((fd = open(Imakefile, O_RDONLY)) < 0)
354 LogFatal("Cannot open %s.", Imakefile);
356 if ((fd = open("Imakefile", O_RDONLY)) < 0)
357 if ((fd = open("imakefile", O_RDONLY)) < 0)
358 LogFatal("No description file.", "");
360 Imakefile = "imakefile";
362 Imakefile = "Imakefile";
373 LogFatal(s, (char *)i);
379 extern char *sys_errlist[];
380 static boolean entered = FALSE;
386 fprintf(stderr, "%s: ", program);
388 fprintf(stderr, "%s: ", sys_errlist[ errno ]);
389 fprintf(stderr, x0,x1);
390 fprintf(stderr, " Stop.\n");
398 for (; *argv; argv++)
399 fprintf(stderr, "%s ", *argv);
400 fprintf(stderr, "\n");
403 cppit(Imakefile, template, outfd)
415 char *cleanedImakefile;
420 if (pipe(pipefd) < 0)
421 LogFatal("Cannot make a pipe.", "");
428 LogFatal("Cannot fork.", "");
429 if (pid) { /* parent */
431 cleanedImakefile = CleanCppInput(Imakefile);
432 if ((pipeFile = fdopen(pipefd[1], "w")) == NULL)
433 LogFatalI("Cannot fdopen fd %d for output.", pipefd[1]);
434 fprintf(pipeFile, "#define IMAKE_TEMPLATE\t\"%s\"\n",
436 fprintf(pipeFile, "#define INCLUDE_IMAKEFILE\t\"%s\"\n",
438 fprintf(pipeFile, "#include IMAKE_TEMPLATE\n");
440 while (wait(&status) > 0) {
443 if ((status >> 8) & 0xff)
444 LogFatalI("Signal %d.", (status >> 8) & 0xff);
446 LogFatalI("Exit code %d.", status & 0xff);
448 if (status.w_termsig)
449 LogFatalI("Signal %d.", status.w_termsig);
450 if (status.w_retcode)
451 LogFatalI("Exit code %d.", status.w_retcode);
454 CleanCppOutput(outfd);
455 } else { /* child... dup and exec cpp */
459 dup2(fileno(outfd), 1);
461 execv(cpp, cpp_argv);
462 LogFatal("Cannot exec %s.", cpp);
480 LogFatal("Cannot fork.", "");
481 if (pid) { /* parent... simply wait */
482 while (wait(&status) > 0) {
485 if ((status >> 8) & 0xff)
486 LogFatalI("Signal %d.", (status >> 8) & 0xff);
488 LogFatalI("Exit code %d.", status & 0xff);
490 if (status.w_termsig)
491 LogFatalI("Signal %d.", status.w_termsig);
492 if (status.w_retcode)
493 LogFatalI("Exit code %d.", status.w_retcode);
496 } else { /* child... dup and exec cpp */
500 execv(make, make_argv);
502 execvp("make", make_argv);
503 LogFatal("Cannot exec %s.", cpp);
507 char *CleanCppInput(Imakefile)
510 FILE *outFile = NULL;
512 char *buf, /* buffer for file content */
513 *pbuf, /* walking pointer to buf */
514 *punwritten, /* pointer to unwritten portion of buf */
515 *cleanedImakefile = Imakefile, /* return value */
516 *ptoken, /* pointer to # token */
517 *pend, /* pointer to end of # token */
518 savec; /* temporary character holder */
522 * grab the entire file.
524 if ((infd = open(Imakefile, O_RDONLY)) < 0)
525 LogFatal("Cannot open %s for input.", Imakefile);
527 buf = Emalloc(st.st_size+1);
528 if (read(infd, buf, st.st_size) != st.st_size)
529 LogFatal("Cannot read all of %s:", Imakefile);
531 buf[ st.st_size ] = '\0';
533 punwritten = pbuf = buf;
535 /* pad make comments for cpp */
536 if (*pbuf == '#' && (pbuf == buf || pbuf[-1] == '\n')) {
539 while (*ptoken == ' ' || *ptoken == '\t')
542 while (*pend && *pend != ' ' && *pend != '\t' && *pend != '\n')
546 if (strcmp(ptoken, "include")
547 && strcmp(ptoken, "define")
548 && strcmp(ptoken, "undef")
549 && strcmp(ptoken, "ifdef")
550 && strcmp(ptoken, "ifndef")
551 && strcmp(ptoken, "else")
552 && strcmp(ptoken, "endif")
553 && strcmp(ptoken, "if")) {
554 if (outFile == NULL) {
555 tmpImakefile = mktemp(strdup(tmpImakefile));
556 cleanedImakefile = tmpImakefile;
557 outFile = fopen(tmpImakefile, "w");
559 LogFatal("Cannot open %s for write.\n",
562 fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
563 fputs("/**/", outFile);
571 fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
572 fclose(outFile); /* also closes the pipe */
575 return(cleanedImakefile);
578 CleanCppOutput(tmpfd)
584 while(input = ReadLine(tmpfd)) {
585 if (isempty(input)) {
591 KludgeOutputLine(&input);
600 * Determine of a line has nothing in it. As a side effect, we trim white
601 * space from the end of the line. Cpp magic cookies are also thrown away.
609 * Check for lines of the form
618 if (strncmp(pend, "line ", 5) == 0)
620 if (isdigit(*pend)) {
621 while (isdigit(*pend))
623 if (*pend++ == ' ' && *pend == '"')
629 * Find the end of the line and then walk back.
631 for (pend=line; *pend; pend++) ;
634 while (pend >= line && (*pend == ' ' || *pend == '\t'))
637 return (*line == '\0');
640 char *ReadLine(tmpfd)
643 static boolean initialized = FALSE;
644 static char *buf, *pline, *end;
655 fstat(fileno(tmpfd), &st);
656 pline = buf = Emalloc(st.st_size+1);
657 total_red = read(fileno(tmpfd), buf, st.st_size);
658 if (total_red != st.st_size)
659 LogFatal("cannot read %s\n", tmpMakefile);
660 end = buf + st.st_size;
662 lseek(fileno(tmpfd), 0, 0);
663 ftruncate(fileno(tmpfd), 0);
665 #ifdef REDUCED_TO_ASCII_SPACE
666 fprintf(tmpfd, "#\n");
667 fprintf(tmpfd, "# Warning: the cpp used on this machine replaces\n");
668 fprintf(tmpfd, "# all newlines and multiple tabs/spaces in a macro\n");
669 fprintf(tmpfd, "# expansion with a single space. Imake tries to\n");
670 fprintf(tmpfd, "# compensate for this, but is not always\n");
671 fprintf(tmpfd, "# successful.\n");
672 fprintf(tmpfd, "#\n");
673 #endif /* REDUCED_TO_ASCII_SPACE */
676 for (p1 = pline; p1 < end; p1++) {
677 if (*p1 == '@' && *(p1+1) == '@') { /* soft EOL */
679 p1++; /* skip over second @ */
682 else if (*p1 == '\n') { /* real EOL */
689 * return NULL at the end of the file.
691 p2 = (pline == p1 ? NULL : pline);
696 writetmpfile(fd, buf, cnt)
702 if (fwrite(buf, cnt, 1, fd) != 1)
703 LogFatal("Cannot write to %s.", tmpMakefile);
711 if ((p = malloc(size)) == NULL)
712 LogFatalI("Cannot allocate %d bytes\n", size);
716 #ifdef REDUCED_TO_ASCII_SPACE
717 KludgeOutputLine(pline)
723 case '#': /*Comment - ignore*/
725 case '\t': /*Already tabbed - ignore it*/
727 case ' ': /*May need a tab*/
729 while (*p) if (*p++ == ':') {
735 if (InRule && **pline == ' ')
745 #endif /* REDUCED_TO_ASCII_SPACE */
750 register char *new = Emalloc(strlen(cp) + 1);