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