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);
271 /* This kludge is here because in AIX cpp doesn't define any
272 * symbols in cpp, they are all defined by cc.
283 if (make_argindex >= ARGUMENTS-1)
284 LogFatal("Out of internal storage.", "");
285 make_argv[ make_argindex++ ] = arg;
286 make_argv[ make_argindex ] = NULL;
293 if (cpp_argindex >= ARGUMENTS-1)
294 LogFatal("Out of internal storage.", "");
295 cpp_argv[ cpp_argindex++ ] = arg;
296 cpp_argv[ cpp_argindex ] = NULL;
305 * Now gather the arguments for make
308 for(argc--, argv++; argc; argc--, argv++) {
310 * We intercept these flags.
312 if (argv[0][0] == '-') {
313 if (argv[0][1] == 'D') {
315 } else if (argv[0][1] == 'I') {
317 } else if (argv[0][1] == 'f') {
319 Imakefile = argv[0]+2;
323 LogFatal("No description arg after -f flag\n", "");
326 } else if (argv[0][1] == 's') {
328 Makefile = (argv[0][2] == '-') ? NULL : argv[0]+2;
329 else if (argc > 1 && argv[1][0] != '-') {
334 } else if (argv[0][1] == 'e') {
335 Makefile = (argv[0][2] ? argv[0]+2 : NULL);
337 } else if (argv[0][1] == 'T') {
339 Template = argv[0]+2;
343 LogFatal("No description arg after -T flag\n", "");
346 } else if (argv[0][1] == 'v') {
355 char *FindImakefile(Imakefile)
361 if ((fd = open(Imakefile, O_RDONLY)) < 0)
362 LogFatal("Cannot open %s.", Imakefile);
364 if ((fd = open("Imakefile", O_RDONLY)) < 0)
365 if ((fd = open("imakefile", O_RDONLY)) < 0)
366 LogFatal("No description file.", "");
368 Imakefile = "imakefile";
370 Imakefile = "Imakefile";
381 LogFatal(s, (char *)i);
387 extern char *sys_errlist[];
388 static boolean entered = FALSE;
394 fprintf(stderr, "%s: ", program);
396 fprintf(stderr, "%s: ", sys_errlist[ errno ]);
397 fprintf(stderr, x0,x1);
398 fprintf(stderr, " Stop.\n");
406 for (; *argv; argv++)
407 fprintf(stderr, "%s ", *argv);
408 fprintf(stderr, "\n");
411 cppit(Imakefile, template, outfd, outfname)
424 char *cleanedImakefile;
429 if (pipe(pipefd) < 0)
430 LogFatal("Cannot make a pipe.", "");
437 LogFatal("Cannot fork.", "");
438 if (pid) { /* parent */
440 cleanedImakefile = CleanCppInput(Imakefile);
441 if ((pipeFile = fdopen(pipefd[1], "w")) == NULL)
442 LogFatalI("Cannot fdopen fd %d for output.", pipefd[1]);
443 fprintf(pipeFile, "#define IMAKE_TEMPLATE\t\"%s\"\n",
445 fprintf(pipeFile, "#define INCLUDE_IMAKEFILE\t<%s>\n",
447 fprintf(pipeFile, "#include IMAKE_TEMPLATE\n");
449 while (wait(&status) > 0) {
452 if ((status >> 8) & 0xff)
453 LogFatalI("Signal %d.", (status >> 8) & 0xff);
455 LogFatalI("Exit code %d.", status & 0xff);
457 if (status.w_termsig)
458 LogFatalI("Signal %d.", status.w_termsig);
459 if (status.w_retcode)
460 LogFatalI("Exit code %d.", status.w_retcode);
463 CleanCppOutput(outfd, outfname);
464 } else { /* child... dup and exec cpp */
468 dup2(fileno(outfd), 1);
470 execv(cpp, cpp_argv);
471 LogFatal("Cannot exec %s.", cpp);
489 LogFatal("Cannot fork.", "");
490 if (pid) { /* parent... simply wait */
491 while (wait(&status) > 0) {
494 if ((status >> 8) & 0xff)
495 LogFatalI("Signal %d.", (status >> 8) & 0xff);
497 LogFatalI("Exit code %d.", status & 0xff);
499 if (status.w_termsig)
500 LogFatalI("Signal %d.", status.w_termsig);
501 if (status.w_retcode)
502 LogFatalI("Exit code %d.", status.w_retcode);
505 } else { /* child... dup and exec cpp */
509 execv(make, make_argv);
511 execvp("make", make_argv);
512 LogFatal("Cannot exec %s.", cpp);
516 char *CleanCppInput(Imakefile)
519 FILE *outFile = NULL;
521 char *buf, /* buffer for file content */
522 *pbuf, /* walking pointer to buf */
523 *punwritten, /* pointer to unwritten portion of buf */
524 *cleanedImakefile = Imakefile, /* return value */
525 *ptoken, /* pointer to # token */
526 *pend, /* pointer to end of # token */
527 savec; /* temporary character holder */
531 * grab the entire file.
533 if ((infd = open(Imakefile, O_RDONLY)) < 0)
534 LogFatal("Cannot open %s for input.", Imakefile);
536 buf = Emalloc(st.st_size+1);
537 if (read(infd, buf, st.st_size) != st.st_size)
538 LogFatal("Cannot read all of %s:", Imakefile);
540 buf[ st.st_size ] = '\0';
542 punwritten = pbuf = buf;
544 /* pad make comments for cpp */
545 if (*pbuf == '#' && (pbuf == buf || pbuf[-1] == '\n')) {
548 while (*ptoken == ' ' || *ptoken == '\t')
551 while (*pend && *pend != ' ' && *pend != '\t' && *pend != '\n')
555 if (strcmp(ptoken, "include")
556 && strcmp(ptoken, "define")
557 && strcmp(ptoken, "undef")
558 && strcmp(ptoken, "ifdef")
559 && strcmp(ptoken, "ifndef")
560 && strcmp(ptoken, "else")
561 && strcmp(ptoken, "endif")
562 && strcmp(ptoken, "if")) {
563 if (outFile == NULL) {
564 tmpImakefile = mktemp(strdup(tmpImakefile));
565 cleanedImakefile = tmpImakefile;
566 outFile = fopen(tmpImakefile, "w");
568 LogFatal("Cannot open %s for write.\n",
571 fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
572 fputs("/**/", outFile);
580 fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
581 fclose(outFile); /* also closes the pipe */
584 return(cleanedImakefile);
587 CleanCppOutput(tmpfd, tmpfname)
594 while(input = ReadLine(tmpfd, tmpfname)) {
595 if (isempty(input)) {
601 KludgeOutputLine(&input);
607 #ifdef NFS_STDOUT_BUG
609 * On some systems, NFS seems to leave a large number of nulls at
610 * the end of the file. Ralph Swick says that this kludge makes the
613 ftruncate (fileno(tmpfd), (off_t)ftell(tmpfd));
618 * Determine of a line has nothing in it. As a side effect, we trim white
619 * space from the end of the line. Cpp magic cookies are also thrown away.
627 * Check for lines of the form
636 if (strncmp(pend, "line ", 5) == 0)
638 if (isdigit(*pend)) {
639 while (isdigit(*pend))
641 if (*pend++ == ' ' && *pend == '"')
647 * Find the end of the line and then walk back.
649 for (pend=line; *pend; pend++) ;
652 while (pend >= line && (*pend == ' ' || *pend == '\t'))
655 return (*line == '\0');
659 char *ReadLine(tmpfd, tmpfname)
663 static boolean initialized = FALSE;
664 static char *buf, *pline, *end;
675 fstat(fileno(tmpfd), &st);
676 pline = buf = Emalloc(st.st_size+1);
677 total_red = read(fileno(tmpfd), buf, st.st_size);
678 if (total_red != st.st_size)
679 LogFatal("cannot read %s\n", tmpMakefile);
680 end = buf + st.st_size;
682 lseek(fileno(tmpfd), 0, 0);
684 freopen(tmpfname, "w+", tmpfd);
686 ftruncate(fileno(tmpfd), 0);
689 fprintf (tmpfd, "# Makefile generated by imake - do not edit!\n");
690 fprintf (tmpfd, "# %s\n",
691 "$XConsortium: imake.c,v 1.51 89/12/12 12:37:30 jim Exp $");
693 #ifdef FIXUP_CPP_WHITESPACE
695 static char *cpp_warning[] = {
697 "# The cpp used on this machine replaces all newlines and multiple tabs and",
698 "# spaces in a macro expansion with a single space. Imake tries to compensate",
699 "# for this, but is not always successful.",
704 for (cpp = cpp_warning; *cpp; cpp++) {
705 fprintf (tmpfd, "%s\n", *cpp);
708 #endif /* FIXUP_CPP_WHITESPACE */
711 for (p1 = pline; p1 < end; p1++) {
712 if (*p1 == '@' && *(p1+1) == '@') { /* soft EOL */
714 p1++; /* skip over second @ */
717 else if (*p1 == '\n') { /* real EOL */
724 * return NULL at the end of the file.
726 p2 = (pline == p1 ? NULL : pline);
731 writetmpfile(fd, buf, cnt)
737 if (fwrite(buf, cnt, 1, fd) != 1)
738 LogFatal("Cannot write to %s.", tmpMakefile);
746 if ((p = malloc(size)) == NULL)
747 LogFatalI("Cannot allocate %d bytes\n", size);
751 #ifdef FIXUP_CPP_WHITESPACE
752 KludgeOutputLine(pline)
758 case '#': /*Comment - ignore*/
760 case '\t': /*Already tabbed - ignore it*/
762 case ' ': /*May need a tab*/
764 for (; *p; p++) if (p[0] == ':' &&
765 p > *pline && p[-1] != '\\') {
771 if (InRule && **pline == ' ')
781 #endif /* FIXUP_CPP_WHITESPACE */
786 register char *new = Emalloc(strlen(cp) + 1);