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