|
|
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.