1 /* $NetBSD: crunchgen.c,v 1.68 2006/01/13 16:36:25 tsutsui Exp $ */ 2 /* 3 * Copyright (c) 1994 University of Maryland 4 * All Rights Reserved. 5 * 6 * Permission to use, copy, modify, distribute, and sell this software and its 7 * documentation for any purpose is hereby granted without fee, provided that 8 * the above copyright notice appear in all copies and that both that 9 * copyright notice and this permission notice appear in supporting 10 * documentation, and that the name of U.M. not be used in advertising or 11 * publicity pertaining to distribution of the software without specific, 12 * written prior permission. U.M. makes no representations about the 13 * suitability of this software for any purpose. It is provided "as is" 14 * without express or implied warranty. 15 * 16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M. 18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 * 23 * Author: James da Silva, Systems Design and Analysis Group 24 * Computer Science Department 25 * University of Maryland at College Park 26 */ 27 /* 28 * ======================================================================== 29 * crunchgen.c 30 * 31 * Generates a Makefile and main C file for a crunched executable, 32 * from specs given in a .conf file. 33 */ 34 35 #if HAVE_NBTOOL_CONFIG_H 36 #include "nbtool_config.h" 37 #endif 38 39 #include <sys/cdefs.h> 40 #if !defined(lint) 41 __RCSID("$NetBSD: crunchgen.c,v 1.68 2006/01/13 16:36:25 tsutsui Exp $"); 42 #endif 43 44 #include <stdlib.h> 45 #include <unistd.h> 46 #include <stdio.h> 47 #include <ctype.h> 48 #include <string.h> 49 50 #include <sys/types.h> 51 #include <sys/stat.h> 52 #include <sys/param.h> 53 #include <sys/utsname.h> 54 55 #define CRUNCH_VERSION "20050208" 56 57 #define MAXLINELEN 16384 58 #define MAXFIELDS 2048 59 60 /* internal representation of conf file: */ 61 62 /* simple lists of strings suffice for most parms */ 63 64 typedef struct strlst { 65 struct strlst *next; 66 char *str; 67 } strlst_t; 68 69 /* progs have structure, each field can be set with "special" or calculated */ 70 71 typedef struct prog { 72 struct prog *next; 73 char *name, *ident; 74 char *srcdir, *objdir; 75 strlst_t *objs, *objpaths; 76 strlst_t *links, *keepsymbols; 77 int goterror; 78 } prog_t; 79 80 81 /* global state */ 82 83 strlst_t *srcdirs = NULL; 84 strlst_t *libs = NULL; 85 strlst_t *vars = NULL; 86 prog_t *progs = NULL; 87 88 char line[MAXLINELEN]; 89 90 char confname[MAXPATHLEN], infilename[MAXPATHLEN]; 91 char outmkname[MAXPATHLEN], outcfname[MAXPATHLEN], execfname[MAXPATHLEN]; 92 char cachename[MAXPATHLEN], curfilename[MAXPATHLEN]; 93 char curdir[MAXPATHLEN]; 94 char topdir[MAXPATHLEN]; 95 char libdir[MAXPATHLEN] = "/usr/lib"; 96 char dbg[MAXPATHLEN] = "-Os"; 97 int linenum = -1; 98 int goterror = 0; 99 100 char *pname = "crunchgen"; 101 102 int verbose, readcache, useobjs, oneobj; /* options */ 103 int reading_cache; 104 char *machine; 105 char *makeobjdirprefix; 106 char *makebin; 107 char *makeflags; 108 109 /* general library routines */ 110 111 void status(char *str); 112 void out_of_memory(void); 113 void add_string(strlst_t **listp, char *str); 114 int is_dir(char *pathname); 115 int is_nonempty_file(char *pathname); 116 117 /* helper routines for main() */ 118 119 void usage(void); 120 void parse_conf_file(void); 121 void gen_outputs(void); 122 123 extern char *crunched_skel[]; 124 125 int 126 main(int argc, char **argv) 127 { 128 char *p; 129 int optc; 130 131 if ((makebin = getenv("MAKE")) == NULL) 132 makebin = strdup("make"); 133 134 if ((makeflags = getenv("MAKEFLAGS")) == NULL) 135 makeflags = strdup(""); 136 137 if ((machine = getenv("MACHINE")) == NULL) { 138 static struct utsname utsname; 139 140 if (uname(&utsname) == -1) { 141 perror("uname"); 142 exit(1); 143 } 144 machine = utsname.machine; 145 } 146 makeobjdirprefix = getenv("MAKEOBJDIRPREFIX"); 147 verbose = 1; 148 readcache = 1; 149 useobjs = 0; 150 oneobj = 1; 151 *outmkname = *outcfname = *execfname = '\0'; 152 153 if (argc > 0) 154 pname = argv[0]; 155 156 while ((optc = getopt(argc, argv, "m:c:d:e:foqD:L:Ov:")) != -1) { 157 switch(optc) { 158 case 'f': readcache = 0; break; 159 case 'q': verbose = 0; break; 160 case 'O': oneobj = 0; break; 161 case 'o': useobjs = 1, oneobj = 0; break; 162 163 case 'm': strcpy(outmkname, optarg); break; 164 case 'c': strcpy(outcfname, optarg); break; 165 case 'e': strcpy(execfname, optarg); break; 166 case 'd': strcpy(dbg, optarg); break; 167 168 case 'D': strcpy(topdir, optarg); break; 169 case 'L': strcpy(libdir, optarg); break; 170 case 'v': add_string(&vars, optarg); break; 171 172 case '?': 173 default: usage(); 174 } 175 } 176 177 argc -= optind; 178 argv += optind; 179 180 if (argc != 1) 181 usage(); 182 183 /* 184 * generate filenames 185 */ 186 187 strcpy(infilename, argv[0]); 188 getcwd(curdir, MAXPATHLEN); 189 190 /* confname = `basename infilename .conf` */ 191 192 if ((p = strrchr(infilename, '/')) != NULL) 193 strcpy(confname, p + 1); 194 else 195 strcpy(confname, infilename); 196 if ((p = strrchr(confname, '.')) != NULL && !strcmp(p, ".conf")) 197 *p = '\0'; 198 199 if (!*outmkname) 200 (void)snprintf(outmkname, sizeof(outmkname), "%s.mk", confname); 201 if (!*outcfname) 202 (void)snprintf(outcfname, sizeof(outcfname), "%s.c", confname); 203 if (!*execfname) 204 (void)snprintf(execfname, sizeof(execfname), "%s", confname); 205 206 (void)snprintf(cachename, sizeof(cachename), "%s.cache", confname); 207 208 parse_conf_file(); 209 gen_outputs(); 210 211 exit(goterror); 212 } 213 214 215 void 216 usage(void) 217 { 218 fprintf(stderr, 219 "%s [-fOoq] [-c c-file-name] [-D src-root] [-d build-options]\n" 220 "\t [-e exec-file-name] [-L lib-dir] [-m makefile-name]\n" 221 "\t [-v var-spec] conf-file\n", pname); 222 exit(1); 223 } 224 225 226 /* 227 * ======================================================================== 228 * parse_conf_file subsystem 229 * 230 */ 231 232 /* helper routines for parse_conf_file */ 233 234 void parse_one_file(char *filename); 235 void parse_line(char *line, int *fc, char **fv, int nf); 236 void add_srcdirs(int argc, char **argv); 237 void add_progs(int argc, char **argv); 238 void add_link(int argc, char **argv); 239 void add_libs(int argc, char **argv); 240 void add_special(int argc, char **argv); 241 242 prog_t *find_prog(char *str); 243 void add_prog(char *progname); 244 245 246 void 247 parse_conf_file(void) 248 { 249 if (!is_nonempty_file(infilename)) { 250 fprintf(stderr, "%s: fatal: input file \"%s\" not found.\n", 251 pname, infilename); 252 exit(1); 253 } 254 parse_one_file(infilename); 255 if (readcache && is_nonempty_file(cachename)) { 256 reading_cache = 1; 257 parse_one_file(cachename); 258 } 259 } 260 261 262 void 263 parse_one_file(char *filename) 264 { 265 char *fieldv[MAXFIELDS]; 266 int fieldc; 267 void (*f)(int c, char **v); 268 FILE *cf; 269 270 (void)snprintf(line, sizeof(line), "reading %s", filename); 271 status(line); 272 strcpy(curfilename, filename); 273 274 if ((cf = fopen(curfilename, "r")) == NULL) { 275 perror(curfilename); 276 goterror = 1; 277 return; 278 } 279 280 linenum = 0; 281 while (fgets(line, MAXLINELEN, cf) != NULL) { 282 linenum++; 283 parse_line(line, &fieldc, fieldv, MAXFIELDS); 284 if (fieldc < 1) 285 continue; 286 if (!strcmp(fieldv[0], "srcdirs")) f = add_srcdirs; 287 else if (!strcmp(fieldv[0], "progs")) f = add_progs; 288 else if (!strcmp(fieldv[0], "ln")) f = add_link; 289 else if (!strcmp(fieldv[0], "libs")) f = add_libs; 290 else if (!strcmp(fieldv[0], "special")) f = add_special; 291 else { 292 fprintf(stderr, "%s:%d: skipping unknown command `%s'.\n", 293 curfilename, linenum, fieldv[0]); 294 goterror = 1; 295 continue; 296 } 297 if (fieldc < 2) { 298 fprintf(stderr, 299 "%s:%d: %s command needs at least 1 argument, skipping.\n", 300 curfilename, linenum, fieldv[0]); 301 goterror = 1; 302 continue; 303 } 304 f(fieldc, fieldv); 305 } 306 307 if (ferror(cf)) { 308 perror(curfilename); 309 goterror = 1; 310 } 311 fclose(cf); 312 } 313 314 315 void 316 parse_line(char *line, int *fc, char **fv, int nf) 317 { 318 char *p; 319 320 p = line; 321 *fc = 0; 322 for (;;) { 323 while (isspace((unsigned char)*p)) 324 p++; 325 if (*p == '\0' || *p == '#') 326 break; 327 328 if (*fc < nf) 329 fv[(*fc)++] = p; 330 while (*p && !isspace((unsigned char)*p) && *p != '#') 331 p++; 332 if (*p == '\0' || *p == '#') 333 break; 334 *p++ = '\0'; 335 } 336 if (*p) 337 *p = '\0'; /* needed for '#' case */ 338 } 339 340 341 void 342 add_srcdirs(int argc, char **argv) 343 { 344 int i; 345 char tmppath[MAXPATHLEN]; 346 347 for (i = 1; i < argc; i++) { 348 if (argv[i][0] == '/') 349 strcpy(tmppath, argv[i]); 350 else { 351 if (topdir[0] == '\0') 352 strcpy(tmppath, curdir); 353 else 354 strcpy(tmppath, topdir); 355 strcat(tmppath, "/"); 356 strcat(tmppath, argv[i]); 357 } 358 if (is_dir(tmppath)) 359 add_string(&srcdirs, tmppath); 360 else { 361 fprintf(stderr, "%s:%d: `%s' is not a directory, skipping it.\n", 362 curfilename, linenum, tmppath); 363 goterror = 1; 364 } 365 } 366 } 367 368 369 void 370 add_progs(int argc, char **argv) 371 { 372 int i; 373 374 for (i = 1; i < argc; i++) 375 add_prog(argv[i]); 376 } 377 378 379 void 380 add_prog(char *progname) 381 { 382 prog_t *p1, *p2; 383 384 /* add to end, but be smart about dups */ 385 386 for (p1 = NULL, p2 = progs; p2 != NULL; p1 = p2, p2 = p2->next) 387 if (!strcmp(p2->name, progname)) 388 return; 389 390 p2 = malloc(sizeof(prog_t)); 391 if (p2) 392 p2->name = strdup(progname); 393 if (!p2 || !p2->name) 394 out_of_memory(); 395 396 p2->next = NULL; 397 if (p1 == NULL) 398 progs = p2; 399 else 400 p1->next = p2; 401 402 p2->ident = p2->srcdir = p2->objdir = NULL; 403 p2->objs = p2->objpaths = p2->links = p2->keepsymbols = NULL; 404 p2->goterror = 0; 405 } 406 407 408 void 409 add_link(int argc, char **argv) 410 { 411 int i; 412 prog_t *p = find_prog(argv[1]); 413 414 if (p == NULL) { 415 fprintf(stderr, 416 "%s:%d: no prog %s previously declared, skipping link.\n", 417 curfilename, linenum, argv[1]); 418 goterror = 1; 419 return; 420 } 421 for (i = 2; i < argc; i++) 422 add_string(&p->links, argv[i]); 423 } 424 425 426 void 427 add_libs(int argc, char **argv) 428 { 429 int i; 430 431 for (i = 1; i < argc; i++) 432 add_string(&libs, argv[i]); 433 } 434 435 436 void 437 add_special(int argc, char **argv) 438 { 439 int i; 440 prog_t *p = find_prog(argv[1]); 441 442 if (p == NULL) { 443 if (reading_cache) 444 return; 445 fprintf(stderr, 446 "%s:%d: no prog %s previously declared, skipping special.\n", 447 curfilename, linenum, argv[1]); 448 goterror = 1; 449 return; 450 } 451 452 if (!strcmp(argv[2], "ident")) { 453 if (argc != 4) 454 goto argcount; 455 if ((p->ident = strdup(argv[3])) == NULL) 456 out_of_memory(); 457 return; 458 } 459 460 if (!strcmp(argv[2], "srcdir")) { 461 if (argc != 4) 462 goto argcount; 463 if (argv[3][0] == '/') { 464 if ((p->srcdir = strdup(argv[3])) == NULL) 465 out_of_memory(); 466 } else { 467 char tmppath[MAXPATHLEN]; 468 if (topdir[0] == '\0') 469 strcpy(tmppath, curdir); 470 else 471 strcpy(tmppath, topdir); 472 strcat(tmppath, "/"); 473 strcat(tmppath, argv[3]); 474 if ((p->srcdir = strdup(tmppath)) == NULL) 475 out_of_memory(); 476 } 477 return; 478 } 479 480 if (!strcmp(argv[2], "objdir")) { 481 if (argc != 4) 482 goto argcount; 483 if ((p->objdir = strdup(argv[3])) == NULL) 484 out_of_memory(); 485 return; 486 } 487 488 if (!strcmp(argv[2], "objs")) { 489 oneobj = 0; 490 p->objs = NULL; 491 for (i = 3; i < argc; i++) 492 add_string(&p->objs, argv[i]); 493 return; 494 } 495 496 if (!strcmp(argv[2], "objpaths")) { 497 oneobj = 0; 498 p->objpaths = NULL; 499 for (i = 3; i < argc; i++) 500 add_string(&p->objpaths, argv[i]); 501 return; 502 } 503 504 if (!strcmp(argv[2], "keepsymbols")) { 505 p->keepsymbols = NULL; 506 for (i = 3; i < argc; i++) 507 add_string(&p->keepsymbols, argv[i]); 508 return; 509 } 510 511 fprintf(stderr, "%s:%d: bad parameter name `%s', skipping line.\n", 512 curfilename, linenum, argv[2]); 513 goterror = 1; 514 return; 515 516 argcount: 517 fprintf(stderr, 518 "%s:%d: too %s arguments, expected \"special %s %s <string>\".\n", 519 curfilename, linenum, argc < 4? "few" : "many", argv[1], argv[2]); 520 goterror = 1; 521 } 522 523 524 prog_t * 525 find_prog(char *str) 526 { 527 prog_t *p; 528 529 for (p = progs; p != NULL; p = p->next) 530 if (!strcmp(p->name, str)) 531 return p; 532 533 return NULL; 534 } 535 536 537 /* 538 * ======================================================================== 539 * gen_outputs subsystem 540 * 541 */ 542 543 /* helper subroutines */ 544 545 void remove_error_progs(void); 546 void fillin_program(prog_t *p); 547 void gen_specials_cache(void); 548 void gen_output_makefile(void); 549 void gen_output_cfile(void); 550 551 void fillin_program_objs(prog_t *p, char *path); 552 void top_makefile_rules(FILE *outmk); 553 void bottom_makefile_rules(FILE *outmk); 554 void prog_makefile_rules(FILE *outmk, prog_t *p); 555 void output_strlst(FILE *outf, strlst_t *lst); 556 char *genident(char *str); 557 char *dir_search(char *progname); 558 559 560 void 561 gen_outputs(void) 562 { 563 prog_t *p; 564 565 for (p = progs; p != NULL; p = p->next) 566 fillin_program(p); 567 568 remove_error_progs(); 569 gen_specials_cache(); 570 gen_output_cfile(); 571 gen_output_makefile(); 572 status(""); 573 fprintf(stderr, 574 "Run \"make -f %s objs exe\" to build crunched binary.\n", 575 outmkname); 576 } 577 578 579 void 580 fillin_program(prog_t *p) 581 { 582 char path[MAXPATHLEN]; 583 char *srcparent; 584 strlst_t *s; 585 586 (void)snprintf(line, sizeof(line), "filling in parms for %s", p->name); 587 status(line); 588 589 if (!p->ident) 590 p->ident = genident(p->name); 591 if (!p->srcdir) { 592 srcparent = dir_search(p->name); 593 if (srcparent) { 594 (void)snprintf(path, sizeof(path), "%s/%s", srcparent, p->name); 595 if (is_dir(path)) { 596 if (path[0] == '/') { 597 if ((p->srcdir = strdup(path)) == NULL) 598 out_of_memory(); 599 } else { 600 char tmppath[MAXPATHLEN]; 601 if (topdir[0] == '\0') 602 strcpy(tmppath, curdir); 603 else 604 strcpy(tmppath, topdir); 605 strcat(tmppath, "/"); 606 strcat(tmppath, path); 607 if ((p->srcdir = strdup(tmppath)) == NULL) 608 out_of_memory(); 609 } 610 } 611 } 612 } 613 614 if (!p->srcdir && verbose) 615 fprintf(stderr, "%s: %s: warning: could not find source directory.\n", 616 infilename, p->name); 617 618 if (!p->objdir && p->srcdir && useobjs) { 619 if (makeobjdirprefix) { 620 (void)snprintf(path, sizeof(path), "%s/%s", makeobjdirprefix, p->srcdir); 621 if (is_dir(path)) 622 p->objdir = strdup(path); 623 } 624 if (!p->objdir) { 625 (void)snprintf(path, sizeof(path), "%s/obj.%s", p->srcdir, machine); 626 if (is_dir(path)) 627 p->objdir = strdup(path); 628 } 629 if (!p->objdir) { 630 (void)snprintf(path, sizeof(path), "%s/obj", p->srcdir); 631 if (is_dir(path)) 632 p->objdir = strdup(path); 633 } 634 if (!p->objdir) { 635 p->objdir = p->srcdir; 636 } 637 } 638 639 if (oneobj) 640 return; 641 642 if (p->srcdir) 643 (void)snprintf(path, sizeof(path), "%s/Makefile", p->srcdir); 644 if (!p->objs && p->srcdir && is_nonempty_file(path)) 645 fillin_program_objs(p, p->srcdir); 646 647 if (!p->objpaths && p->objs) { 648 char *objdir; 649 if (p->objdir && useobjs) 650 objdir = p->objdir; 651 else 652 objdir = p->ident; 653 for (s = p->objs; s != NULL; s = s->next) { 654 (void)snprintf(line, sizeof(line), "%s/%s", objdir, s->str); 655 add_string(&p->objpaths, line); 656 } 657 } 658 659 if (!p->objs && verbose) 660 fprintf(stderr, "%s: %s: warning: could not find any .o files.\n", 661 infilename, p->name); 662 663 if (!p->objpaths) { 664 fprintf(stderr, 665 "%s: %s: error: no objpaths specified or calculated.\n", 666 infilename, p->name); 667 p->goterror = goterror = 1; 668 } 669 } 670 671 void 672 fillin_program_objs(prog_t *p, char *dirpath) 673 { 674 char *obj, *cp; 675 int rc; 676 int fd; 677 FILE *f; 678 char tempfname[MAXPATHLEN]; 679 680 /* discover the objs from the srcdir Makefile */ 681 682 (void)snprintf(tempfname, sizeof(tempfname), "/tmp/%sXXXXXX", confname); 683 if ((fd = mkstemp(tempfname)) < 0) { 684 perror(tempfname); 685 exit(1); 686 } 687 688 if ((f = fdopen(fd, "w")) == NULL) { 689 perror(tempfname); 690 goterror = 1; 691 return; 692 } 693 694 fprintf(f, ".include \"${.CURDIR}/Makefile\"\n"); 695 fprintf(f, ".if defined(PROG)\n"); 696 fprintf(f, "OBJS?= ${PROG}.o\n"); 697 fprintf(f, ".endif\n"); 698 fprintf(f, "crunchgen_objs:\n\t@echo 'OBJS= '${OBJS}\n"); 699 fclose(f); 700 701 (void)snprintf(line, sizeof(line), 702 "cd %s && %s -B -f %s %s CRUNCHEDPROG=1 crunchgen_objs 2>&1", dirpath, 703 makebin, tempfname, makeflags); 704 if ((f = popen(line, "r")) == NULL) { 705 perror("submake pipe"); 706 goterror = 1; 707 unlink(tempfname); 708 return; 709 } 710 711 while (fgets(line, MAXLINELEN, f)) { 712 if (strncmp(line, "OBJS= ", 6)) { 713 if (strcmp(line, 714 "sh: warning: running as root with dot in PATH\n") == 0) 715 continue; 716 fprintf(stderr, "make error: %s", line); 717 goterror = 1; 718 continue; 719 } 720 cp = line + 6; 721 while (isspace((unsigned char)*cp)) 722 cp++; 723 while (*cp) { 724 obj = cp; 725 while (*cp && !isspace((unsigned char)*cp)) 726 cp++; 727 if (*cp) 728 *cp++ = '\0'; 729 add_string(&p->objs, obj); 730 while (isspace((unsigned char)*cp)) 731 cp++; 732 } 733 } 734 if ((rc=pclose(f)) != 0) { 735 fprintf(stderr, "make error: make returned %d\n", rc); 736 goterror = 1; 737 } 738 unlink(tempfname); 739 } 740 741 void 742 remove_error_progs(void) 743 { 744 prog_t *p1, *p2; 745 746 p1 = NULL; p2 = progs; 747 while (p2 != NULL) { 748 if (!p2->goterror) 749 p1 = p2, p2 = p2->next; 750 else { 751 /* delete it from linked list */ 752 fprintf(stderr, "%s: %s: ignoring program because of errors.\n", 753 infilename, p2->name); 754 if (p1) 755 p1->next = p2->next; 756 else 757 progs = p2->next; 758 p2 = p2->next; 759 } 760 } 761 } 762 763 void 764 gen_specials_cache(void) 765 { 766 FILE *cachef; 767 prog_t *p; 768 769 (void)snprintf(line, sizeof(line), "generating %s", cachename); 770 status(line); 771 772 if ((cachef = fopen(cachename, "w")) == NULL) { 773 perror(cachename); 774 goterror = 1; 775 return; 776 } 777 778 fprintf(cachef, "# %s - parm cache generated from %s by crunchgen %s\n\n", 779 cachename, infilename, CRUNCH_VERSION); 780 781 for (p = progs; p != NULL; p = p->next) { 782 fprintf(cachef, "\n"); 783 if (p->srcdir) 784 fprintf(cachef, "special %s srcdir %s\n", p->name, p->srcdir); 785 if (p->objdir && useobjs) 786 fprintf(cachef, "special %s objdir %s\n", p->name, p->objdir); 787 if (p->objs) { 788 fprintf(cachef, "special %s objs", p->name); 789 output_strlst(cachef, p->objs); 790 } 791 if (p->objpaths) { 792 fprintf(cachef, "special %s objpaths", p->name); 793 output_strlst(cachef, p->objpaths); 794 } 795 } 796 fclose(cachef); 797 } 798 799 800 void 801 gen_output_makefile(void) 802 { 803 prog_t *p; 804 FILE *outmk; 805 806 (void)snprintf(line, sizeof(line), "generating %s", outmkname); 807 status(line); 808 809 if ((outmk = fopen(outmkname, "w")) == NULL) { 810 perror(outmkname); 811 goterror = 1; 812 return; 813 } 814 815 fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n", 816 outmkname, infilename, CRUNCH_VERSION); 817 818 top_makefile_rules(outmk); 819 820 for (p = progs; p != NULL; p = p->next) 821 prog_makefile_rules(outmk, p); 822 823 fprintf(outmk, "\n.include <bsd.prog.mk>\n"); 824 fprintf(outmk, "\n# ========\n"); 825 826 bottom_makefile_rules(outmk); 827 828 fclose(outmk); 829 } 830 831 832 void 833 gen_output_cfile(void) 834 { 835 char **cp; 836 FILE *outcf; 837 prog_t *p; 838 strlst_t *s; 839 840 (void)snprintf(line, sizeof(line), "generating %s", outcfname); 841 status(line); 842 843 if ((outcf = fopen(outcfname, "w")) == NULL) { 844 perror(outcfname); 845 goterror = 1; 846 return; 847 } 848 849 fprintf(outcf, 850 "/* %s - generated from %s by crunchgen %s */\n", 851 outcfname, infilename, CRUNCH_VERSION); 852 853 fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname); 854 for (cp = crunched_skel; *cp != NULL; cp++) 855 fprintf(outcf, "%s\n", *cp); 856 857 for (p = progs; p != NULL; p = p->next) 858 fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident); 859 860 fprintf(outcf, "\nstruct stub entry_points[] = {\n"); 861 for (p = progs; p != NULL; p = p->next) { 862 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 863 p->name, p->ident); 864 for (s = p->links; s != NULL; s = s->next) 865 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 866 s->str, p->ident); 867 } 868 869 fprintf(outcf, "\t{ EXECNAME, crunched_main },\n"); 870 fprintf(outcf, "\t{ NULL, NULL }\n};\n"); 871 fclose(outcf); 872 } 873 874 875 char * 876 genident(char *str) 877 { 878 char *n,*s,*d; 879 880 /* 881 * generates a Makefile/C identifier from a program name, mapping '-' to 882 * '_' and ignoring all other non-identifier characters. This leads to 883 * programs named "foo.bar" and "foobar" to map to the same identifier. 884 */ 885 886 if ((n = strdup(str)) == NULL) 887 return NULL; 888 for (d = s = n; *s != '\0'; s++) { 889 if (*s == '-') 890 *d++ = '_'; 891 else 892 if (*s == '_' || isalnum((unsigned char)*s)) 893 *d++ = *s; 894 } 895 *d = '\0'; 896 return n; 897 } 898 899 900 char * 901 dir_search(char *progname) 902 { 903 char path[MAXPATHLEN]; 904 strlst_t *dir; 905 906 for (dir=srcdirs; dir != NULL; dir=dir->next) { 907 (void)snprintf(path, sizeof(path), "%s/%s", dir->str, progname); 908 if (is_dir(path)) 909 return dir->str; 910 } 911 return NULL; 912 } 913 914 915 void 916 top_makefile_rules(FILE *outmk) 917 { 918 prog_t *p; 919 920 fprintf(outmk, "NOMAN=\n\n"); 921 922 fprintf(outmk, "DBG=%s\n", dbg); 923 fprintf(outmk, "MAKE?=make\n"); 924 #ifdef NEW_TOOLCHAIN 925 fprintf(outmk, "OBJCOPY?=objcopy\n"); 926 fprintf(outmk, "NM?=nm\n"); 927 #else 928 fprintf(outmk, "CRUNCHIDE?=crunchide\n"); 929 #endif 930 931 fprintf(outmk, "CRUNCHED_OBJS="); 932 for (p = progs; p != NULL; p = p->next) 933 fprintf(outmk, " %s.cro", p->name); 934 fprintf(outmk, "\n"); 935 fprintf(outmk, "DPADD+= ${CRUNCHED_OBJS}\n"); 936 fprintf(outmk, "LDADD+= ${CRUNCHED_OBJS} "); 937 output_strlst(outmk, libs); 938 fprintf(outmk, "CRUNCHEDOBJSDIRS="); 939 for (p = progs; p != NULL; p = p->next) 940 fprintf(outmk, " %s", p->ident); 941 fprintf(outmk, "\n\n"); 942 943 fprintf(outmk, "SUBMAKE_TARGETS="); 944 for (p = progs; p != NULL; p = p->next) 945 fprintf(outmk, " %s_make", p->ident); 946 fprintf(outmk, "\n\n"); 947 948 fprintf(outmk, "PROG=%s\n\n", execfname); 949 950 fprintf(outmk, "all: ${PROG}.crunched\n"); 951 fprintf(outmk, "${PROG}.crunched: ${SUBMAKE_TARGETS} .WAIT ${PROG}.strip\n"); 952 fprintf(outmk, "${PROG}.strip:\n"); 953 fprintf(outmk, "\t${MAKE} -f ${PROG}.mk ${PROG}\n"); 954 fprintf(outmk, "\t@[ -f ${PROG}.unstripped -a ! ${PROG} -nt ${PROG}.unstripped ] || { \\\n"); 955 fprintf(outmk, "\t\t${_MKSHMSG:Uecho} \" strip \" ${PROG}; \\\n"); 956 fprintf(outmk, "\t\tcp ${PROG} ${PROG}.unstripped && \\\n"); 957 fprintf(outmk, "\t\t${OBJCOPY} -S -R .note -R .ident -R .comment -R .copyright ${PROG} && \\\n"); 958 fprintf(outmk, "\t\ttouch ${PROG}.unstripped; \\\n"); 959 fprintf(outmk, "\t}\n"); 960 fprintf(outmk, "objs: $(SUBMAKE_TARGETS)\n"); 961 fprintf(outmk, "exe: %s\n", execfname); 962 fprintf(outmk, "clean:\n\trm -rf %s *.cro *.cro.syms *.o *_stub.c ${CRUNCHEDOBJSDIRS} ${PROG}.unstripped\n", 963 execfname); 964 } 965 966 void 967 bottom_makefile_rules(FILE *outmk) 968 { 969 fprintf(outmk, "LDSTATIC=-static\n"); 970 } 971 972 973 void 974 prog_makefile_rules(FILE *outmk, prog_t *p) 975 { 976 strlst_t *lst; 977 978 fprintf(outmk, "\n# -------- %s\n\n", p->name); 979 980 fprintf(outmk, "%s_OBJPATHS=", p->ident); 981 #ifndef NEW_TOOLCHAIN 982 fprintf(outmk, " %s_stub.o", p->name); 983 #endif 984 if (p->objs) 985 output_strlst(outmk, p->objpaths); 986 else 987 fprintf(outmk, " %s/%s.ro\n", p->ident, p->name); 988 989 if (p->srcdir && !useobjs) { 990 fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir); 991 if (p->objs) { 992 fprintf(outmk, "%s_OBJS=", p->ident); 993 output_strlst(outmk, p->objs); 994 } 995 fprintf(outmk, "%s:\n\t mkdir %s\n", p->ident, p->ident); 996 fprintf(outmk, "%s_make: %s .PHONY\n", p->ident, p->ident); 997 fprintf(outmk, "\t( cd %s; printf '.PATH: ${%s_SRCDIR}\\n" 998 ".CURDIR:= ${%s_SRCDIR}\\n" 999 ".include \"$${.CURDIR}/Makefile\"\\n", 1000 p->ident, p->ident, p->ident); 1001 for (lst = vars; lst != NULL; lst = lst->next) 1002 fprintf(outmk, "%s\\n", lst->str); 1003 fprintf(outmk, "'\\\n"); 1004 fprintf(outmk, "\t| ${MAKE} -f- CRUNCHEDPROG=1 DBG=\"${DBG}\" depend"); 1005 fprintf(outmk, " )\n"); 1006 fprintf(outmk, "\t( cd %s; printf '.PATH: ${%s_SRCDIR}\\n" 1007 ".CURDIR:= ${%s_SRCDIR}\\n" 1008 ".include \"$${.CURDIR}/Makefile\"\\n", 1009 p->ident, p->ident, p->ident); 1010 for (lst = vars; lst != NULL; lst = lst->next) 1011 fprintf(outmk, "%s\\n", lst->str); 1012 fprintf(outmk, "'\\\n"); 1013 fprintf(outmk, "\t| ${MAKE} -f- CRUNCHEDPROG=1 DBG=\"${DBG}\" "); 1014 if (p->objs) 1015 fprintf(outmk, "${%s_OBJS} ) \n\n", p->ident); 1016 else 1017 fprintf(outmk, "%s.ro ) \n\n", p->name); 1018 } else 1019 fprintf(outmk, "%s_make:\n\t@echo \"** Using existing objs for %s\"\n\n", 1020 p->ident, p->name); 1021 1022 fprintf(outmk, "%s.cro: %s .WAIT ${%s_OBJPATHS}\n", 1023 p->name, p->ident, p->ident); 1024 1025 #ifdef NEW_TOOLCHAIN 1026 if (p->objs) 1027 fprintf(outmk, "\t${LD} -r -o %s/%s.ro $(%s_OBJPATHS)\n", 1028 p->ident, p->name, p->ident); 1029 /* Use one awk command.... */ 1030 fprintf(outmk, "\t${NM} -ng %s/%s.ro | awk '/^ *U / { next };", 1031 p->ident, p->name); 1032 fprintf(outmk, " /^[0-9a-fA-F]+ C/ { next };"); 1033 for (lst = p->keepsymbols; lst != NULL; lst = lst->next) 1034 fprintf(outmk, " / %s$$/ { next };", lst->str); 1035 fprintf(outmk, " / main$$/ { print \"main _crunched_%s_stub\"; next };", 1036 p->name); 1037 /* gdb thinks these are C++ and ignores everthing after the first $$. */ 1038 fprintf(outmk, " { print $$3 \" \" $$3 \"$$$$from$$$$%s\" }' " 1039 "> %s.cro.syms\n", p->name, p->name); 1040 fprintf(outmk, "\t${OBJCOPY} --redefine-syms %s.cro.syms ", p->name); 1041 fprintf(outmk, "%s/%s.ro %s.cro\n", p->ident, p->name, p->name); 1042 #else 1043 fprintf(outmk, "\t${LD} -dc -r -o %s.cro $(%s_OBJPATHS)\n", 1044 p->name, p->ident); 1045 fprintf(outmk, "\t${CRUNCHIDE} -k _crunched_%s_stub ", p->ident); 1046 for (lst = p->keepsymbols; lst != NULL; lst = lst->next) 1047 fprintf(outmk, "-k %s ", lst->str); 1048 fprintf(outmk, "%s.cro\n", p->name); 1049 fprintf(outmk, "%s_stub.c:\n", p->name); 1050 fprintf(outmk, "\techo \"" 1051 "int _crunched_%s_stub(int argc, char **argv, char **envp)" 1052 "{return main(argc,argv,envp);}\" >%s_stub.c\n", 1053 p->ident, p->name); 1054 #endif 1055 } 1056 1057 void 1058 output_strlst(FILE *outf, strlst_t *lst) 1059 { 1060 for (; lst != NULL; lst = lst->next) 1061 fprintf(outf, " %s", lst->str); 1062 fprintf(outf, "\n"); 1063 } 1064 1065 1066 /* 1067 * ======================================================================== 1068 * general library routines 1069 * 1070 */ 1071 1072 void 1073 status(char *str) 1074 { 1075 static int lastlen = 0; 1076 int len, spaces; 1077 1078 if (!verbose) 1079 return; 1080 1081 len = strlen(str); 1082 spaces = lastlen - len; 1083 if (spaces < 1) 1084 spaces = 1; 1085 1086 fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " "); 1087 fflush(stderr); 1088 lastlen = len; 1089 } 1090 1091 1092 void 1093 out_of_memory(void) 1094 { 1095 fprintf(stderr, "%s: %d: out of memory, stopping.\n", infilename, linenum); 1096 exit(1); 1097 } 1098 1099 1100 void 1101 add_string(strlst_t **listp, char *str) 1102 { 1103 strlst_t *p1, *p2; 1104 1105 /* add to end, but be smart about dups */ 1106 1107 for (p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next) 1108 if (!strcmp(p2->str, str)) 1109 return; 1110 1111 p2 = malloc(sizeof(strlst_t)); 1112 if (p2) 1113 p2->str = strdup(str); 1114 if (!p2 || !p2->str) 1115 out_of_memory(); 1116 1117 p2->next = NULL; 1118 if (p1 == NULL) 1119 *listp = p2; 1120 else 1121 p1->next = p2; 1122 } 1123 1124 1125 int 1126 is_dir(char *pathname) 1127 { 1128 struct stat buf; 1129 1130 if (stat(pathname, &buf) == -1) 1131 return 0; 1132 return S_ISDIR(buf.st_mode); 1133 } 1134 1135 int 1136 is_nonempty_file(char *pathname) 1137 { 1138 struct stat buf; 1139 1140 if (stat(pathname, &buf) == -1) 1141 return 0; 1142 1143 return S_ISREG(buf.st_mode) && buf.st_size > 0; 1144 } 1145