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