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;
126 char *cpp = "/lib/cpp";
128 char *cpp = "/usr/lib/cpp";
131 char *tmpMakefile = "/usr/tmp/tmp-make.XXXXXX";
132 char *tmpImakefile = "/usr/tmp/tmp-imake.XXXXXX";
133 char *make_argv[ ARGUMENTS ] = { "make" };
134 char *cpp_argv[ ARGUMENTS ] = {
147 char *Imakefile = NULL;
148 char *Makefile = NULL;
149 char *Template = "Imake.template";
151 char *FindImakefile();
153 char *CleanCppInput();
156 boolean verbose = FALSE;
157 boolean show = FALSE;
159 extern char *Emalloc();
160 extern char *realloc();
161 extern char *getenv();
162 extern char *mktemp();
169 char makeMacro[ BUFSIZ ];
170 char makefileMacro[ BUFSIZ ];
175 AddCppArg("-I/usr/lib/local/imake.includes");
177 Imakefile = FindImakefile(Imakefile);
179 tmpMakefile = Makefile;
181 tmpMakefile = mktemp(strdup(tmpMakefile));
183 AddMakeArg( tmpMakefile );
184 sprintf(makeMacro, "MAKE=%s", program);
185 AddMakeArg( makeMacro );
186 sprintf(makefileMacro, "MAKEFILE=%s", Imakefile);
187 AddMakeArg( makefileMacro );
189 if ((tmpfd = fopen(tmpMakefile, "w+")) == NULL)
190 LogFatal("Cannot create temporary file %s.", tmpMakefile);
192 cppit(Imakefile, Template, tmpfd);
195 if (Makefile == NULL)
210 while ((red = fread(buf, 1, BUFSIZ, fd)) > 0)
211 fwrite(buf, red, 1, stdout);
213 LogFatal("Cannot write stdout.", "");
218 if (tmpMakefile != Makefile)
220 unlink(tmpImakefile);
227 LogFatalI("Signal %d.", sig);
231 * Initialize some variables.
238 while (make_argv[ make_argindex ] != NULL)
241 while (cpp_argv[ cpp_argindex ] != NULL)
245 * See if the standard include directory is different than
246 * the default. Or if cpp is not the default. Or if the make
247 * found by the PATH variable is not the default.
249 if (p = getenv("IMAKEINCLUDE")) {
250 if (*p != '-' || *(p+1) != 'I')
251 LogFatal("Environment var IMAKEINCLUDE %s\n",
252 "must begin with -I");
260 if (p = getenv("IMAKECPP"))
262 if (p = getenv("IMAKEMAKE"))
265 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
266 signal(SIGINT, catch);
273 if (make_argindex >= ARGUMENTS-1)
274 LogFatal("Out of internal storage.", "");
275 make_argv[ make_argindex++ ] = arg;
276 make_argv[ make_argindex ] = NULL;
283 if (cpp_argindex >= ARGUMENTS-1)
284 LogFatal("Out of internal storage.", "");
285 cpp_argv[ cpp_argindex++ ] = arg;
286 cpp_argv[ cpp_argindex ] = NULL;
295 * Now gather the arguments for make
298 for(argc--, argv++; argc; argc--, argv++) {
300 * We intercept these flags.
302 if (argv[0][0] == '-') {
303 if (argv[0][1] == 'D') {
305 } else if (argv[0][1] == 'I') {
307 } else if (argv[0][1] == 'f') {
309 Imakefile = argv[0]+2;
313 LogFatal("No description arg after -f flag\n", "");
316 } else if (argv[0][1] == 's') {
318 Makefile = argv[0]+2;
319 else if (argc > 1 && argv[1][0] != '-') {
324 } else if (argv[0][1] == 'T') {
326 Template = argv[0]+2;
330 LogFatal("No description arg after -T flag\n", "");
333 } else if (argv[0][1] == 'v') {
342 char *FindImakefile(Imakefile)
348 if ((fd = open(Imakefile, O_RDONLY)) < 0)
349 LogFatal("Cannot open %s.", Imakefile);
351 if ((fd = open("Imakefile", O_RDONLY)) < 0)
352 if ((fd = open("imakefile", O_RDONLY)) < 0)
353 LogFatal("No description file.", "");
355 Imakefile = "imakefile";
357 Imakefile = "Imakefile";
368 LogFatal(s, (char *)i);
374 extern char *sys_errlist[];
375 static boolean entered = FALSE;
381 fprintf(stderr, "%s: ", program);
383 fprintf(stderr, "%s: ", sys_errlist[ errno ]);
384 fprintf(stderr, x0,x1);
385 fprintf(stderr, " Stop.\n");
393 for (; *argv; argv++)
394 fprintf(stderr, "%s ", *argv);
395 fprintf(stderr, "\n");
398 cppit(Imakefile, template, outfd)
410 char *cleanedImakefile;
415 if (pipe(pipefd) < 0)
416 LogFatal("Cannot make a pipe.", "");
423 LogFatal("Cannot fork.", "");
424 if (pid) { /* parent */
426 cleanedImakefile = CleanCppInput(Imakefile);
427 if ((pipeFile = fdopen(pipefd[1], "w")) == NULL)
428 LogFatalI("Cannot fdopen fd %d for output.", pipefd[1]);
429 fprintf(pipeFile, "#define IMAKE_TEMPLATE\t\"%s\"\n",
431 fprintf(pipeFile, "#define INCLUDE_IMAKEFILE\t\"%s\"\n",
433 fprintf(pipeFile, "#include IMAKE_TEMPLATE\n");
435 while (wait(&status) > 0) {
438 if ((status >> 8) & 0xff)
439 LogFatalI("Signal %d.", (status >> 8) & 0xff);
441 LogFatalI("Exit code %d.", status & 0xff);
443 if (status.w_termsig)
444 LogFatalI("Signal %d.", status.w_termsig);
445 if (status.w_retcode)
446 LogFatalI("Exit code %d.", status.w_retcode);
449 CleanCppOutput(outfd);
450 } else { /* child... dup and exec cpp */
454 dup2(fileno(outfd), 1);
456 execv(cpp, cpp_argv);
457 LogFatal("Cannot exec %s.", cpp);
475 LogFatal("Cannot fork.", "");
476 if (pid) { /* parent... simply wait */
477 while (wait(&status) > 0) {
480 if ((status >> 8) & 0xff)
481 LogFatalI("Signal %d.", (status >> 8) & 0xff);
483 LogFatalI("Exit code %d.", status & 0xff);
485 if (status.w_termsig)
486 LogFatalI("Signal %d.", status.w_termsig);
487 if (status.w_retcode)
488 LogFatalI("Exit code %d.", status.w_retcode);
491 } else { /* child... dup and exec cpp */
495 execv(make, make_argv);
497 execvp("make", make_argv);
498 LogFatal("Cannot exec %s.", cpp);
502 char *CleanCppInput(Imakefile)
505 FILE *outFile = NULL;
507 char *buf, /* buffer for file content */
508 *pbuf, /* walking pointer to buf */
509 *punwritten, /* pointer to unwritten portion of buf */
510 *cleanedImakefile = Imakefile, /* return value */
511 *ptoken, /* pointer to # token */
512 *pend, /* pointer to end of # token */
513 savec; /* temporary character holder */
517 * grab the entire file.
519 if ((infd = open(Imakefile, O_RDONLY)) < 0)
520 LogFatal("Cannot open %s for input.", Imakefile);
522 buf = Emalloc(st.st_size+1);
523 if (read(infd, buf, st.st_size) != st.st_size)
524 LogFatal("Cannot read all of %s:", Imakefile);
526 buf[ st.st_size ] = '\0';
528 punwritten = pbuf = buf;
530 /* pad make comments for cpp */
531 if (*pbuf == '#' && (pbuf == buf || pbuf[-1] == '\n')) {
534 while (*ptoken == ' ' || *ptoken == '\t')
537 while (*pend && *pend != ' ' && *pend != '\t' && *pend != '\n')
541 if (strcmp(ptoken, "include")
542 && strcmp(ptoken, "define")
543 && strcmp(ptoken, "undef")
544 && strcmp(ptoken, "ifdef")
545 && strcmp(ptoken, "ifndef")
546 && strcmp(ptoken, "else")
547 && strcmp(ptoken, "endif")
548 && strcmp(ptoken, "if")) {
549 if (outFile == NULL) {
550 tmpImakefile = mktemp(strdup(tmpImakefile));
551 cleanedImakefile = tmpImakefile;
552 outFile = fopen(tmpImakefile, "w");
554 LogFatal("Cannot open %s for write.\n",
557 fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
558 fputs("/**/", outFile);
566 fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
567 fclose(outFile); /* also closes the pipe */
570 return(cleanedImakefile);
573 CleanCppOutput(tmpfd)
579 while(input = ReadLine(tmpfd)) {
580 if (isempty(input)) {
586 KludgeOutputLine(&input);
595 * Determine of a line has nothing in it. As a side effect, we trim white
596 * space from the end of the line. Cpp magic cookies are also thrown away.
604 * Check for lines of the form
613 if (strncmp(pend, "line ", 5) == 0)
615 if (isdigit(*pend)) {
616 while (isdigit(*pend))
618 if (*pend++ == ' ' && *pend == '"')
624 * Find the end of the line and then walk back.
626 for (pend=line; *pend; pend++) ;
629 while (pend >= line && (*pend == ' ' || *pend == '\t'))
632 return (*line == '\0');
635 char *ReadLine(tmpfd)
638 static boolean initialized = FALSE;
639 static char *buf, *pline, *end;
650 fstat(fileno(tmpfd), &st);
651 pline = buf = Emalloc(st.st_size+1);
652 total_red = read(fileno(tmpfd), buf, st.st_size);
653 if (total_red != st.st_size)
654 LogFatal("cannot read %s\n", tmpMakefile);
655 end = buf + st.st_size;
657 lseek(fileno(tmpfd), 0, 0);
658 ftruncate(fileno(tmpfd), 0);
660 #ifdef REDUCED_TO_ASCII_SPACE
661 fprintf(tmpfd, "#\n");
662 fprintf(tmpfd, "# Warning: the cpp used on this machine replaces\n");
663 fprintf(tmpfd, "# all newlines and multiple tabs/spaces in a macro\n");
664 fprintf(tmpfd, "# expansion with a single space. Imake tries to\n");
665 fprintf(tmpfd, "# compensate for this, but is not always\n");
666 fprintf(tmpfd, "# successful.\n");
667 fprintf(tmpfd, "#\n");
668 #endif /* REDUCED_TO_ASCII_SPACE */
671 for (p1 = pline; p1 < end; p1++) {
672 if (*p1 == '@' && *(p1+1) == '@') { /* soft EOL */
674 p1++; /* skip over second @ */
677 else if (*p1 == '\n') { /* real EOL */
684 * return NULL at the end of the file.
686 p2 = (pline == p1 ? NULL : pline);
691 writetmpfile(fd, buf, cnt)
697 if (fwrite(buf, cnt, 1, fd) != 1)
698 LogFatal("Cannot write to %s.", tmpMakefile);
706 if ((p = malloc(size)) == NULL)
707 LogFatalI("Cannot allocate %d bytes\n", size);
711 #ifdef REDUCED_TO_ASCII_SPACE
712 KludgeOutputLine(pline)
718 case '#': /*Comment - ignore*/
720 case '\t': /*Already tabbed - ignore it*/
722 case ' ': /*May need a tab*/
724 while (*p) if (*p++ == ':') {
730 if (InRule && **pline == ' ')
740 #endif /* REDUCED_TO_ASCII_SPACE */
745 register char *new = Emalloc(strlen(cp) + 1);