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
main(int argc,char ** argv)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
usage(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
parse_conf_file(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
parse_one_file(char * filename)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
parse_line(char * pline,int * fc,char ** fv,int nf)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
add_srcdirs(int argc,char ** argv)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
add_progs(int argc,char ** argv)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
add_prog(char * progname)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
add_link(int argc,char ** argv)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
add_libs(int argc,char ** argv)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
add_libs_so(int argc,char ** argv)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
add_libs_int(int argc,char ** argv)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
add_buildopts(int argc,char ** argv)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
add_linkopts(int argc,char ** argv)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
add_special(int argc,char ** argv)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
find_prog(char * str)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
gen_outputs(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
fillin_program(prog_t * p)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
fillin_program_objs(prog_t * p,char * path)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
remove_error_progs(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
gen_specials_cache(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
gen_output_makefile(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
gen_output_cfile(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
genident(char * str)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
dir_search(char * progname)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
collect_internal_libs(strlst_t ** listp)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
top_makefile_rules(FILE * outmk)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
prog_makefile_rules(FILE * outmk,prog_t * p)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
intlib_makefile_rules(FILE * outmk,char * path)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
output_strlst(FILE * outf,strlst_t * lst)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
status(const char * str)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
out_of_memory(void)1287 out_of_memory(void)
1288 {
1289 err(1, "%s: %d: out of memory, stopping", infilename, linenum);
1290 }
1291
1292
1293 static void
add_string(strlst_t ** listp,char * str,int nodup)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
subtract_strlst(strlst_t ** lista,strlst_t ** listb)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
in_list(strlst_t ** listp,char * str)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
free_list(strlst_t * head)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
is_dir(const char * pathname)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
is_nonempty_file(const char * pathname)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
iseq(const char * a,const char * b)1381 iseq(const char *a, const char *b)
1382 {
1383 return (strcmp(a, b) == 0);
1384 }
1385