|
|
1.1 root 1: /*
2: * Create a compile/link manifest for the system kernel based on the
3: * configured devices and the presence of files in the configuration
4: * directories.
5: */
6:
7: /*
8: *-IMPORTS:
9: * <sys/compat.h>
10: * CONST
11: * USE_PROTO
12: * ARGS ()
13: * <stdio.h>
14: * NULL
15: * stdin
16: * stdout
17: * fopen ()
18: * fclose ()
19: * fputs ()
20: * fputc ()
21: * <stdlib.h>
22: * getenv ()
23: * <time.h>
24: * time_t
25: * localtime ()
26: * time ()
27: * <string.h>
28: * strerror ()
29: * strftime ()
30: * "ehand.h"
31: * ehand_t
32: * PUSH_HANDLER ()
33: * POP_HANDLER ()
34: * CHAIN_ERROR ()
35: * throw_error ()
36: * "buildobj.h"
37: * build_t
38: * builder_alloc ()
39: * builder_free ()
40: * build_begin ()
41: * build_add ()
42: * build_end ()
43: * build_release ()
44: * build_error ()
45: * "mdev.h"
46: * MD_ENABLED
47: * mdev_t
48: * miter_t
49: * for_all_mdevices ()
50: * "symbol.h"
51: * symbol_t
52: * "read.h"
53: * READ_EOF
54: * input_t
55: * read_char ()
56: * read_error ()
57: * read_close ()
58: * "mkinput.h"
59: * make_file_input ()
60: */
61:
62: #include <sys/compat.h>
63: #include <stdio.h>
64: #include <stdlib.h>
65: #include <time.h>
66: #include <string.h>
67: #include <errno.h>
68:
69: #include "ehand.h"
70: #include "buildobj.h"
71: #include "mdev.h"
72: #include "symbol.h"
73: #include "read.h"
74: #include "mkinput.h"
75:
76: #include "mkconf.h"
77:
78: /*
79: * For entries that are OS-specific, wrap up the choice in a macro.
80: */
81:
82: #ifdef __MSDOS__
83:
84: #define DOS_OR_UNIX(dos,unix) dos
85:
86: #else
87:
88: #define DOS_OR_UNIX(dos, unix) unix
89:
90: #endif
91:
92:
93: /*
94: * Testing for the existence of a file may seem simple, but there are some
95: * subtleties that can trip the unwary, such as which function to use to test
96: * for the existence of the object?
97: *
98: * + fopen () is in ISO C and checks for (a) existence of the named object,
99: * (b) whether it is readable. It may return a useful value in 'errno'
100: * which can be used to (i) issue diagnostics in the user's local language,
101: * (ii) cope with specific kinds of error gracefully, since support for an
102: * E... code defined in an extension to C library behaviour such as POSIX.1
103: * can be tested for with the #ifdef directive.
104: * - fopen () can be slow.
105: *
106: * + access () can test quickly for readability and existence, is present in
107: * POSIX.1.
108: * - access () isn't in ISO C, and some older Unix systems do things rather
109: * differently than POSIX specifies.
110: *
111: * + fstat () returns all the information that access () does, and more. It
112: * is older and more likely to be present as a local extension to C in non-
113: * POSIX systems. fstat () can distinguish between real files and file-like
114: * objects such as named pipes.
115: * - It is usually possible to fstat () objects that cannot be accessed,
116: * which means extra complexity such as getuid () if restrictive
117: * permissions are set up. Testing for unusual objects such as pipes may
118: * reduce portability to systems which don't possess such objects.
119: *
120: * Additional considerations when using a system that may involve a large
121: * directory tree is whether to walk the tree with chdir () rather than
122: * building large pathnames which require expensive rescanning by the
123: * operating system, and so forth.
124: *
125: * This file uses fopen () and simple macro-substitution in pathnames, since
126: * performance is a lesser concern than portability. As for permissions, the
127: * user of this program should be highly priveleged in order to be able to
128: * specify the contents of the kernel code; the user has to be able to read
129: * the files we scan for in order to compile or link them during the actual
130: * kernel build phase.
131: */
132:
133: #define PATH_MACRO 1
134:
135: struct macro_spec {
136: char ms_macrochar; /* character after '%' sign */
137: CONST char * ms_envname; /* environment variable to override */
138: CONST char * ms_default;
139: CONST char * ms_value; /* Actual value */
140: } macros [] = {
141: { 'p' }, /* built at run-time */
142: { 'P', "CONFPATH", DOS_OR_UNIX (".", ".") },
143: { 'T', "OBJPATH", DOS_OR_UNIX ("obj", "obj") },
144: { 'o', "OBJEXT", DOS_OR_UNIX ("obj", "o") },
145: { 'O', "LIBEXT", DOS_OR_UNIX ("lib", "a") },
146: { 'c', "CEXT", DOS_OR_UNIX ("c", "c") },
147: { 'C', "CPPEXT", DOS_OR_UNIX ("cpp", "cc") }
148: };
149:
150:
151: /*
152: * Since all the entries in the "make_spec" are strings which we want to treat
153: * in a uniform way, we arrange things as an array rather than a structure.
154: */
155:
156: enum {
157: TEST_FILE,
158: LINK_ENTRY,
159: COMPILE_RULES,
160: CLEAN_ENTRY,
161: BEFORE_ENTRY,
162: BEFORE_RULES,
163: AFTER_ENTRY,
164: AFTER_RULES,
165:
166: MAKE_MAX
167: };
168:
169:
170: /*
171: * Special entries for use to mean "*exactly* the same as" some previously
172: * generated entry. This save generating multiple copies of the macroexpanded
173: * text to save memory.
174: */
175:
176: CONST char SAME_AS [MAKE_MAX] [1];
177:
178:
179: /*
180: * Structure of make specifications. The macroexpanded versions are kept
181: * around so that the strings can be emitted in appropriate phases of the
182: * make-file generation.
183: */
184:
185: typedef struct make_spec make_t;
186:
187: struct make_spec {
188: CONST char * m_specs [MAKE_MAX];
189: make_t * m_next;
190: };
191:
192: make_t config_commands [] = {
193: { { "%P/%p/Driver.%O", "%P/%p/Driver.%O ", NULL, NULL,
194: NULL, NULL, NULL, NULL } },
195: { { "%P/%p/Driver.%o", "%P/%p/Driver.%o ", NULL, NULL,
196: NULL, NULL, NULL, NULL } },
197: { { "%P/%p/Space.%c", "%T/%p.%o ",
198: "%2: %1\n\t$(CC) $(CFLAGS) -I%P/%p -I. -o"
199: DOS_OR_UNIX ("", " ") "%2 -c %1\n\n",
200: SAME_AS [LINK_ENTRY],
201: NULL, NULL, NULL, NULL } },
202: { { "%P/%p/Space.%C", "%T/%p.%o",
203: "%2: %1\n\t$(CC) $(CFLAGS) -I%P/%p -o"
204: DOS_OR_UNIX ("", " ") "%2 -c %1\n\n",
205: SAME_AS [LINK_ENTRY],
206: NULL, NULL, NULL, NULL } }
207: };
208:
209: make_t stub_commands [] = {
210: { { "%P/%p/Stub.%o", "%P/%p/Stub.%o ", NULL, NULL,
211: NULL, NULL, NULL, NULL } }
212: };
213:
214: make_t pre_commands [] = {
215: { { "%P/%p/before", NULL, NULL, NULL,
216: "%P/%p/before ", NULL,
217: NULL, NULL } }
218: };
219:
220: make_t post_commands [] = {
221: { { "%P/%p/after", NULL, NULL, NULL,
222: NULL, NULL,
223: "%P/%p/after ", NULL } }
224: };
225:
226: #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (* a))
227:
228:
229: /*
230: * Check the named file for existence.
231: */
232:
233: #if USE_PROTO
234: LOCAL int file_exists (CONST char * name)
235: #else
236: LOCAL int
237: file_exists ARGS ((name))
238: CONST char * name;
239: #endif
240: {
241: FILE * temp;
242:
243: if ((temp = fopen (name, "r")) == NULL)
244: return 0;
245:
246: fclose (temp);
247: return 1;
248: }
249:
250:
251: /*
252: * Set up macros, checking environment variables and selecting defaults. This
253: * function also sets up the 'prefix' macro variable.
254: */
255:
256: #if USE_PROTO
257: LOCAL void (init_macros) (CONST char * prefix)
258: #else
259: LOCAL void
260: init_macros ARGS ((prefix))
261: CONST char * prefix;
262: #endif
263: {
264: int i;
265:
266: for (i = 0 ; i < ARRAY_LENGTH (macros) ; i ++) {
267:
268: if (macros [i].ms_envname == NULL)
269: macros [i].ms_value = prefix;
270:
271: if (macros [i].ms_value == NULL &&
272: (macros [i].ms_value =
273: getenv (macros [i].ms_envname)) == NULL)
274: macros [i].ms_value = macros [i].ms_default;
275: }
276: }
277:
278:
279: /*
280: * Macroexpand a single entry in a make specification.
281: */
282:
283: #if USE_PROTO
284: LOCAL void (expand_line) (make_t * spec, make_t * target, build_t * heap,
285: int line)
286: #else
287: LOCAL void
288: expand_line ARGS ((spec, target, heap, line))
289: make_t * spec;
290: make_t * target;
291: build_t * heap;
292: int line;
293: #endif
294: {
295: int i;
296: CONST char * scan;
297:
298: /*
299: * Check to see if the spec wants to be identical to a
300: * previous entry.
301: */
302:
303: scan = spec->m_specs [line];
304:
305: for (i = 0 ; i < line ; i ++)
306: if (scan == SAME_AS [i]) {
307:
308: target->m_specs [line] = target->m_specs [i];
309: return;
310: }
311:
312: /*
313: * We will need to build a new entry in the heap.
314: */
315:
316: if ((i = build_begin (heap, 0, NULL)) != 0)
317: throw_error ("Cannot begin make line, error %s",
318: build_error (i));
319:
320: for (;;) {
321: CONST char * start;
322: char ch;
323:
324: /*
325: * Begin by moving any constant stuff to the output.
326: */
327:
328: start = scan;
329:
330: while ((ch = * scan ++) != 0 && ch != '%')
331: ; /* DO NOTHING */
332:
333: /*
334: * If we have hit the last character in the source string,
335: * add that into the section we copy to the output, since it
336: * make manipulating the strings simpler :-).
337: */
338:
339: if ((i = scan - start - (ch != 0)) > 0 &&
340: (i == build_add (heap, i, start)) != 0)
341: throw_error ("Unable to add to make macro, error %s",
342: build_error (i));
343:
344: if (ch == 0) {
345: /*
346: * End the macro generation and record the start
347: * address of the generated text in the output record.
348: */
349:
350: if ((target->m_specs [line] =
351: build_end (heap, NULL)) == NULL)
352: throw_error ("Error ending make macro");
353: return;
354: }
355:
356:
357: /*
358: * To get here we must have hit a '%'-sign, so deal with it.
359: *
360: * Our test for the numeric macros assumes that the digits
361: * have consecutive character codes in the executuion
362: * environment.
363: */
364:
365: ch = * scan ++;
366:
367: if (ch == 0)
368: throw_error ("Bad macro specification in expand_line ()");
369: else if (ch > '0' && ch - '1' < line)
370: start = target->m_specs [ch - '1'];
371: else if (ch == '%')
372: start = "%";
373: else {
374: /*
375: * Scan the macro table for a match on 'ch'.
376: */
377:
378: start = NULL;
379:
380: for (i = 0 ; i < ARRAY_LENGTH (macros) ; i ++)
381: if (macros [i].ms_macrochar == ch) {
382:
383: start = macros [i].ms_value;
384: break;
385: }
386: }
387:
388:
389: /*
390: * Copy the macro expansion to the destination.
391: */
392:
393: if (start != NULL &&
394: (i = build_add (heap, strlen (start), start)) != 0)
395: throw_error ("Unable to add to make macro, error %s",
396: build_error (i));
397: }
398: }
399:
400:
401: /*
402: * Macroexpand the make specification template, building the target macros in
403: * a build heap. If the file named by the first line does not exist, then
404: * discard that name and return, otherwise expand the rest of the macros and
405: * return an indication that the caller should record this entry.
406: */
407:
408: #if USE_PROTO
409: LOCAL int (expand_spec) (make_t * spec, make_t * target, build_t * heap)
410: #else
411: LOCAL int
412: expand_spec ARGS ((spec, target, heap))
413: make_t * spec;
414: make_t * target;
415: build_t * heap;
416: #endif
417: {
418: int i;
419:
420: expand_line (spec, target, heap, TEST_FILE);
421:
422: if (! file_exists (target->m_specs [TEST_FILE])) {
423:
424: if (build_release (heap, target->m_specs [TEST_FILE]) != 0)
425: throw_error ("Cannot release macro memory in expand_spec ()");
426:
427: return 0;
428: }
429:
430: for (i = TEST_FILE + 1 ; i < MAKE_MAX ; i ++) {
431:
432: if (spec->m_specs [i] == NULL) {
433:
434: target->m_specs [i] = NULL;
435: continue;
436: }
437:
438: expand_line (spec, target, heap, i);
439: }
440:
441: return 1;
442: }
443:
444:
445: /*
446: * This function builds up part of a makefile specification.
447: */
448:
449: #if USE_PROTO
450: LOCAL make_t * (make_make) (build_t * heap, CONST char * prefix,
451: make_t * specs, int nspecs, make_t * makelist)
452: #else
453: LOCAL make_t *
454: make_make ARGS ((heap, prefix, specs, nspecs, makelist))
455: build_t * heap;
456: CONST char * prefix;
457: make_t * specs;
458: int nspecs;
459: make_t * makelist;
460: #endif
461: {
462: int i;
463: make_t temp;
464: make_t * scan;
465:
466: init_macros (prefix);
467:
468: for (i = 0 ; i < nspecs ; i ++) {
469: /*
470: * Expand the specifications, and then test to see if the file
471: * named by the first part of the expanded specification
472: * exists.
473: */
474:
475: if (expand_spec (specs + i, & temp, heap)) {
476: /*
477: * Add the specification to the list of items to be
478: * included in the makefile. We do a quick scan for
479: * duplicate file-name strings, just for paranoia.
480: */
481:
482: for (scan = makelist ; scan != NULL ;
483: scan = scan->m_next) {
484:
485: if (strcmp (scan->m_specs [TEST_FILE],
486: temp.m_specs [TEST_FILE]) == 0)
487: continue;
488: }
489:
490: if ((scan = (make_t *)
491: build_malloc (heap,
492: sizeof (temp))) == NULL)
493: throw_error ("Out of memory in make_make ()");
494:
495: * scan = temp;
496: scan->m_next = makelist;
497: makelist = scan;
498: }
499: }
500:
501: return makelist;
502: }
503:
504:
505: /*
506: * Structure for emulating local functions in C with the function following.
507: */
508:
509: struct make {
510: build_t * _heap;
511: make_t * _spec;
512: };
513:
514: #if USE_PROTO
515: LOCAL void _write_makefile (struct make * make, mdev_t * mdevp)
516: #else
517: LOCAL void
518: _write_makefile (make, mdevp)
519: struct make * make;
520: mdev_t * mdevp;
521: #endif
522: {
523: CONST char * data = mdevp->md_devname->s_data;
524:
525: make->_spec = mdevp->md_configure == MD_ENABLED ?
526: make_make (make->_heap, data, config_commands,
527: ARRAY_LENGTH (config_commands),
528: make_make (make->_heap, data, post_commands,
529: ARRAY_LENGTH (post_commands),
530: make_make (make->_heap, data,
531: pre_commands,
532: ARRAY_LENGTH (pre_commands),
533: make->_spec))) :
534: make_make (make->_heap, data, stub_commands,
535: ARRAY_LENGTH (stub_commands), make->_spec);
536: }
537:
538:
539: /*
540: * Iterate over all the mdevice entries and run over either the 'config' or
541: * 'stub' make-file specifications.
542: */
543:
544: #if USE_PROTO
545: LOCAL void (write_makefile) (FILE * out, input_t * example, build_t * heap)
546: #else
547: LOCAL void
548: write_makefile ARGS ((out, example, heap))
549: FILE * out;
550: input_t * example;
551: build_t * heap;
552: #endif
553: {
554: int ch;
555: struct make make;
556: char pathbuf [80];
557:
558: make._heap = heap;
559: make._spec = NULL;
560:
561: init_macros (NULL);
562:
563: if (getcwd (pathbuf, sizeof (pathbuf)) == NULL)
564: throw_error ("write_makefile () : cannot save current directory");
565:
566: if (chdir (macros [PATH_MACRO].ms_value) != 0)
567: throw_error ("write_makefile () : cannot change directory, "
568: "OS says %s", strerror (errno));
569:
570: for_all_mdevices ((miter_t) _write_makefile, & make);
571:
572: chdir (pathbuf);
573:
574: /*
575: * Actually generate the output from a template file, expanding '%'-
576: * commands in the template into lists of strings as generated above.
577: */
578:
579: while ((ch = read_char (example)) != READ_EOF) {
580: make_t * scan;
581:
582: switch (ch) {
583:
584: case '%':
585: switch (ch = read_char (example)) {
586:
587: case '%':
588: fputc (ch, out);
589: break;
590:
591: case 'T':
592: ch = TEST_FILE;
593: goto write_list;
594:
595: case 'l':
596: ch = LINK_ENTRY;
597: goto write_list;
598:
599: case 'C':
600: ch = COMPILE_RULES;
601: goto write_list;
602:
603: case 'r':
604: ch = CLEAN_ENTRY;
605: goto write_list;
606:
607: case 'b':
608: ch = BEFORE_ENTRY;
609: goto write_list;
610:
611: case 'B':
612: ch = BEFORE_RULES;
613: goto write_list;
614:
615: case 'a':
616: ch = AFTER_ENTRY;
617: goto write_list;
618:
619: case 'A':
620: ch = AFTER_RULES;
621: write_list:
622: for (scan = make._spec ; scan != NULL ;
623: scan = scan->m_next) {
624:
625: if (scan->m_specs [ch] == NULL)
626: continue;
627:
628: fputs (scan->m_specs [ch], out);
629: }
630:
631: break;
632:
633: default:
634: read_error (example);
635: throw_error ("Bad %%-entry in template file");
636: }
637: break;
638:
639: default:
640: fputc (ch, out);
641: break;
642: }
643: }
644: }
645:
646:
647: /*
648: * Write out a detailed set of compile/link commands which will set up a new
649: * kernel. It searches the appropriate directories where driver object files
650: * are expected to be installed and determines what files should be linked
651: * based on the presence of files with appropriate names.
652: */
653:
654: #if USE_PROTO
655: int (write_link) (CONST char * outname, CONST char * inname)
656: #else
657: int
658: write_link ARGS ((outname, inname))
659: CONST char * outname;
660: CONST char * inname;
661: #endif
662: {
663: time_t gentime;
664: char timebuf [70];
665: char pathbuf [80];
666: FILE * VOLATILE out;
667: FILE * in;
668: input_t * VOLATILE example;
669: ehand_t err;
670: build_t * heap;
671: int closein = 1;
672: int closeout = 1;
673:
674: if ((heap = builder_alloc (256, 1)) == NULL)
675: throw_error ("Unable to allocate temporary heap in write_link ()");
676:
677: if (inname == NULL) {
678: in = stdin;
679: inname = "template";
680: closein = 0;
681: } else if ((in = fopen (inname, "r")) == NULL)
682: throw_error ("Unable to open template file in write_link ()");
683:
684: if (outname == NULL) {
685: out = stdout;
686: closeout = 0;
687: } else if ((out = fopen (outname, "w")) == NULL)
688: throw_error ("Unable to open output file for writing in write_link ()");
689:
690: if ((example = make_filter (in, inname, closein, '#',
691: out, closeout)) == NULL)
692: throw_error ("Out of memory in write_link ()");
693:
694: if (PUSH_HANDLER (err) == 0) {
695: time (& gentime);
696:
697: fprintf (out, "# Kernel makefile\n");
698: fprintf (out, "# This makefile was automatically generated."
699: " Do not hand-modify\n");
700:
701: #ifdef __COHERENT__
702: strncpy (timebuf, asctime (localtime (& gentime)),
703: sizeof (timebuf) - 1);
704: timebuf [sizeof (timebuf) - 1] = 0;
705: #else
706: strftime (timebuf, sizeof (timebuf) - 1, "%x %X %Z",
707: localtime (& gentime));
708: #endif
709:
710: fprintf (out, "# Generated at %s\n", timebuf);
711:
712: write_makefile (out, example, heap);
713:
714: read_close (example);
715: builder_free (heap);
716:
717: } else {
718: read_close (example);
719: builder_free (heap);
720:
721: CHAIN_ERROR (err);
722: }
723:
724: POP_HANDLER (err);
725: return 0;
726: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.