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