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