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