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