1 /*****************************************************************************\
5 * Add the value of BOOTSTRAPCFLAGS to the cpp_argv table so that it will be *
6 * passed to the template file. *
8 \*****************************************************************************/
14 * Copyright 1985, 1986, 1987 by the Massachusetts Institute of Technology
16 * Permission to use, copy, modify, and distribute this
17 * software and its documentation for any purpose and without
18 * fee is hereby granted, provided that the above copyright
19 * notice appear in all copies and that both that copyright
20 * notice and this permission notice appear in supporting
21 * documentation, and that the name of M.I.T. not be used in
22 * advertising or publicity pertaining to distribution of the
23 * software without specific, written prior permission.
24 * M.I.T. makes no representations about the suitability of
25 * this software for any purpose. It is provided "as is"
26 * without express or implied warranty.
28 * $XConsortium: imake.c,v 1.51 89/12/12 12:37:30 jim Exp $
34 * While a guest engineer at Project Athena, MIT
36 * imake: the include-make program.
38 * Usage: imake [-Idir] [-Ddefine] [-T] [-f imakefile ] [-s] [-e] [-v] [make flags]
40 * Imake takes a template makefile (Imake.tmpl) and runs cpp on it
41 * producing a temporary makefile in /tmp. It then runs make on
42 * this pre-processed makefile.
44 * -D define. Same as cpp -D argument.
45 * -I Include directory. Same as cpp -I argument.
46 * -T template. Designate a template other
48 * -s[F] show. Show the produced makefile on the standard
49 * output. Make is not run is this case. If a file
50 * argument is provided, the output is placed there.
51 * -e[F] execute instead of show; optionally name Makefile F
52 * -v verbose. Show the make command line executed.
54 * Environment variables:
56 * IMAKEINCLUDE Include directory to use in addition to "."
57 * IMAKECPP Cpp to use instead of /lib/cpp
58 * IMAKEMAKE make program to use other than what is
59 * found by searching the $PATH variable.
61 * imake reads the entire cpp output into memory and then scans it
62 * for occurences of "@@". If it encounters them, it replaces it with
63 * a newline. It also trims any trailing white space on output lines
64 * (because make gets upset at them). This helps when cpp expands
65 * multi-line macros but you want them to appear on multiple lines.
67 * The macros MAKEFILE and MAKE are provided as macros
68 * to make. MAKEFILE is set to imake's makefile (not the constructed,
69 * preprocessed one) and MAKE is set to argv[0], i.e. the name of
72 * Theory of operation:
73 * 1. Determine the name of the imakefile from the command line (-f)
74 * or from the content of the current directory (Imakefile or imakefile).
75 * Call this <imakefile>. This gets added to the arguments for
76 * make as MAKEFILE=<imakefile>.
77 * 2. Determine the name of the template from the command line (-T)
78 * or the default, Imake.tmpl. Call this <template>
79 * 3. Start up cpp an provide it with three lines of input:
80 * #define IMAKE_TEMPLATE " <template> "
81 * #define INCLUDE_IMAKEFILE < <imakefile> >
82 * #include IMAKE_TEMPLATE
83 * Note that the define for INCLUDE_IMAKEFILE is intended for
84 * use in the template file. This implies that the imake is
85 * useless unless the template file contains at least the line
86 * #include INCLUDE_IMAKEFILE
87 * 4. Gather the output from cpp, and clean it up, expanding @@ to
88 * newlines, stripping trailing white space, cpp control lines,
89 * and extra blank lines. This cleaned output is placed in a
90 * temporary file. Call this <makefile>.
91 * 5. Start up make specifying <makefile> as its input.
93 * The design of the template makefile should therefore be:
94 * <set global macros like CFLAGS, etc.>
95 * <include machine dependent additions>
96 * #include INCLUDE_IMAKEFILE
97 * <add any global targets like 'clean' and long dependencies>
101 #include <sys/types.h>
103 #ifndef macII /* mac will get the stuff out of file.h */
107 #include <sys/wait.h>
109 #include <sys/file.h>
111 #include <sys/stat.h>
112 #include "imakemdep.h"
118 #ifdef FIXUP_CPP_WHITESPACE
123 * Some versions of cpp reduce all tabs in macro expansion to a single
124 * space. In addition, the escaped newline may be replaced with a
125 * space instead of being deleted. Blech.
127 #ifndef FIXUP_CPP_WHITESPACE
128 #define KludgeOutputLine(arg)
129 #define KludgeResetRule()
132 typedef unsigned char boolean;
135 #define DEFAULT_CPP "/lib/cpp"
138 char *cpp = DEFAULT_CPP;
140 char *tmpMakefile = "/tmp/Imf.XXXXXX";
141 char *tmpImakefile = "/tmp/IIf.XXXXXX";
142 char *make_argv[ ARGUMENTS ] = { "make" };
147 char *Imakefile = NULL;
148 char *Makefile = "Makefile";
149 char *Template = "Imake.tmpl";
151 char *FindImakefile();
153 char *CleanCppInput();
156 boolean verbose = 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 Imakefile = FindImakefile(Imakefile);
177 tmpMakefile = Makefile;
179 tmpMakefile = mktemp(strdup(tmpMakefile));
181 AddMakeArg( tmpMakefile );
182 sprintf(makeMacro, "MAKE=%s", program);
183 AddMakeArg( makeMacro );
184 sprintf(makefileMacro, "MAKEFILE=%s", Imakefile);
185 AddMakeArg( makefileMacro );
187 if ((tmpfd = fopen(tmpMakefile, "w+")) == NULL)
188 LogFatal("Cannot create temporary file %s.", tmpMakefile);
190 cppit(Imakefile, Template, tmpfd, tmpMakefile);
193 if (Makefile == NULL)
208 while ((red = fread(buf, 1, BUFSIZ, fd)) > 0)
209 fwrite(buf, red, 1, stdout);
211 LogFatal("Cannot write stdout.", "");
216 if (tmpMakefile != Makefile)
218 unlink(tmpImakefile);
230 LogFatalI("Signal %d.", sig);
234 * Initialize some variables.
241 while (make_argv[ make_argindex ] != NULL)
244 while (cpp_argv[ cpp_argindex ] != NULL)
248 * See if the standard include directory is different than
249 * the default. Or if cpp is not the default. Or if the make
250 * found by the PATH variable is not the default.
252 if (p = getenv("IMAKEINCLUDE")) {
253 if (*p != '-' || *(p+1) != 'I')
254 LogFatal("Environment var IMAKEINCLUDE %s\n",
255 "must begin with -I");
263 if (p = getenv("IMAKECPP"))
265 if (p = getenv("IMAKEMAKE"))
268 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
269 signal(SIGINT, catch);
276 if (make_argindex >= ARGUMENTS-1)
277 LogFatal("Out of internal storage.", "");
278 make_argv[ make_argindex++ ] = arg;
279 make_argv[ make_argindex ] = NULL;
286 if (cpp_argindex >= ARGUMENTS-1)
287 LogFatal("Out of internal storage.", "");
288 cpp_argv[ cpp_argindex++ ] = arg;
289 cpp_argv[ cpp_argindex ] = NULL;
298 * Now gather the arguments for make
301 for(argc--, argv++; argc; argc--, argv++) {
303 * We intercept these flags.
305 if (argv[0][0] == '-') {
306 if (argv[0][1] == 'D') {
308 } else if (argv[0][1] == 'I') {
310 } else if (argv[0][1] == 'f') {
312 Imakefile = argv[0]+2;
316 LogFatal("No description arg after -f flag\n", "");
319 } else if (argv[0][1] == 's') {
321 Makefile = (argv[0][2] == '-') ? NULL : argv[0]+2;
322 else if (argc > 1 && argv[1][0] != '-') {
327 } else if (argv[0][1] == 'e') {
328 Makefile = (argv[0][2] ? argv[0]+2 : NULL);
330 } else if (argv[0][1] == 'T') {
332 Template = argv[0]+2;
336 LogFatal("No description arg after -T flag\n", "");
339 } else if (argv[0][1] == 'v') {
348 char *FindImakefile(Imakefile)
354 if ((fd = open(Imakefile, O_RDONLY)) < 0)
355 LogFatal("Cannot open %s.", Imakefile);
357 if ((fd = open("Imakefile", O_RDONLY)) < 0)
358 if ((fd = open("imakefile", O_RDONLY)) < 0)
359 LogFatal("No description file.", "");
361 Imakefile = "imakefile";
363 Imakefile = "Imakefile";
374 LogFatal(s, (char *)i);
380 extern char *sys_errlist[];
381 static boolean entered = FALSE;
387 fprintf(stderr, "%s: ", program);
389 fprintf(stderr, "%s: ", sys_errlist[ errno ]);
390 fprintf(stderr, x0,x1);
391 fprintf(stderr, " Stop.\n");
399 for (; *argv; argv++)
400 fprintf(stderr, "%s ", *argv);
401 fprintf(stderr, "\n");
404 cppit(Imakefile, template, outfd, outfname)
417 char *cleanedImakefile;
422 if (pipe(pipefd) < 0)
423 LogFatal("Cannot make a pipe.", "");
430 LogFatal("Cannot fork.", "");
431 if (pid) { /* parent */
433 cleanedImakefile = CleanCppInput(Imakefile);
434 if ((pipeFile = fdopen(pipefd[1], "w")) == NULL)
435 LogFatalI("Cannot fdopen fd %d for output.", pipefd[1]);
436 fprintf(pipeFile, "#define IMAKE_TEMPLATE\t\"%s\"\n",
438 fprintf(pipeFile, "#define INCLUDE_IMAKEFILE\t<%s>\n",
440 fprintf(pipeFile, "#include IMAKE_TEMPLATE\n");
442 while (wait(&status) > 0) {
445 if ((status >> 8) & 0xff)
446 LogFatalI("Signal %d.", (status >> 8) & 0xff);
448 LogFatalI("Exit code %d.", status & 0xff);
450 if (status.w_termsig)
451 LogFatalI("Signal %d.", status.w_termsig);
452 if (status.w_retcode)
453 LogFatalI("Exit code %d.", status.w_retcode);
456 CleanCppOutput(outfd, outfname);
457 } else { /* child... dup and exec cpp */
461 dup2(fileno(outfd), 1);
463 execv(cpp, cpp_argv);
464 LogFatal("Cannot exec %s.", cpp);
482 LogFatal("Cannot fork.", "");
483 if (pid) { /* parent... simply wait */
484 while (wait(&status) > 0) {
487 if ((status >> 8) & 0xff)
488 LogFatalI("Signal %d.", (status >> 8) & 0xff);
490 LogFatalI("Exit code %d.", status & 0xff);
492 if (status.w_termsig)
493 LogFatalI("Signal %d.", status.w_termsig);
494 if (status.w_retcode)
495 LogFatalI("Exit code %d.", status.w_retcode);
498 } else { /* child... dup and exec cpp */
502 execv(make, make_argv);
504 execvp("make", make_argv);
505 LogFatal("Cannot exec %s.", cpp);
509 char *CleanCppInput(Imakefile)
512 FILE *outFile = NULL;
514 char *buf, /* buffer for file content */
515 *pbuf, /* walking pointer to buf */
516 *punwritten, /* pointer to unwritten portion of buf */
517 *cleanedImakefile = Imakefile, /* return value */
518 *ptoken, /* pointer to # token */
519 *pend, /* pointer to end of # token */
520 savec; /* temporary character holder */
524 * grab the entire file.
526 if ((infd = open(Imakefile, O_RDONLY)) < 0)
527 LogFatal("Cannot open %s for input.", Imakefile);
529 buf = Emalloc(st.st_size+1);
530 if (read(infd, buf, st.st_size) != st.st_size)
531 LogFatal("Cannot read all of %s:", Imakefile);
533 buf[ st.st_size ] = '\0';
535 punwritten = pbuf = buf;
537 /* pad make comments for cpp */
538 if (*pbuf == '#' && (pbuf == buf || pbuf[-1] == '\n')) {
541 while (*ptoken == ' ' || *ptoken == '\t')
544 while (*pend && *pend != ' ' && *pend != '\t' && *pend != '\n')
548 if (strcmp(ptoken, "include")
549 && strcmp(ptoken, "define")
550 && strcmp(ptoken, "undef")
551 && strcmp(ptoken, "ifdef")
552 && strcmp(ptoken, "ifndef")
553 && strcmp(ptoken, "else")
554 && strcmp(ptoken, "endif")
555 && strcmp(ptoken, "if")) {
556 if (outFile == NULL) {
557 tmpImakefile = mktemp(strdup(tmpImakefile));
558 cleanedImakefile = tmpImakefile;
559 outFile = fopen(tmpImakefile, "w");
561 LogFatal("Cannot open %s for write.\n",
564 fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
565 fputs("/**/", outFile);
573 fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
574 fclose(outFile); /* also closes the pipe */
577 return(cleanedImakefile);
580 CleanCppOutput(tmpfd, tmpfname)
587 while(input = ReadLine(tmpfd, tmpfname)) {
588 if (isempty(input)) {
594 KludgeOutputLine(&input);
600 #ifdef NFS_STDOUT_BUG
602 * On some systems, NFS seems to leave a large number of nulls at
603 * the end of the file. Ralph Swick says that this kludge makes the
606 ftruncate (fileno(tmpfd), (off_t)ftell(tmpfd));
611 * Determine of a line has nothing in it. As a side effect, we trim white
612 * space from the end of the line. Cpp magic cookies are also thrown away.
620 * Check for lines of the form
629 if (strncmp(pend, "line ", 5) == 0)
631 if (isdigit(*pend)) {
632 while (isdigit(*pend))
634 if (*pend++ == ' ' && *pend == '"')
640 * Find the end of the line and then walk back.
642 for (pend=line; *pend; pend++) ;
645 while (pend >= line && (*pend == ' ' || *pend == '\t'))
648 return (*line == '\0');
652 char *ReadLine(tmpfd, tmpfname)
656 static boolean initialized = FALSE;
657 static char *buf, *pline, *end;
668 fstat(fileno(tmpfd), &st);
669 pline = buf = Emalloc(st.st_size+1);
670 total_red = read(fileno(tmpfd), buf, st.st_size);
671 if (total_red != st.st_size)
672 LogFatal("cannot read %s\n", tmpMakefile);
673 end = buf + st.st_size;
675 lseek(fileno(tmpfd), 0, 0);
677 freopen(tmpfname, "w+", tmpfd);
679 ftruncate(fileno(tmpfd), 0);
682 fprintf (tmpfd, "# Makefile generated by imake - do not edit!\n");
683 fprintf (tmpfd, "# %s\n",
684 "$XConsortium: imake.c,v 1.51 89/12/12 12:37:30 jim Exp $");
686 #ifdef FIXUP_CPP_WHITESPACE
688 static char *cpp_warning[] = {
690 "# The cpp used on this machine replaces all newlines and multiple tabs and",
691 "# spaces in a macro expansion with a single space. Imake tries to compensate",
692 "# for this, but is not always successful.",
697 for (cpp = cpp_warning; *cpp; cpp++) {
698 fprintf (tmpfd, "%s\n", *cpp);
701 #endif /* FIXUP_CPP_WHITESPACE */
704 for (p1 = pline; p1 < end; p1++) {
705 if (*p1 == '@' && *(p1+1) == '@') { /* soft EOL */
707 p1++; /* skip over second @ */
710 else if (*p1 == '\n') { /* real EOL */
717 * return NULL at the end of the file.
719 p2 = (pline == p1 ? NULL : pline);
724 writetmpfile(fd, buf, cnt)
730 if (fwrite(buf, cnt, 1, fd) != 1)
731 LogFatal("Cannot write to %s.", tmpMakefile);
739 if ((p = malloc(size)) == NULL)
740 LogFatalI("Cannot allocate %d bytes\n", size);
744 #ifdef FIXUP_CPP_WHITESPACE
745 KludgeOutputLine(pline)
751 case '#': /*Comment - ignore*/
753 case '\t': /*Already tabbed - ignore it*/
755 case ' ': /*May need a tab*/
757 for (; *p; p++) if (p[0] == ':' &&
758 p > *pline && p[-1] != '\\') {
764 if (InRule && **pline == ' ')
774 #endif /* FIXUP_CPP_WHITESPACE */
779 register char *new = Emalloc(strlen(cp) + 1);