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.
34 #include <sys/param.h>
46 #define CRUNCH_VERSION "0.3"
48 #define MAXLINELEN 16384
49 #define MAXFIELDS 2048
52 /* internal representation of conf file: */
54 /* simple lists of strings suffice for most parms */
56 typedef struct strlst {
61 /* progs have structure, each field can be set with "special" or calculated */
64 struct prog *next; /* link field */
65 char *name; /* program name */
66 char *ident; /* C identifier for the program name */
70 char *objvar; /* Makefile variable to replace OBJS */
77 strlst_t *libs_int; /* internal libraries */
84 static strlst_t *buildopts = NULL;
85 static strlst_t *linkopts = NULL;
86 static strlst_t *srcdirs = NULL;
87 static strlst_t *libs = NULL;
88 static strlst_t *libs_so = NULL;
89 static strlst_t *libs_int = NULL;
90 static prog_t *progs = NULL;
92 static char confname[MAXPATHLEN - 32], infilename[MAXPATHLEN];
93 static char outmkname[MAXPATHLEN], outcfname[MAXPATHLEN], execfname[MAXPATHLEN];
94 static char tempfname[MAXPATHLEN], cachename[MAXPATHLEN], curfilename[MAXPATHLEN];
95 static char outhdrname[MAXPATHLEN]; /* user-supplied header for *.mk */
96 static char *objprefix; /* where are the objects ? */
97 static char *path_make;
98 static int linenum = -1;
99 static int goterror = 0;
101 static int verbose, readcache; /* options */
102 static int reading_cache;
103 static int makeobj = 0; /* add 'make obj' rules to the makefile */
105 static int list_mode;
107 /* general routines */
109 static void status(const char *str);
110 static void out_of_memory(void) __dead2;
111 static void add_string(strlst_t **listp, char *str, int nodup);
112 static int is_dir(const char *pathname);
113 static int is_nonempty_file(const char *pathname);
114 static int subtract_strlst(strlst_t **lista, strlst_t **listb);
115 static int in_list(strlst_t **listp, char *str);
116 static void free_list(strlst_t *head);
117 static int iseq(const char *a, const char *b);
119 /* helper routines for main() */
121 static void usage(void) __dead2;
122 static void parse_conf_file(void);
123 static void gen_outputs(void);
125 extern char *crunched_skel[];
129 main(int argc, char **argv)
136 *outmkname = *outcfname = *execfname = '\0';
138 path_make = getenv("MAKE");
139 if (path_make == NULL || *path_make == '\0')
142 p = getenv("MAKEOBJDIRPREFIX");
143 if (p == NULL || *p == '\0')
144 objprefix = "/usr/obj"; /* default */
145 else if ((objprefix = strdup(p)) == NULL)
148 while ((optc = getopt(argc, argv, "lh:m:c:e:p:foq")) != -1) {
161 strlcpy(outmkname, optarg, sizeof(outmkname));
164 if ((objprefix = strdup(optarg)) == NULL)
169 strlcpy(outhdrname, optarg, sizeof(outhdrname));
172 strlcpy(outcfname, optarg, sizeof(outcfname));
175 strlcpy(execfname, optarg, sizeof(execfname));
199 strlcpy(infilename, argv[0], sizeof(infilename));
201 /* confname = `basename infilename .conf` */
203 if ((p = strrchr(infilename, '/')) != NULL)
204 strlcpy(confname, p + 1, sizeof(confname));
206 strlcpy(confname, infilename, sizeof(confname));
208 if ((p = strrchr(confname, '.')) != NULL && iseq(p, ".conf"))
212 snprintf(outmkname, sizeof(outmkname), "%s.mk", confname);
214 snprintf(outcfname, sizeof(outcfname), "%s.c", confname);
216 snprintf(execfname, sizeof(execfname), "%s", confname);
218 snprintf(cachename, sizeof(cachename), "%s.cache", confname);
219 snprintf(tempfname, sizeof(tempfname), "%s/crunchgen_%sXXXXXX",
220 getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP, confname);
235 fprintf(stderr, "%s%s\n\t%s%s\n", "usage: crunchgen [-foq] ",
236 "[-h <makefile-header-name>] [-m <makefile>]",
237 "[-p <obj-prefix>] [-c <c-file-name>] [-e <exec-file>] ",
244 * ========================================================================
245 * parse_conf_file subsystem
249 /* helper routines for parse_conf_file */
251 static void parse_one_file(char *filename);
252 static void parse_line(char *pline, int *fc, char **fv, int nf);
253 static void add_srcdirs(int argc, char **argv);
254 static void add_progs(int argc, char **argv);
255 static void add_link(int argc, char **argv);
256 static void add_libs(int argc, char **argv);
257 static void add_libs_so(int argc, char **argv);
258 static void add_libs_int(int argc, char **argv);
259 static void add_buildopts(int argc, char **argv);
260 static void add_linkopts(int argc, char **argv);
261 static void add_special(int argc, char **argv);
263 static prog_t *find_prog(char *str);
264 static void add_prog(char *progname);
268 parse_conf_file(void)
270 if (!is_nonempty_file(infilename))
271 errx(1, "fatal: input file \"%s\" not found", infilename);
273 parse_one_file(infilename);
274 if (readcache && is_nonempty_file(cachename)) {
276 parse_one_file(cachename);
282 parse_one_file(char *filename)
284 char *fieldv[MAXFIELDS];
286 void (*f)(int c, char **v);
288 char line[MAXLINELEN];
290 snprintf(line, sizeof(line), "reading %s", filename);
292 strlcpy(curfilename, filename, sizeof(curfilename));
294 if ((cf = fopen(curfilename, "r")) == NULL) {
295 warn("%s", curfilename);
301 while (fgets(line, MAXLINELEN, cf) != NULL) {
303 parse_line(line, &fieldc, fieldv, MAXFIELDS);
307 } else if (fieldc == 1) {
308 warnx("%s:%d: %s %s",
309 curfilename, linenum, fieldv[0],
310 "command needs at least 1 argument, skipping");
315 if (iseq(fieldv[0], "srcdirs"))
317 else if (iseq(fieldv[0], "progs"))
319 else if (iseq(fieldv[0], "ln"))
321 else if (iseq(fieldv[0], "libs"))
323 else if (iseq(fieldv[0], "libs_so"))
325 else if (iseq(fieldv[0], "libs_int"))
327 else if (iseq(fieldv[0], "buildopts"))
329 else if (iseq(fieldv[0], "linkopts"))
331 else if (iseq(fieldv[0], "special"))
334 warnx("%s:%d: skipping unknown command `%s'",
335 curfilename, linenum, fieldv[0]);
344 warn("%s", curfilename);
352 parse_line(char *pline, int *fc, char **fv, int nf)
360 while (isspace((unsigned char)*p))
363 if (*p == '\0' || *p == '#')
369 while (*p && !isspace((unsigned char)*p) && *p != '#')
372 if (*p == '\0' || *p == '#')
379 *p = '\0'; /* needed for '#' case */
384 add_srcdirs(int argc, char **argv)
388 for (i = 1; i < argc; i++) {
389 if (is_dir(argv[i])) {
390 add_string(&srcdirs, argv[i], 1);
392 warnx("%s:%d: `%s' is not a directory, skipping it",
393 curfilename, linenum, argv[i]);
401 add_progs(int argc, char **argv)
405 for (i = 1; i < argc; i++)
411 add_prog(char *progname)
415 /* add to end, but be smart about dups */
417 for (p1 = NULL, p2 = progs; p2 != NULL; p1 = p2, p2 = p2->next)
418 if (iseq(p2->name, progname))
421 p2 = malloc(sizeof(prog_t));
423 memset(p2, 0, sizeof(prog_t));
424 p2->name = strdup(progname);
426 if (!p2 || !p2->name)
437 p2->realsrcdir = NULL;
443 p2->buildopts = NULL;
447 printf("%s\n", progname);
452 add_link(int argc, char **argv)
455 prog_t *p = find_prog(argv[1]);
458 warnx("%s:%d: no prog %s previously declared, skipping link",
459 curfilename, linenum, argv[1]);
464 for (i = 2; i < argc; i++) {
466 printf("%s\n",argv[i]);
468 add_string(&p->links, argv[i], 1);
474 add_libs(int argc, char **argv)
478 for (i = 1; i < argc; i++) {
479 add_string(&libs, argv[i], 1);
480 if (in_list(&libs_so, argv[i]))
482 "library `%s' specified as dynamic earlier",
483 curfilename, linenum, argv[i]);
489 add_libs_so(int argc, char **argv)
493 for (i = 1; i < argc; i++) {
494 add_string(&libs_so, argv[i], 1);
495 if (in_list(&libs, argv[i]))
497 "library `%s' specified as static earlier",
498 curfilename, linenum, argv[i]);
504 add_libs_int(int argc, char **argv)
508 for (i = 1; i < argc; i++) {
509 add_string(&libs_int, argv[i], 1);
515 add_buildopts(int argc, char **argv)
519 for (i = 1; i < argc; i++)
520 add_string(&buildopts, argv[i], 0); /* allow duplicates */
525 add_linkopts(int argc, char **argv)
529 for (i = 1; i < argc; i++)
530 add_string(&linkopts, argv[i], 0); /* allow duplicates */
535 add_special(int argc, char **argv)
538 prog_t *p = find_prog(argv[1]);
544 warnx("%s:%d: no prog %s previously declared, skipping special",
545 curfilename, linenum, argv[1]);
550 if (iseq(argv[2], "ident")) {
553 if ((p->ident = strdup(argv[3])) == NULL)
555 } else if (iseq(argv[2], "srcdir")) {
558 if ((p->srcdir = strdup(argv[3])) == NULL)
560 } else if (iseq(argv[2], "objdir")) {
563 if ((p->objdir = strdup(argv[3])) == NULL)
565 } else if (iseq(argv[2], "objs")) {
567 for (i = 3; i < argc; i++)
568 add_string(&p->objs, argv[i], 1);
569 } else if (iseq(argv[2], "objpaths")) {
571 for (i = 3; i < argc; i++)
572 add_string(&p->objpaths, argv[i], 1);
573 } else if (iseq(argv[2], "keep")) {
575 for (i = 3; i < argc; i++)
576 add_string(&p->keeplist, argv[i], 1);
577 } else if (iseq(argv[2], "objvar")) {
580 if ((p->objvar = strdup(argv[3])) == NULL)
582 } else if (iseq(argv[2], "buildopts")) {
584 for (i = 3; i < argc; i++)
585 add_string(&p->buildopts, argv[i], 0);
586 } else if (iseq(argv[2], "lib")) {
587 for (i = 3; i < argc; i++)
588 add_string(&p->libs, argv[i], 1);
589 } else if (iseq(argv[2], "lib_int")) {
590 for (i = 3; i < argc; i++)
591 add_string(&p->libs_int, argv[i], 1);
593 warnx("%s:%d: bad parameter name `%s', skipping line",
594 curfilename, linenum, argv[2]);
600 warnx("%s:%d: too %s arguments, expected \"special %s %s <string>\"",
601 curfilename, linenum, argc < 4 ? "few" : "many", argv[1], argv[2]);
606 static prog_t *find_prog(char *str)
610 for (p = progs; p != NULL; p = p->next)
611 if (iseq(p->name, str))
619 * ========================================================================
620 * gen_outputs subsystem
624 /* helper subroutines */
626 static void remove_error_progs(void);
627 static void fillin_program(prog_t *p);
628 static void gen_specials_cache(void);
629 static void gen_output_makefile(void);
630 static void gen_output_cfile(void);
632 static void fillin_program_objs(prog_t *p, char *path);
633 static void top_makefile_rules(FILE *outmk);
634 static void prog_makefile_rules(FILE *outmk, prog_t *p);
635 static void intlib_makefile_rules(FILE *outmk, char *path);
636 static void output_strlst(FILE *outf, strlst_t *lst);
637 static char *genident(char *str);
638 static char *dir_search(char *progname);
639 static void collect_internal_libs(strlst_t **listp);
647 for (p = progs; p != NULL; p = p->next)
650 remove_error_progs();
651 gen_specials_cache();
653 gen_output_makefile();
657 "Run \"%s -f %s\" to build crunched binary.\n",
658 path_make, outmkname);
662 * run the makefile for the program to find which objects are necessary
665 fillin_program(prog_t *p)
667 char path[MAXPATHLEN];
668 char line[MAXLINELEN];
670 snprintf(line, MAXLINELEN, "filling in parms for %s", p->name);
674 p->ident = genident(p->name);
676 /* look for the source directory if one wasn't specified by a special */
678 p->srcdir = dir_search(p->name);
681 /* Determine the real srcdir (maybe symlinked). */
683 if ((realpath(p->srcdir, path)) == NULL)
684 errx(1, "Can't get realpath on: %s\n", p->srcdir);
685 p->realsrcdir = strdup(path);
688 /* Unless the option to make object files was specified the
689 * the objects will be built in the source directory unless
690 * an object directory already exists.
692 if (!makeobj && !p->objdir && p->srcdir) {
693 snprintf(line, sizeof line, "%s/%s", objprefix, p->realsrcdir);
695 if ((p->objdir = strdup(line)) == NULL)
698 p->objdir = p->realsrcdir;
703 * XXX look for a Makefile.{name} in local directory first.
704 * This lets us override the original Makefile.
706 snprintf(path, sizeof(path), "Makefile.%s", p->name);
707 if (is_nonempty_file(path)) {
708 snprintf(line, MAXLINELEN, "Using %s for %s", path, p->name);
710 } else if (p->srcdir) {
711 snprintf(path, sizeof(path), "%s/Makefile", p->srcdir);
714 if (!p->objs && p->srcdir && is_nonempty_file(path))
715 fillin_program_objs(p, path);
717 if (!p->srcdir && !p->objdir && verbose)
719 "warning: could not find source directory",
720 infilename, p->name);
721 if (!p->objs && verbose)
722 warnx("%s: %s: warning: could not find any .o files",
723 infilename, p->name);
725 if ((!p->srcdir || !p->objdir) && !p->objs)
730 fillin_program_objs(prog_t *p, char *path)
735 char *objvar = "OBJS";
737 char line[MAXLINELEN];
739 /* discover the objs from the srcdir Makefile */
741 if ((fd = mkstemp(tempfname)) == -1) {
745 if ((f = fdopen(fd, "w")) == NULL) {
746 warn("%s", tempfname);
754 * XXX include outhdrname (e.g. to contain Make variables)
756 if (outhdrname[0] != '\0')
757 fprintf(f, ".include \"%s\"\n", outhdrname);
758 fprintf(f, ".include \"%s\"\n", path);
760 fprintf(f, "BUILDOPTS+=");
761 output_strlst(f, buildopts);
763 fprintf(f, ".if defined(PROG)\n");
764 fprintf(f, "%s?= ${PROG}.o\n", objvar);
765 fprintf(f, ".endif\n");
766 fprintf(f, "loop:\n\t@echo 'OBJS= '${%s}\n", objvar);
768 fprintf(f, "crunchgen_objs:\n"
769 "\t@cd %s && %s -f %s ${BUILDOPTS} ${%s_OPTS}",
770 p->srcdir, path_make, tempfname, p->ident);
771 for (s = p->buildopts; s != NULL; s = s->next)
772 fprintf(f, " %s", s->str);
773 fprintf(f, " loop\n");
777 snprintf(line, MAXLINELEN, "cd %s && %s -f %s -B crunchgen_objs",
778 p->srcdir, path_make, tempfname);
779 if ((f = popen(line, "r")) == NULL) {
780 warn("submake pipe");
786 while (fgets(line, MAXLINELEN, f)) {
787 if (strncmp(line, "OBJS= ", 6)) {
788 warnx("make error: %s", line);
794 while (isspace((unsigned char)*cp))
799 while (*cp && !isspace((unsigned char)*cp))
803 add_string(&p->objs, obj, 1);
804 while (isspace((unsigned char)*cp))
809 if ((rc = pclose(f)) != 0) {
810 warnx("make error: make returned %d", rc);
818 remove_error_progs(void)
822 p1 = NULL; p2 = progs;
825 p1 = p2, p2 = p2->next;
827 /* delete it from linked list */
828 warnx("%s: %s: ignoring program because of errors",
829 infilename, p2->name);
840 gen_specials_cache(void)
844 char line[MAXLINELEN];
846 snprintf(line, MAXLINELEN, "generating %s", cachename);
849 if ((cachef = fopen(cachename, "w")) == NULL) {
850 warn("%s", cachename);
856 "# %s - parm cache generated from %s by crunchgen %s\n\n",
857 cachename, infilename, CRUNCH_VERSION);
859 for (p = progs; p != NULL; p = p->next) {
860 fprintf(cachef, "\n");
862 fprintf(cachef, "special %s srcdir %s\n",
865 fprintf(cachef, "special %s objdir %s\n",
868 fprintf(cachef, "special %s objs", p->name);
869 output_strlst(cachef, p->objs);
872 fprintf(cachef, "special %s objpaths", p->name);
873 output_strlst(cachef, p->objpaths);
881 gen_output_makefile(void)
884 strlst_t *intlibs, *l;
886 char line[MAXLINELEN];
888 snprintf(line, MAXLINELEN, "generating %s", outmkname);
891 if ((outmk = fopen(outmkname, "w")) == NULL) {
892 warn("%s", outmkname);
897 fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n",
898 outmkname, infilename, CRUNCH_VERSION);
900 if (outhdrname[0] != '\0')
901 fprintf(outmk, ".include \"%s\"\n", outhdrname);
903 top_makefile_rules(outmk);
906 collect_internal_libs(&intlibs);
907 for (l = intlibs; l != NULL; l = l->next)
908 intlib_makefile_rules(outmk, l->str);
911 for (p = progs; p != NULL; p = p->next)
912 prog_makefile_rules(outmk, p);
914 fprintf(outmk, "\n# ========\n");
920 gen_output_cfile(void)
926 char line[MAXLINELEN];
928 snprintf(line, MAXLINELEN, "generating %s", outcfname);
931 if((outcf = fopen(outcfname, "w")) == NULL) {
932 warn("%s", outcfname);
938 "/* %s - generated from %s by crunchgen %s */\n",
939 outcfname, infilename, CRUNCH_VERSION);
941 fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname);
942 for (cp = crunched_skel; *cp != NULL; cp++)
943 fprintf(outcf, "%s\n", *cp);
945 for (p = progs; p != NULL; p = p->next)
946 fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident);
948 fprintf(outcf, "\nstatic const struct stub entry_points[] = {\n");
949 for (p = progs; p != NULL; p = p->next) {
950 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
952 for (s = p->links; s != NULL; s = s->next)
953 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
957 fprintf(outcf, "\t{ EXECNAME, crunched_main },\n");
958 fprintf(outcf, "\t{ NULL, NULL }\n};\n");
963 static char *genident(char *str)
968 * generates a Makefile/C identifier from a program name,
969 * mapping '-' to '_' and ignoring all other non-identifier
970 * characters. This leads to programs named "foo.bar" and
971 * "foobar" to map to the same identifier.
974 if ((n = strdup(str)) == NULL)
976 for (d = s = n; *s != '\0'; s++) {
979 else if (*s == '_' || isalnum((unsigned char)*s))
987 static char *dir_search(char *progname)
989 char path[MAXPATHLEN];
993 for (dir = srcdirs; dir != NULL; dir = dir->next) {
994 snprintf(path, MAXPATHLEN, "%s/%s", dir->str, progname);
998 if ((srcdir = strdup(path)) == NULL)
1008 collect_internal_libs(strlst_t **listp)
1013 for (l = libs_int; l != NULL; l = l->next)
1014 add_string(listp, l->str, 1);
1015 for (p = progs; p != NULL; p = p->next) {
1017 for (l = p->libs_int; l != NULL; l = l->next)
1018 add_string(listp, l->str, 1);
1025 top_makefile_rules(FILE *outmk)
1028 strlst_t *intlibs, *l;
1030 if (subtract_strlst(&libs, &libs_so))
1031 fprintf(outmk, "# NOTE: Some LIBS declarations below overridden by LIBS_SO\n");
1033 fprintf(outmk, "LIBS+=");
1034 output_strlst(outmk, libs);
1036 fprintf(outmk, "LIBS_SO+=");
1037 output_strlst(outmk, libs_so);
1039 fprintf(outmk, "LIBS_INT+=");
1040 output_strlst(outmk, libs_int);
1043 fprintf(outmk, "MAKEOBJDIRPREFIX?=%s\n", objprefix);
1044 fprintf(outmk, "MAKEENV=env MAKEOBJDIRPREFIX=${MAKEOBJDIRPREFIX}\n");
1045 fprintf(outmk, "CRUNCHMAKE=${MAKEENV} ${MAKE}\n");
1047 fprintf(outmk, "CRUNCHMAKE=${MAKE}\n");
1051 fprintf(outmk, "BUILDOPTS+=");
1052 output_strlst(outmk, buildopts);
1055 fprintf(outmk, "LINKOPTS+=");
1056 output_strlst(outmk, linkopts);
1059 fprintf(outmk, "CRUNCHED_OBJS=");
1060 for (p = progs; p != NULL; p = p->next)
1061 fprintf(outmk, " %s.lo", p->name);
1062 fprintf(outmk, "\n");
1064 fprintf(outmk, "SUBMAKE_TARGETS=");
1065 for (p = progs; p != NULL; p = p->next)
1066 fprintf(outmk, " %s_make", p->ident);
1067 fprintf(outmk, "\nSUBCLEAN_TARGETS=");
1068 for (p = progs; p != NULL; p = p->next)
1069 fprintf(outmk, " %s_clean", p->ident);
1070 fprintf(outmk, "\n");
1072 /* internal libraries */
1074 collect_internal_libs(&intlibs);
1075 fprintf(outmk, "SUBMAKE_TARGETS+=");
1076 for (l = intlibs; l != NULL; l = l->next)
1077 fprintf(outmk, " %s_make", basename(l->str));
1078 fprintf(outmk, "\nSUBCLEAN_TARGETS+=");
1079 for (l = intlibs; l != NULL; l = l->next)
1080 fprintf(outmk, " %s_clean", basename(l->str));
1081 fprintf(outmk, "\n\n");
1084 fprintf(outmk, "all: objs exe\nobjs: ${SUBMAKE_TARGETS}\n");
1085 fprintf(outmk, "exe: %s\n", execfname);
1086 fprintf(outmk, "%s: %s.o ${CRUNCHED_OBJS} ${SUBMAKE_TARGETS}\n",
1087 execfname, execfname);
1088 fprintf(outmk, ".if defined(LIBS_SO) && !empty(LIBS_SO)\n");
1089 fprintf(outmk, "\t${CC} ${LINKOPTS} -o %s %s.o \\\n",
1090 execfname, execfname);
1091 fprintf(outmk, "\t\t${CRUNCHED_OBJS} ${LIBS_INT} \\\n");
1092 fprintf(outmk, "\t\t-Xlinker -Bstatic ${LIBS} \\\n");
1093 fprintf(outmk, "\t\t-Xlinker -Bdynamic ${LIBS_SO}\n");
1094 fprintf(outmk, ".else\n");
1095 fprintf(outmk, "\t${CC} ${LINKOPTS} -static -o %s %s.o \\\n",
1096 execfname, execfname);
1097 fprintf(outmk, "\t\t${CRUNCHED_OBJS} ${LIBS_INT} ${LIBS}\n");
1098 fprintf(outmk, ".endif\n");
1099 fprintf(outmk, "\tstrip %s\n", execfname);
1100 fprintf(outmk, "realclean: clean subclean\n");
1101 fprintf(outmk, "clean:\n\trm -f %s *.lo *.o *_stub.c\n", execfname);
1102 fprintf(outmk, "subclean: ${SUBCLEAN_TARGETS}\n");
1107 prog_makefile_rules(FILE *outmk, prog_t *p)
1111 fprintf(outmk, "\n# -------- %s\n\n", p->name);
1113 fprintf(outmk, "%s_OBJDIR=", p->ident);
1115 fprintf(outmk, "%s", p->objdir);
1117 fprintf(outmk, "${MAKEOBJDIRPREFIX}/${%s_REALSRCDIR}\n",
1119 fprintf(outmk, "\n");
1121 fprintf(outmk, "%s_OBJPATHS=", p->ident);
1123 output_strlst(outmk, p->objpaths);
1125 for (lst = p->objs; lst != NULL; lst = lst->next)
1126 fprintf(outmk, " ${%s_OBJDIR}/%s", p->ident, lst->str);
1127 fprintf(outmk, "\n");
1129 fprintf(outmk, "${%s_OBJPATHS}: .NOMETA\n", p->ident);
1131 if (p->srcdir && p->objs) {
1132 fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir);
1133 fprintf(outmk, "%s_REALSRCDIR=%s\n", p->ident, p->realsrcdir);
1135 fprintf(outmk, "%s_OBJS=", p->ident);
1136 output_strlst(outmk, p->objs);
1137 if (p->buildopts != NULL) {
1138 fprintf(outmk, "%s_OPTS+=", p->ident);
1139 output_strlst(outmk, p->buildopts);
1141 fprintf(outmk, "%s_make:\n", p->ident);
1142 fprintf(outmk, "\t(cd ${%s_SRCDIR} && ", p->ident);
1144 fprintf(outmk, "${CRUNCHMAKE} obj && ");
1145 fprintf(outmk, "\\\n");
1146 fprintf(outmk, "\t\t${CRUNCHMAKE} ${BUILDOPTS} ${%s_OPTS} depend && ",
1148 fprintf(outmk, "\\\n");
1149 fprintf(outmk, "\t\t${CRUNCHMAKE} ${BUILDOPTS} ${%s_OPTS} "
1151 p->ident, p->ident);
1152 fprintf(outmk, "\n");
1153 fprintf(outmk, "%s_clean:\n", p->ident);
1154 fprintf(outmk, "\t(cd ${%s_SRCDIR} && ${CRUNCHMAKE} ${BUILDOPTS} clean cleandepend)\n\n",
1157 fprintf(outmk, "%s_make:\n", p->ident);
1158 fprintf(outmk, "\t@echo \"** cannot make objs for %s\"\n\n",
1163 fprintf(outmk, "%s_LIBS_INT=", p->ident);
1164 for (lst = p->libs_int; lst != NULL; lst = lst->next)
1165 fprintf(outmk, " ${%s_LIB}", basename(lst->str));
1166 fprintf(outmk, "\n");
1169 fprintf(outmk, "%s_LIBS=", p->ident);
1170 output_strlst(outmk, p->libs);
1173 fprintf(outmk, "%s_stub.c:\n", p->name);
1174 fprintf(outmk, "\techo \""
1175 "extern int main(int, char **, char **); "
1176 "int _crunched_%s_stub(int argc, char **argv, char **envp)"
1177 "{return main(argc,argv,envp);}\" >%s_stub.c\n",
1179 fprintf(outmk, "%s_stub.o: %s_stub.c\n",
1181 fprintf(outmk, "\t${CC} ${CFLAGS:N-flto*} -c %s_stub.c -o %s_stub.o",
1183 fprintf(outmk, "\n");
1184 fprintf(outmk, "%s.lo: %s_stub.o ${%s_OBJPATHS}",
1185 p->name, p->name, p->ident);
1187 fprintf(outmk, " ${%s_LIBS_INT}", p->ident);
1189 fprintf(outmk, " ${%s_LIBS}", p->ident);
1191 fprintf(outmk, "\n");
1192 fprintf(outmk, "\t${CC} -nostdlib -Wl,-dc -r "
1193 "-o %s.lo %s_stub.o ${%s_OBJPATHS}",
1194 p->name, p->name, p->ident);
1196 fprintf(outmk, " ${%s_LIBS}", p->ident);
1198 fprintf(outmk, " ${%s_LIBS_INT}", p->ident);
1199 fprintf(outmk, "\n");
1200 fprintf(outmk, "\tcrunchide -k _crunched_%s_stub ", p->ident);
1201 for (lst = p->keeplist; lst != NULL; lst = lst->next)
1202 fprintf(outmk, "-k %s ", lst->str);
1203 fprintf(outmk, "%s.lo\n", p->name);
1208 intlib_makefile_rules(FILE *outmk, char *path)
1210 char *pathcopy, *libname, *srcdir, *objdir;
1211 char realsrcdir[MAXPATHLEN], line[MAXPATHLEN];
1213 libname = basename(path);
1214 if ((pathcopy = strdup(path)) == NULL)
1216 srcdir = dirname(pathcopy);
1217 if ((realpath(srcdir, realsrcdir)) == NULL)
1218 errx(1, "Can't get realpath on: %s\n", srcdir);
1220 fprintf(outmk, "\n# -------- %s\n\n", libname);
1221 fprintf(outmk, "%s_SRCDIR=%s\n", libname, srcdir);
1222 fprintf(outmk, "%s_REALSRCDIR=%s\n", libname, realsrcdir);
1224 snprintf(line, sizeof line, "%s/%s", objprefix, realsrcdir);
1226 if ((objdir = strdup(line)) == NULL)
1229 objdir = realsrcdir;
1231 fprintf(outmk, "%s_OBJDIR=%s\n", libname, objdir);
1232 fprintf(outmk, "%s_LIB=${%s_OBJDIR}/%s\n", libname, libname, libname);
1234 fprintf(outmk, "%s_make:\n", libname);
1235 fprintf(outmk, "\t(cd ${%s_SRCDIR} && ", libname);
1237 fprintf(outmk, "${CRUNCHMAKE} obj && ");
1238 fprintf(outmk, "\\\n");
1239 fprintf(outmk, "\t\t${CRUNCHMAKE} ${BUILDOPTS} ${%s_OPTS} depend && ",
1241 fprintf(outmk, "\\\n");
1242 fprintf(outmk, "\t\t${CRUNCHMAKE} ${BUILDOPTS} ${%s_OPTS} %s)\n",
1244 fprintf(outmk, "%s_clean:\n", libname);
1245 fprintf(outmk, "\t(cd ${%s_SRCDIR} && ${CRUNCHMAKE} ${BUILDOPTS} clean cleandepend)\n",
1251 output_strlst(FILE *outf, strlst_t *lst)
1253 for (; lst != NULL; lst = lst->next)
1254 if ( strlen(lst->str) )
1255 fprintf(outf, " %s", lst->str);
1256 fprintf(outf, "\n");
1261 * ========================================================================
1262 * general library routines
1267 status(const char *str)
1269 static int lastlen = 0;
1276 spaces = lastlen - len;
1280 fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " ");
1289 err(1, "%s: %d: out of memory, stopping", infilename, linenum);
1294 add_string(strlst_t **listp, char *str, int nodup)
1298 /* add to end, and avoid duplicate if nodup != 0 */
1300 for (p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next)
1301 if (nodup && iseq(p2->str, str))
1304 p2 = malloc(sizeof(strlst_t));
1307 p2->str = strdup(str);
1309 if (!p2 || !p2->str)
1319 subtract_strlst(strlst_t **lista, strlst_t **listb)
1321 int subtract_count = 0;
1324 for (p1 = *listb; p1 != NULL; p1 = p1->next)
1325 if (in_list(lista, p1->str)) {
1326 warnx("Will compile library `%s' dynamically", p1->str);
1327 strcat(p1->str, "");
1331 return subtract_count;
1335 in_list(strlst_t **listp, char *str)
1339 for (p1 = *listp; p1 != NULL; p1 = p1->next)
1340 if (iseq(p1->str, str))
1346 free_list(strlst_t *head)
1350 while (head != NULL) {
1359 is_dir(const char *pathname)
1363 if (stat(pathname, &buf) == -1)
1366 return S_ISDIR(buf.st_mode);
1370 is_nonempty_file(const char *pathname)
1374 if (stat(pathname, &buf) == -1)
1377 return S_ISREG(buf.st_mode) && buf.st_size > 0;
1381 iseq(const char *a, const char *b)
1383 return (strcmp(a, b) == 0);