2 * Copyright (c) 1994 University of Maryland
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the name of U.M. not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission. U.M. makes no representations about the
12 * suitability of this software for any purpose. It is provided "as is"
13 * without express or implied warranty.
15 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
17 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
19 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 * Author: James da Silva, Systems Design and Analysis Group
23 * Computer Science Department
24 * University of Maryland at College Park
27 * ========================================================================
30 * Generates a Makefile and main C file for a crunched executable,
31 * from specs given in a .conf file.
40 #include <sys/types.h>
42 #include <sys/param.h>
44 #define CRUNCH_VERSION "0.2"
46 #define MAXLINELEN 16384
47 #define MAXFIELDS 2048
50 /* internal representation of conf file: */
52 /* simple lists of strings suffice for most parms */
54 typedef struct strlst {
59 /* progs have structure, each field can be set with "special" or calculated */
64 char *srcdir, *objdir;
65 strlst_t *objs, *objpaths;
74 strlst_t *srcdirs = NULL;
75 strlst_t *libs = NULL;
78 char line[MAXLINELEN];
80 char confname[MAXPATHLEN], infilename[MAXPATHLEN];
81 char outmkname[MAXPATHLEN], outcfname[MAXPATHLEN], execfname[MAXPATHLEN];
82 char tempfname[MAXPATHLEN], cachename[MAXPATHLEN], curfilename[MAXPATHLEN];
86 int verbose, readcache; /* options */
91 /* general library routines */
93 void status(char *str);
94 void out_of_memory(void);
95 void add_string(strlst_t **listp, char *str);
96 int is_dir(char *pathname);
97 int is_nonempty_file(char *pathname);
99 /* helper routines for main() */
102 void parse_conf_file(void);
103 void gen_outputs(void);
106 int main(int argc, char **argv)
113 *outmkname = *outcfname = *execfname = '\0';
115 while((optc = getopt(argc, argv, "lm:c:e:fq")) != -1) {
117 case 'f': readcache = 0; break;
118 case 'q': verbose = 0; break;
120 case 'm': strcpy(outmkname, optarg); break;
121 case 'c': strcpy(outcfname, optarg); break;
122 case 'e': strcpy(execfname, optarg); break;
123 case 'l': list_mode++; verbose = 0; break;
133 if(argc != 1) usage();
139 strcpy(infilename, argv[0]);
141 /* confname = `basename infilename .conf` */
143 if((p=strrchr(infilename, '/')) != NULL) strcpy(confname, p+1);
144 else strcpy(confname, infilename);
145 if((p=strrchr(confname, '.')) != NULL && !strcmp(p, ".conf")) *p = '\0';
147 if(!*outmkname) sprintf(outmkname, "%s.mk", confname);
148 if(!*outcfname) sprintf(outcfname, "%s.c", confname);
149 if(!*execfname) sprintf(execfname, "%s", confname);
151 sprintf(cachename, "%s.cache", confname);
152 sprintf(tempfname, ".tmp_%sXXXXXX", confname);
153 if(mktemp(tempfname) == NULL) {
170 fprintf(stderr, "%s\n%s\n",
171 "usage: crunchgen [-fq] [-m <makefile>] [-c <c file>]",
172 " [-e <exec file>] <conffile>");
178 * ========================================================================
179 * parse_conf_file subsystem
183 /* helper routines for parse_conf_file */
185 void parse_one_file(char *filename);
186 void parse_line(char *line, int *fc, char **fv, int nf);
187 void add_srcdirs(int argc, char **argv);
188 void add_progs(int argc, char **argv);
189 void add_link(int argc, char **argv);
190 void add_libs(int argc, char **argv);
191 void add_special(int argc, char **argv);
193 prog_t *find_prog(char *str);
194 void add_prog(char *progname);
197 void parse_conf_file(void)
199 if(!is_nonempty_file(infilename))
200 errx(1, "fatal: input file \"%s\" not found", infilename);
201 parse_one_file(infilename);
202 if(readcache && is_nonempty_file(cachename)) {
204 parse_one_file(cachename);
209 void parse_one_file(char *filename)
211 char *fieldv[MAXFIELDS];
213 void (*f)(int c, char **v);
216 sprintf(line, "reading %s", filename);
218 strcpy(curfilename, filename);
220 if((cf = fopen(curfilename, "r")) == NULL) {
221 warn("%s", curfilename);
227 while(fgets(line, MAXLINELEN, cf) != NULL) {
229 parse_line(line, &fieldc, fieldv, MAXFIELDS);
230 if(fieldc < 1) continue;
231 if(!strcmp(fieldv[0], "srcdirs")) f = add_srcdirs;
232 else if(!strcmp(fieldv[0], "progs")) f = add_progs;
233 else if(!strcmp(fieldv[0], "ln")) f = add_link;
234 else if(!strcmp(fieldv[0], "libs")) f = add_libs;
235 else if(!strcmp(fieldv[0], "special")) f = add_special;
237 warnx("%s:%d: skipping unknown command `%s'",
238 curfilename, linenum, fieldv[0]);
243 warnx("%s:%d: %s command needs at least 1 argument, skipping",
244 curfilename, linenum, fieldv[0]);
252 warn("%s", curfilename);
259 void parse_line(char *line, int *fc, char **fv, int nf)
266 while(isspace(*p)) p++;
267 if(*p == '\0' || *p == '#') break;
269 if(*fc < nf) fv[(*fc)++] = p;
270 while(*p && !isspace(*p) && *p != '#') p++;
271 if(*p == '\0' || *p == '#') break;
274 if(*p) *p = '\0'; /* needed for '#' case */
278 void add_srcdirs(int argc, char **argv)
282 for(i=1;i<argc;i++) {
284 add_string(&srcdirs, argv[i]);
286 warnx("%s:%d: `%s' is not a directory, skipping it",
287 curfilename, linenum, argv[i]);
294 void add_progs(int argc, char **argv)
303 void add_prog(char *progname)
307 /* add to end, but be smart about dups */
309 for(p1 = NULL, p2 = progs; p2 != NULL; p1 = p2, p2 = p2->next)
310 if(!strcmp(p2->name, progname)) return;
312 p2 = malloc(sizeof(prog_t));
314 memset(p2, 0, sizeof(prog_t));
315 p2->name = strdup(progname);
321 if(p1 == NULL) progs = p2;
324 p2->ident = p2->srcdir = p2->objdir = NULL;
325 p2->links = p2->objs = p2->keeplist = NULL;
328 printf("%s\n",progname);
332 void add_link(int argc, char **argv)
335 prog_t *p = find_prog(argv[1]);
338 warnx("%s:%d: no prog %s previously declared, skipping link",
339 curfilename, linenum, argv[1]);
343 for(i=2;i<argc;i++) {
345 printf("%s\n",argv[i]);
346 add_string(&p->links, argv[i]);
351 void add_libs(int argc, char **argv)
356 add_string(&libs, argv[i]);
360 void add_special(int argc, char **argv)
363 prog_t *p = find_prog(argv[1]);
366 if(reading_cache) return;
367 warnx("%s:%d: no prog %s previously declared, skipping special",
368 curfilename, linenum, argv[1]);
373 if(!strcmp(argv[2], "ident")) {
374 if(argc != 4) goto argcount;
375 if((p->ident = strdup(argv[3])) == NULL)
378 else if(!strcmp(argv[2], "srcdir")) {
379 if(argc != 4) goto argcount;
380 if((p->srcdir = strdup(argv[3])) == NULL)
383 else if(!strcmp(argv[2], "objdir")) {
384 if(argc != 4) goto argcount;
385 if((p->objdir = strdup(argv[3])) == NULL)
388 else if(!strcmp(argv[2], "objs")) {
391 add_string(&p->objs, argv[i]);
393 else if(!strcmp(argv[2], "objpaths")) {
396 add_string(&p->objpaths, argv[i]);
398 else if(!strcmp(argv[2], "keep")) {
401 add_string(&p->keeplist, argv[i]);
404 warnx("%s:%d: bad parameter name `%s', skipping line",
405 curfilename, linenum, argv[2]);
412 warnx("%s:%d: too %s arguments, expected \"special %s %s <string>\"",
413 curfilename, linenum, argc < 4? "few" : "many", argv[1], argv[2]);
418 prog_t *find_prog(char *str)
422 for(p = progs; p != NULL; p = p->next)
423 if(!strcmp(p->name, str)) return p;
430 * ========================================================================
431 * gen_outputs subsystem
435 /* helper subroutines */
437 void remove_error_progs(void);
438 void fillin_program(prog_t *p);
439 void gen_specials_cache(void);
440 void gen_output_makefile(void);
441 void gen_output_cfile(void);
443 void fillin_program_objs(prog_t *p, char *path);
444 void top_makefile_rules(FILE *outmk);
445 void prog_makefile_rules(FILE *outmk, prog_t *p);
446 void output_strlst(FILE *outf, strlst_t *lst);
447 char *genident(char *str);
448 char *dir_search(char *progname);
451 void gen_outputs(void)
455 for(p = progs; p != NULL; p = p->next)
458 remove_error_progs();
459 gen_specials_cache();
461 gen_output_makefile();
464 "Run \"make -f %s objs exe\" to build crunched binary.\n",
469 void fillin_program(prog_t *p)
471 char path[MAXPATHLEN];
475 sprintf(line, "filling in parms for %s", p->name);
479 p->ident = genident(p->name);
481 srcparent = dir_search(p->name);
483 sprintf(path, "%s/%s", srcparent, p->name);
485 p->srcdir = strdup(path);
487 if(!p->objdir && p->srcdir) {
490 sprintf(path, "cd %s && echo -n /usr/obj`/bin/pwd`", p->srcdir);
491 p->objdir = p->srcdir;
494 fgets(path,sizeof path, f);
497 p->objdir = strdup(path);
502 if(p->srcdir) sprintf(path, "%s/Makefile", p->srcdir);
503 if(!p->objs && p->srcdir && is_nonempty_file(path))
504 fillin_program_objs(p, path);
506 if(!p->objpaths && p->objdir && p->objs)
507 for(s = p->objs; s != NULL; s = s->next) {
508 sprintf(line, "%s/%s", p->objdir, s->str);
509 add_string(&p->objpaths, line);
512 if(!p->srcdir && verbose)
513 warnx("%s: %s: warning: could not find source directory",
514 infilename, p->name);
515 if(!p->objs && verbose)
516 warnx("%s: %s: warning: could not find any .o files",
517 infilename, p->name);
520 warnx("%s: %s: error: no objpaths specified or calculated",
521 infilename, p->name);
522 p->goterror = goterror = 1;
526 void fillin_program_objs(prog_t *p, char *path)
532 /* discover the objs from the srcdir Makefile */
534 if((f = fopen(tempfname, "w")) == NULL) {
535 warn("%s", tempfname);
540 fprintf(f, ".include \"%s\"\n", path);
541 fprintf(f, ".if defined(PROG) && !defined(OBJS)\n");
542 fprintf(f, "OBJS=${PROG}.o\n");
543 fprintf(f, ".endif\n");
544 fprintf(f, "crunchgen_objs:\n\t@echo 'OBJS= '${OBJS}\n");
547 sprintf(line, "make -f %s crunchgen_objs 2>&1", tempfname);
548 if((f = popen(line, "r")) == NULL) {
549 warn("submake pipe");
554 while(fgets(line, MAXLINELEN, f)) {
555 if(strncmp(line, "OBJS= ", 6)) {
556 warnx("make error: %s", line);
561 while(isspace(*cp)) cp++;
564 while(*cp && !isspace(*cp)) cp++;
565 if(*cp) *cp++ = '\0';
566 add_string(&p->objs, obj);
567 while(isspace(*cp)) cp++;
570 if((rc=pclose(f)) != 0) {
571 warnx("make error: make returned %d", rc);
577 void remove_error_progs(void)
581 p1 = NULL; p2 = progs;
584 p1 = p2, p2 = p2->next;
586 /* delete it from linked list */
587 warnx("%s: %s: ignoring program because of errors",
588 infilename, p2->name);
589 if(p1) p1->next = p2->next;
590 else progs = p2->next;
596 void gen_specials_cache(void)
601 sprintf(line, "generating %s", cachename);
604 if((cachef = fopen(cachename, "w")) == NULL) {
605 warn("%s", cachename);
610 fprintf(cachef, "# %s - parm cache generated from %s by crunchgen %s\n\n",
611 cachename, infilename, CRUNCH_VERSION);
613 for(p = progs; p != NULL; p = p->next) {
614 fprintf(cachef, "\n");
616 fprintf(cachef, "special %s srcdir %s\n", p->name, p->srcdir);
618 fprintf(cachef, "special %s objdir %s\n", p->name, p->objdir);
620 fprintf(cachef, "special %s objs", p->name);
621 output_strlst(cachef, p->objs);
623 fprintf(cachef, "special %s objpaths", p->name);
624 output_strlst(cachef, p->objpaths);
630 void gen_output_makefile(void)
635 sprintf(line, "generating %s", outmkname);
638 if((outmk = fopen(outmkname, "w")) == NULL) {
639 warn("%s", outmkname);
644 fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n",
645 outmkname, infilename, CRUNCH_VERSION);
647 top_makefile_rules(outmk);
649 for(p = progs; p != NULL; p = p->next)
650 prog_makefile_rules(outmk, p);
652 fprintf(outmk, "\n# ========\n");
657 void gen_output_cfile(void)
659 extern char *crunched_skel[];
665 sprintf(line, "generating %s", outcfname);
668 if((outcf = fopen(outcfname, "w")) == NULL) {
669 warn("%s", outcfname);
675 "/* %s - generated from %s by crunchgen %s */\n",
676 outcfname, infilename, CRUNCH_VERSION);
678 fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname);
679 for(cp = crunched_skel; *cp != NULL; cp++)
680 fprintf(outcf, "%s\n", *cp);
682 for(p = progs; p != NULL; p = p->next)
683 fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident);
685 fprintf(outcf, "\nstruct stub entry_points[] = {\n");
686 for(p = progs; p != NULL; p = p->next) {
687 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
689 for(s = p->links; s != NULL; s = s->next)
690 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
694 fprintf(outcf, "\t{ EXECNAME, crunched_main },\n");
695 fprintf(outcf, "\t{ NULL, NULL }\n};\n");
700 char *genident(char *str)
705 * generates a Makefile/C identifier from a program name, mapping '-' to
706 * '_' and ignoring all other non-identifier characters. This leads to
707 * programs named "foo.bar" and "foobar" to map to the same identifier.
710 if((n = strdup(str)) == NULL)
712 for(d = s = n; *s != '\0'; s++) {
713 if(*s == '-') *d++ = '_';
714 else if(*s == '_' || isalnum(*s)) *d++ = *s;
721 char *dir_search(char *progname)
723 char path[MAXPATHLEN];
726 for(dir=srcdirs; dir != NULL; dir=dir->next) {
727 sprintf(path, "%s/%s", dir->str, progname);
728 if(is_dir(path)) return dir->str;
734 void top_makefile_rules(FILE *outmk)
738 fprintf(outmk, "LIBS=");
739 output_strlst(outmk, libs);
741 fprintf(outmk, "CRUNCHED_OBJS=");
742 for(p = progs; p != NULL; p = p->next)
743 fprintf(outmk, " %s.lo", p->name);
744 fprintf(outmk, "\n");
746 fprintf(outmk, "SUBMAKE_TARGETS=");
747 for(p = progs; p != NULL; p = p->next)
748 fprintf(outmk, " %s_make", p->ident);
749 fprintf(outmk, "\nSUBCLEAN_TARGETS=");
750 for(p = progs; p != NULL; p = p->next)
751 fprintf(outmk, " %s_clean", p->ident);
752 fprintf(outmk, "\n\n");
754 fprintf(outmk, "%s: %s.o $(CRUNCHED_OBJS)\n",
755 execfname, execfname);
756 fprintf(outmk, "\t$(CC) -static -o %s %s.o $(CRUNCHED_OBJS) $(LIBS)\n",
757 execfname, execfname);
758 fprintf(outmk, "\tstrip %s\n", execfname);
759 fprintf(outmk, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n");
760 fprintf(outmk, "exe: %s\n", execfname);
761 fprintf(outmk, "realclean: clean subclean\n");
762 fprintf(outmk, "clean:\n\trm -f %s *.lo *.o *_stub.c\n",
764 fprintf(outmk, "subclean: $(SUBCLEAN_TARGETS)\n");
768 void prog_makefile_rules(FILE *outmk, prog_t *p)
772 fprintf(outmk, "\n# -------- %s\n\n", p->name);
774 if(p->srcdir && p->objs) {
775 fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir);
776 fprintf(outmk, "%s_OBJS=", p->ident);
777 output_strlst(outmk, p->objs);
778 fprintf(outmk, "%s_make:\n", p->ident);
779 fprintf(outmk, "\t(cd $(%s_SRCDIR) && make depend && make $(%s_OBJS))\n",
781 fprintf(outmk, "%s_clean:\n", p->ident);
782 fprintf(outmk, "\t(cd $(%s_SRCDIR) && make clean)\n\n", p->ident);
785 fprintf(outmk, "%s_make:\n\t@echo \"** cannot make objs for %s\"\n\n",
788 fprintf(outmk, "%s_OBJPATHS=", p->ident);
789 output_strlst(outmk, p->objpaths);
791 fprintf(outmk, "%s_stub.c:\n", p->name);
792 fprintf(outmk, "\techo \""
793 "int _crunched_%s_stub(int argc, char **argv, char **envp)"
794 "{return main(argc,argv,envp);}\" >%s_stub.c\n",
796 fprintf(outmk, "%s.lo: %s_stub.o $(%s_OBJPATHS)\n",
797 p->name, p->name, p->ident);
798 fprintf(outmk, "\tld -dc -r -o %s.lo %s_stub.o $(%s_OBJPATHS)\n",
799 p->name, p->name, p->ident);
800 fprintf(outmk, "\tcrunchide -k _crunched_%s_stub ", p->ident);
801 for(lst = p->keeplist; lst != NULL; lst = lst->next)
802 fprintf(outmk, "-k _%s ", lst->str);
803 fprintf(outmk, "%s.lo\n", p->name);
806 void output_strlst(FILE *outf, strlst_t *lst)
808 for(; lst != NULL; lst = lst->next)
809 fprintf(outf, " %s", lst->str);
815 * ========================================================================
816 * general library routines
820 void status(char *str)
822 static int lastlen = 0;
828 spaces = lastlen - len;
829 if(spaces < 1) spaces = 1;
831 fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " ");
837 void out_of_memory(void)
839 errx(1, "%s: %d: out of memory, stopping", infilename, linenum);
843 void add_string(strlst_t **listp, char *str)
847 /* add to end, but be smart about dups */
849 for(p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next)
850 if(!strcmp(p2->str, str)) return;
852 p2 = malloc(sizeof(strlst_t));
854 memset(p2, 0, sizeof(strlst_t));
855 p2->str = strdup(str);
861 if(p1 == NULL) *listp = p2;
866 int is_dir(char *pathname)
870 if(stat(pathname, &buf) == -1)
872 return S_ISDIR(buf.st_mode);
875 int is_nonempty_file(char *pathname)
879 if(stat(pathname, &buf) == -1)
882 return S_ISREG(buf.st_mode) && buf.st_size > 0;