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