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