]> andersk Git - moira.git/blame - util/imake/imake.c
location change for cpp on post-4.3 systems
[moira.git] / util / imake / imake.c
CommitLineData
4c069d87 1/*
2 *
3 * Copyright 1985, 1986, 1987 by the Massachusetts Institute of Technology
4 *
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.
16 *
17 * $Header$
18 * $Locker$
19 *
20 * Author:
21 * Todd Brunhoff
22 * Tektronix, inc.
23 * While a guest engineer at Project Athena, MIT
24 *
25 * imake: the include-make program.
26 *
27 * Usage: imake [-Idir] [-Ddefine] [-T] [-f imakefile ] [-s] [-v] [make flags]
28 *
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.
32 * Options:
33 * -D define. Same as cpp -D argument.
34 * -I Include directory. Same as cpp -I argument.
35 * -T template. Designate a template other
36 * than Imake.template
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.
41 *
42 * Environment variables:
43 *
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.
49 * Other features:
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.
55 *
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
59 * the imake program.
60 *
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.
81 *
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>
87 */
88#include <stdio.h>
89#include <ctype.h>
90#include <sys/types.h>
91#include <sys/file.h>
92#ifdef SYSV
93#include <fcntl.h>
94#else /* !SYSV */
95#include <sys/wait.h>
96#endif /* !SYSV */
97#include <sys/signal.h>
98#include <sys/stat.h>
99
100#ifdef SYSV
101#define dup2(fd1,fd2) ((fd1 == fd2) ? fd1 : \
102 (close(fd2), fcntl(fd1, F_DUPFD, fd2)))
103#endif /* SYSV */
104#define TRUE 1
105#define FALSE 0
106#define ARGUMENTS 50
107
108#if defined(sun) || defined(hpux)
109#define REDUCED_TO_ASCII_SPACE
110int InRule = FALSE;
111#endif
112
113/*
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.
117 */
118#ifndef REDUCED_TO_ASCII_SPACE
119#define KludgeOutputLine(arg)
120#define KludgeResetRule()
121#endif
122
123typedef u_char boolean;
124
bbc17d4d 125#include <sys/param.h>
126#if BSD > 43
127char *cpp = "/usr/bin/cpp";
128#else
129#ifdef apollo
4c069d87 130char *cpp = "/usr/lib/cpp";
bbc17d4d 131#else
132char *cpp = "/lib/cpp";
133#endif
134#endif
4c069d87 135
136char *tmpMakefile = "/usr/tmp/tmp-make.XXXXXX";
137char *tmpImakefile = "/usr/tmp/tmp-imake.XXXXXX";
138char *make_argv[ ARGUMENTS ] = { "make" };
139char *cpp_argv[ ARGUMENTS ] = {
140 "cpp",
141 "-I.",
142#ifdef unix
143 "-Uunix",
144#endif /* unix */
145#ifdef pegasus
146 "-Dpegasus",
147#endif /* pegasus */
148};
149int make_argindex;
150int cpp_argindex;
151char *make = NULL;
152char *Imakefile = NULL;
153char *Makefile = NULL;
154char *Template = "Imake.template";
155char *program;
156char *FindImakefile();
157char *ReadLine();
158char *CleanCppInput();
159char *strdup();
160
161boolean verbose = FALSE;
162boolean show = FALSE;
163extern int errno;
164extern char *Emalloc();
165extern char *realloc();
166extern char *getenv();
167extern char *mktemp();
168
169main(argc, argv)
170 int argc;
171 char **argv;
172{
173 FILE *tmpfd;
174 char makeMacro[ BUFSIZ ];
175 char makefileMacro[ BUFSIZ ];
176
177 init();
178 SetOpts(argc, argv);
179
180 AddCppArg("-I/usr/lib/local/imake.includes");
181
182 Imakefile = FindImakefile(Imakefile);
183 if (Makefile)
184 tmpMakefile = Makefile;
185 else
186 tmpMakefile = mktemp(strdup(tmpMakefile));
187 AddMakeArg("-f");
188 AddMakeArg( tmpMakefile );
189 sprintf(makeMacro, "MAKE=%s", program);
190 AddMakeArg( makeMacro );
191 sprintf(makefileMacro, "MAKEFILE=%s", Imakefile);
192 AddMakeArg( makefileMacro );
193
194 if ((tmpfd = fopen(tmpMakefile, "w+")) == NULL)
195 LogFatal("Cannot create temporary file %s.", tmpMakefile);
196
197 cppit(Imakefile, Template, tmpfd);
198
199 if (show) {
200 if (Makefile == NULL)
201 showit(tmpfd);
202 } else
203 makeit();
204 wrapup();
205 exit(0);
206}
207
208showit(fd)
209 FILE *fd;
210{
211 char buf[ BUFSIZ ];
212 int red;
213
214 fseek(fd, 0, 0);
215 while ((red = fread(buf, 1, BUFSIZ, fd)) > 0)
216 fwrite(buf, red, 1, stdout);
217 if (red < 0)
218 LogFatal("Cannot write stdout.", "");
219}
220
221wrapup()
222{
223 if (tmpMakefile != Makefile)
224 unlink(tmpMakefile);
225 unlink(tmpImakefile);
226}
227
228catch(sig)
229 int sig;
230{
231 errno = 0;
232 LogFatalI("Signal %d.", sig);
233}
234
235/*
236 * Initialize some variables.
237 */
238init()
239{
240 char *p;
241
242 make_argindex=0;
243 while (make_argv[ make_argindex ] != NULL)
244 make_argindex++;
245 cpp_argindex = 0;
246 while (cpp_argv[ cpp_argindex ] != NULL)
247 cpp_argindex++;
248
249 /*
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.
253 */
254 if (p = getenv("IMAKEINCLUDE")) {
255 if (*p != '-' || *(p+1) != 'I')
256 LogFatal("Environment var IMAKEINCLUDE %s\n",
257 "must begin with -I");
258 AddCppArg(p);
259 for (; *p; p++)
260 if (*p == ' ') {
261 *p++ = '\0';
262 AddCppArg(p);
263 }
264 }
265 if (p = getenv("IMAKECPP"))
266 cpp = p;
267 if (p = getenv("IMAKEMAKE"))
268 make = p;
269
270 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
271 signal(SIGINT, catch);
272}
273
274AddMakeArg(arg)
275 char *arg;
276{
277 errno = 0;
278 if (make_argindex >= ARGUMENTS-1)
279 LogFatal("Out of internal storage.", "");
280 make_argv[ make_argindex++ ] = arg;
281 make_argv[ make_argindex ] = NULL;
282}
283
284AddCppArg(arg)
285 char *arg;
286{
287 errno = 0;
288 if (cpp_argindex >= ARGUMENTS-1)
289 LogFatal("Out of internal storage.", "");
290 cpp_argv[ cpp_argindex++ ] = arg;
291 cpp_argv[ cpp_argindex ] = NULL;
292}
293
294SetOpts(argc, argv)
295 int argc;
296 char **argv;
297{
298 errno = 0;
299 /*
300 * Now gather the arguments for make
301 */
302 program = argv[0];
303 for(argc--, argv++; argc; argc--, argv++) {
304 /*
305 * We intercept these flags.
306 */
307 if (argv[0][0] == '-') {
308 if (argv[0][1] == 'D') {
309 AddCppArg(argv[0]);
310 } else if (argv[0][1] == 'I') {
311 AddCppArg(argv[0]);
312 } else if (argv[0][1] == 'f') {
313 if (argv[0][2])
314 Imakefile = argv[0]+2;
315 else {
316 argc--, argv++;
317 if (! argc)
318 LogFatal("No description arg after -f flag\n", "");
319 Imakefile = argv[0];
320 }
321 } else if (argv[0][1] == 's') {
322 if (argv[0][2])
323 Makefile = argv[0]+2;
324 else if (argc > 1 && argv[1][0] != '-') {
325 argc--, argv++;
326 Makefile = argv[0];
327 }
328 show = TRUE;
329 } else if (argv[0][1] == 'T') {
330 if (argv[0][2])
331 Template = argv[0]+2;
332 else {
333 argc--, argv++;
334 if (! argc)
335 LogFatal("No description arg after -T flag\n", "");
336 Template = argv[0];
337 }
338 } else if (argv[0][1] == 'v') {
339 verbose = TRUE;
340 } else
341 AddMakeArg(argv[0]);
342 } else
343 AddMakeArg(argv[0]);
344 }
345}
346
347char *FindImakefile(Imakefile)
348 char *Imakefile;
349{
350 int fd;
351
352 if (Imakefile) {
353 if ((fd = open(Imakefile, O_RDONLY)) < 0)
354 LogFatal("Cannot open %s.", Imakefile);
355 } else {
356 if ((fd = open("Imakefile", O_RDONLY)) < 0)
357 if ((fd = open("imakefile", O_RDONLY)) < 0)
358 LogFatal("No description file.", "");
359 else
360 Imakefile = "imakefile";
361 else
362 Imakefile = "Imakefile";
363 }
364 close (fd);
365 return(Imakefile);
366}
367
368LogFatalI(s, i)
369 char *s;
370 int i;
371{
372 /*NOSTRICT*/
373 LogFatal(s, (char *)i);
374}
375
376LogFatal(x0,x1)
377 char *x0, *x1;
378{
379 extern char *sys_errlist[];
380 static boolean entered = FALSE;
381
382 if (entered)
383 return;
384 entered = TRUE;
385
386 fprintf(stderr, "%s: ", program);
387 if (errno)
388 fprintf(stderr, "%s: ", sys_errlist[ errno ]);
389 fprintf(stderr, x0,x1);
390 fprintf(stderr, " Stop.\n");
391 wrapup();
392 exit(1);
393}
394
395showargs(argv)
396 char **argv;
397{
398 for (; *argv; argv++)
399 fprintf(stderr, "%s ", *argv);
400 fprintf(stderr, "\n");
401}
402
403cppit(Imakefile, template, outfd)
404 char *Imakefile;
405 char *template;
406 FILE *outfd;
407{
408 FILE *pipeFile;
409 int pid, pipefd[2];
410#ifdef SYSV
411 int status;
412#else /* !SYSV */
413 union wait status;
414#endif /* !SYSV */
415 char *cleanedImakefile;
416
417 /*
418 * Get a pipe.
419 */
420 if (pipe(pipefd) < 0)
421 LogFatal("Cannot make a pipe.", "");
422
423 /*
424 * Fork and exec cpp
425 */
426 pid = vfork();
427 if (pid < 0)
428 LogFatal("Cannot fork.", "");
429 if (pid) { /* parent */
430 close(pipefd[0]);
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",
435 template);
436 fprintf(pipeFile, "#define INCLUDE_IMAKEFILE\t\"%s\"\n",
437 cleanedImakefile);
438 fprintf(pipeFile, "#include IMAKE_TEMPLATE\n");
439 fclose(pipeFile);
440 while (wait(&status) > 0) {
441 errno = 0;
442#ifdef SYSV
443 if ((status >> 8) & 0xff)
444 LogFatalI("Signal %d.", (status >> 8) & 0xff);
445 if (status & 0xff)
446 LogFatalI("Exit code %d.", status & 0xff);
447#else /* !SYSV */
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);
452#endif /* !SYSV */
453 }
454 CleanCppOutput(outfd);
455 } else { /* child... dup and exec cpp */
456 if (verbose)
457 showargs(cpp_argv);
458 dup2(pipefd[0], 0);
459 dup2(fileno(outfd), 1);
460 close(pipefd[1]);
461 execv(cpp, cpp_argv);
462 LogFatal("Cannot exec %s.", cpp);
463 }
464}
465
466makeit()
467{
468 int pid;
469#ifdef SYSV
470 int status;
471#else /* !SYSV */
472 union wait status;
473#endif /* !SYSV */
474
475 /*
476 * Fork and exec make
477 */
478 pid = vfork();
479 if (pid < 0)
480 LogFatal("Cannot fork.", "");
481 if (pid) { /* parent... simply wait */
482 while (wait(&status) > 0) {
483 errno = 0;
484#ifdef SYSV
485 if ((status >> 8) & 0xff)
486 LogFatalI("Signal %d.", (status >> 8) & 0xff);
487 if (status & 0xff)
488 LogFatalI("Exit code %d.", status & 0xff);
489#else /* !SYSV */
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);
494#endif /* !SYSV */
495 }
496 } else { /* child... dup and exec cpp */
497 if (verbose)
498 showargs(make_argv);
499 if (make)
500 execv(make, make_argv);
501 else
502 execvp("make", make_argv);
503 LogFatal("Cannot exec %s.", cpp);
504 }
505}
506
507char *CleanCppInput(Imakefile)
508 char *Imakefile;
509{
510 FILE *outFile = NULL;
511 int infd;
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 */
519 struct stat st;
520
521 /*
522 * grab the entire file.
523 */
524 if ((infd = open(Imakefile, O_RDONLY)) < 0)
525 LogFatal("Cannot open %s for input.", Imakefile);
526 fstat(infd, &st);
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);
530 close(infd);
531 buf[ st.st_size ] = '\0';
532
533 punwritten = pbuf = buf;
534 while (*pbuf) {
535 /* pad make comments for cpp */
536 if (*pbuf == '#' && (pbuf == buf || pbuf[-1] == '\n')) {
537
538 ptoken = pbuf+1;
539 while (*ptoken == ' ' || *ptoken == '\t')
540 ptoken++;
541 pend = ptoken;
542 while (*pend && *pend != ' ' && *pend != '\t' && *pend != '\n')
543 pend++;
544 savec = *pend;
545 *pend = '\0';
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");
558 if (outFile == NULL)
559 LogFatal("Cannot open %s for write.\n",
560 tmpImakefile);
561 }
562 fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
563 fputs("/**/", outFile);
564 punwritten = pbuf;
565 }
566 *pend = savec;
567 }
568 pbuf++;
569 }
570 if (outFile) {
571 fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
572 fclose(outFile); /* also closes the pipe */
573 }
574
575 return(cleanedImakefile);
576}
577
578CleanCppOutput(tmpfd)
579 FILE *tmpfd;
580{
581 char *input;
582 int blankline = 0;
583
584 while(input = ReadLine(tmpfd)) {
585 if (isempty(input)) {
586 if (blankline++)
587 continue;
588 KludgeResetRule();
589 } else {
590 blankline = 0;
591 KludgeOutputLine(&input);
592 fputs(input, tmpfd);
593 }
594 putc('\n', tmpfd);
595 }
596 fflush(tmpfd);
597}
598
599/*
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.
602 */
603isempty(line)
604 char *line;
605{
606 char *pend;
607
608 /*
609 * Check for lines of the form
610 * # n "...
611 * or
612 * # line n "...
613 */
614 if (*line == '#') {
615 pend = line+1;
616 if (*pend == ' ')
617 pend++;
618 if (strncmp(pend, "line ", 5) == 0)
619 pend += 5;
620 if (isdigit(*pend)) {
621 while (isdigit(*pend))
622 pend++;
623 if (*pend++ == ' ' && *pend == '"')
624 return(TRUE);
625 }
626 }
627
628 /*
629 * Find the end of the line and then walk back.
630 */
631 for (pend=line; *pend; pend++) ;
632
633 pend--;
634 while (pend >= line && (*pend == ' ' || *pend == '\t'))
635 pend--;
636 *++pend = '\0';
637 return (*line == '\0');
638}
639
640char *ReadLine(tmpfd)
641 FILE *tmpfd;
642{
643 static boolean initialized = FALSE;
644 static char *buf, *pline, *end;
645 char *p1, *p2;
646
647 if (! initialized) {
648 int total_red;
649 struct stat st;
650
651 /*
652 * Slurp it all up.
653 */
654 fseek(tmpfd, 0, 0);
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;
661 *end = '\0';
662 lseek(fileno(tmpfd), 0, 0);
663 ftruncate(fileno(tmpfd), 0);
664 initialized = TRUE;
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 */
674 }
675
676 for (p1 = pline; p1 < end; p1++) {
677 if (*p1 == '@' && *(p1+1) == '@') { /* soft EOL */
678 *p1++ = '\0';
679 p1++; /* skip over second @ */
680 break;
681 }
682 else if (*p1 == '\n') { /* real EOL */
683 *p1++ = '\0';
684 break;
685 }
686 }
687
688 /*
689 * return NULL at the end of the file.
690 */
691 p2 = (pline == p1 ? NULL : pline);
692 pline = p1;
693 return(p2);
694}
695
696writetmpfile(fd, buf, cnt)
697 FILE *fd;
698 int cnt;
699 char *buf;
700{
701 errno = 0;
702 if (fwrite(buf, cnt, 1, fd) != 1)
703 LogFatal("Cannot write to %s.", tmpMakefile);
704}
705
706char *Emalloc(size)
707 int size;
708{
709 char *p, *malloc();
710
711 if ((p = malloc(size)) == NULL)
712 LogFatalI("Cannot allocate %d bytes\n", size);
713 return(p);
714}
715
716#ifdef REDUCED_TO_ASCII_SPACE
717KludgeOutputLine(pline)
718 char **pline;
719{
720 char *p = *pline;
721
722 switch (*p) {
723 case '#': /*Comment - ignore*/
724 break;
725 case '\t': /*Already tabbed - ignore it*/
726 break;
727 case ' ': /*May need a tab*/
728 default:
729 while (*p) if (*p++ == ':') {
730 if (**pline == ' ')
731 (*pline)++;
732 InRule = TRUE;
733 break;
734 }
735 if (InRule && **pline == ' ')
736 **pline = '\t';
737 break;
738 }
739}
740
741KludgeResetRule()
742{
743 InRule = FALSE;
744}
745#endif /* REDUCED_TO_ASCII_SPACE */
746
747char *strdup(cp)
748 register char *cp;
749{
750 register char *new = Emalloc(strlen(cp) + 1);
751
752 strcpy(new, cp);
753 return new;
754}
This page took 0.61064 seconds and 5 git commands to generate.