crunchgen.c revision 187131
12490Sjkh/* 22490Sjkh * Copyright (c) 1994 University of Maryland 32490Sjkh * All Rights Reserved. 42490Sjkh * 52490Sjkh * Permission to use, copy, modify, distribute, and sell this software and its 62490Sjkh * documentation for any purpose is hereby granted without fee, provided that 72490Sjkh * the above copyright notice appear in all copies and that both that 82490Sjkh * copyright notice and this permission notice appear in supporting 92490Sjkh * documentation, and that the name of U.M. not be used in advertising or 102490Sjkh * publicity pertaining to distribution of the software without specific, 112490Sjkh * written prior permission. U.M. makes no representations about the 122490Sjkh * suitability of this software for any purpose. It is provided "as is" 132490Sjkh * without express or implied warranty. 142490Sjkh * 152490Sjkh * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL 162490Sjkh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M. 172490Sjkh * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 182490Sjkh * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 192490Sjkh * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 202490Sjkh * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 212490Sjkh * 222490Sjkh * Author: James da Silva, Systems Design and Analysis Group 232490Sjkh * Computer Science Department 242490Sjkh * University of Maryland at College Park 252490Sjkh * 262490Sjkh * $FreeBSD: head/usr.sbin/crunch/crunchgen/crunchgen.c 187131 2009-01-13 06:52:51Z obrien $ 272490Sjkh */ 282490Sjkh/* 292490Sjkh * ======================================================================== 302490Sjkh * crunchgen.c 312490Sjkh * 322490Sjkh * Generates a Makefile and main C file for a crunched executable, 332490Sjkh * from specs given in a .conf file. 3410352Sjoerg */ 3510352Sjoerg#include <sys/types.h> 3610352Sjoerg#include <sys/stat.h> 3710352Sjoerg#include <sys/param.h> 3810352Sjoerg 392490Sjkh#include <ctype.h> 4053920Sbillf#include <err.h> 412490Sjkh#include <paths.h> 422490Sjkh#include <stdio.h> 432490Sjkh#include <stdlib.h> 442490Sjkh#include <string.h> 452490Sjkh#include <unistd.h> 4653920Sbillf 472490Sjkh#define CRUNCH_VERSION "0.2" 4853920Sbillf 4953920Sbillf#define MAXLINELEN 16384 5053920Sbillf#define MAXFIELDS 2048 512490Sjkh 522490Sjkh 5357527Sjoerg/* internal representation of conf file: */ 5457527Sjoerg 552490Sjkh/* simple lists of strings suffice for most parms */ 5657527Sjoerg 5778793Sachetypedef struct strlst { 5810352Sjoerg struct strlst *next; 5957527Sjoerg char *str; 6057527Sjoerg} strlst_t; 6110352Sjoerg 6235857Sjb/* progs have structure, each field can be set with "special" or calculated */ 6357527Sjoerg 6454622Sbillftypedef struct prog { 652490Sjkh struct prog *next; /* link field */ 6610352Sjoerg char *name; /* program name */ 6710352Sjoerg char *ident; /* C identifier for the program name */ 6810352Sjoerg char *srcdir; 6910352Sjoerg char *realsrcdir; 7010352Sjoerg char *objdir; 7110352Sjoerg char *objvar; /* Makefile variable to replace OBJS */ 7210352Sjoerg strlst_t *objs, *objpaths; 732490Sjkh strlst_t *buildopts; 742490Sjkh strlst_t *keeplist; 7554622Sbillf strlst_t *links; 762490Sjkh strlst_t *libs; 7710352Sjoerg strlst_t *libs_so; 7810352Sjoerg int goterror; 7954622Sbillf} prog_t; 8054622Sbillf 8154622Sbillf 8254622Sbillf/* global state */ 8354622Sbillf 8454622Sbillfstrlst_t *buildopts = NULL; 8554622Sbillfstrlst_t *srcdirs = NULL; 8654622Sbillfstrlst_t *libs = NULL; 8754622Sbillfstrlst_t *libs_so = NULL; 8854622Sbillfprog_t *progs = NULL; 8954622Sbillf 9054622Sbillfchar confname[MAXPATHLEN], infilename[MAXPATHLEN]; 9154622Sbillfchar outmkname[MAXPATHLEN], outcfname[MAXPATHLEN], execfname[MAXPATHLEN]; 9254622Sbillfchar tempfname[MAXPATHLEN], cachename[MAXPATHLEN], curfilename[MAXPATHLEN]; 9354622Sbillfchar outhdrname[MAXPATHLEN] ; /* user-supplied header for *.mk */ 9454622Sbillfchar *objprefix; /* where are the objects ? */ 9554622Sbillfint linenum = -1; 9654622Sbillfint goterror = 0; 9754622Sbillf 9854622Sbillfint verbose, readcache; /* options */ 9954622Sbillfint reading_cache; 10054622Sbillfint makeobj = 0; /* add 'make obj' rules to the makefile */ 10154622Sbillf 10254622Sbillfint list_mode; 10354622Sbillf 10454622Sbillf/* general library routines */ 10510352Sjoerg 10610352Sjoergvoid status(char *str); 10710352Sjoergvoid out_of_memory(void); 10854622Sbillfvoid add_string(strlst_t **listp, char *str); 10954622Sbillfint is_dir(char *pathname); 11054622Sbillfint is_nonempty_file(char *pathname); 11154622Sbillfint subtract_strlst(strlst_t **lista, strlst_t **listb); 11254622Sbillfint in_list(strlst_t **listp, char *str); 11354622Sbillf 11454622Sbillf/* helper routines for main() */ 11554622Sbillf 11654622Sbillfvoid usage(void); 11754622Sbillfvoid parse_conf_file(void); 11810352Sjoergvoid gen_outputs(void); 11910352Sjoerg 12010352Sjoerg 12154622Sbillfint main(int argc, char **argv) 12254622Sbillf{ 123126040Sfanf char *p; 124126040Sfanf int optc; 12554622Sbillf 12654622Sbillf verbose = 1; 12754622Sbillf readcache = 1; 12854622Sbillf *outmkname = *outcfname = *execfname = '\0'; 12954622Sbillf 13054622Sbillf p = getenv("MAKEOBJDIRPREFIX"); 13154622Sbillf if (p == NULL || *p == '\0') 13254622Sbillf objprefix = "/usr/obj"; /* default */ 13354622Sbillf else 13454622Sbillf if ((objprefix = strdup(p)) == NULL) 135126044Sfanf out_of_memory(); 13610352Sjoerg 13710352Sjoerg while((optc = getopt(argc, argv, "lh:m:c:e:p:foq")) != -1) { 13810352Sjoerg switch(optc) { 13954622Sbillf case 'f': 140126040Sfanf readcache = 0; 14154622Sbillf break; 14254622Sbillf case 'o': 14310352Sjoerg makeobj = 1; 14454622Sbillf break; 14510352Sjoerg case 'q': 14610352Sjoerg verbose = 0; 14710352Sjoerg break; 148129114Sdds 14954622Sbillf case 'm': 15054622Sbillf strlcpy(outmkname, optarg, sizeof(outmkname)); 15154622Sbillf break; 15254622Sbillf case 'p': 15354622Sbillf if ((objprefix = strdup(optarg)) == NULL) 15454622Sbillf out_of_memory(); 15554622Sbillf break; 15654622Sbillf 15754622Sbillf case 'h': 15854622Sbillf strlcpy(outhdrname, optarg, sizeof(outhdrname)); 15910352Sjoerg break; 16054622Sbillf case 'c': 16110352Sjoerg strlcpy(outcfname, optarg, sizeof(outcfname)); 16210352Sjoerg break; 163129114Sdds case 'e': 164129114Sdds strlcpy(execfname, optarg, sizeof(execfname)); 165129114Sdds break; 166129114Sdds 167129114Sdds case 'l': 168129114Sdds list_mode++; 169129114Sdds verbose = 0; 170129114Sdds break; 171129114Sdds 172129114Sdds case '?': 173129114Sdds default: 174129114Sdds usage(); 175129114Sdds } 176129114Sdds } 177129114Sdds 178129114Sdds argc -= optind; 179129114Sdds argv += optind; 180129114Sdds 181129114Sdds if (argc != 1) 182129114Sdds usage(); 183129114Sdds 184129114Sdds /* 185129114Sdds * generate filenames 186129114Sdds */ 187129114Sdds 188129114Sdds strlcpy(infilename, argv[0], sizeof(infilename)); 189129114Sdds 190129114Sdds /* confname = `basename infilename .conf` */ 191129114Sdds 192129114Sdds if ((p=strrchr(infilename, '/')) != NULL) 193129114Sdds strlcpy(confname, p + 1, sizeof(confname)); 194129114Sdds else 195129114Sdds strlcpy(confname, infilename, sizeof(confname)); 196129114Sdds 197129114Sdds if ((p=strrchr(confname, '.')) != NULL && !strcmp(p, ".conf")) 198129114Sdds *p = '\0'; 199129114Sdds 200129114Sdds if (!*outmkname) 201129114Sdds snprintf(outmkname, sizeof(outmkname), "%s.mk", confname); 202129114Sdds if (!*outcfname) 203129114Sdds snprintf(outcfname, sizeof(outcfname), "%s.c", confname); 204129114Sdds if (!*execfname) 205129114Sdds snprintf(execfname, sizeof(execfname), "%s", confname); 206129114Sdds 207129114Sdds snprintf(cachename, sizeof(cachename), "%s.cache", confname); 208129114Sdds snprintf(tempfname, sizeof(tempfname), "%s/crunchgen_%sXXXXXX", 209129114Sdds getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP, confname); 210129114Sdds 211129114Sdds parse_conf_file(); 212129114Sdds if (list_mode) 213129114Sdds exit(goterror); 214129114Sdds 215129114Sdds gen_outputs(); 216129114Sdds 217129114Sdds exit(goterror); 218129114Sdds} 219129114Sdds 220129114Sdds 221129114Sddsvoid usage(void) 222129114Sdds{ 223129114Sdds fprintf(stderr, "%s%s\n\t%s%s\n", "usage: crunchgen [-foq] ", 22454622Sbillf "[-h <makefile-header-name>] [-m <makefile>]", 22510352Sjoerg "[-p <obj-prefix>] [-c <c-file-name>] [-e <exec-file>] ", 22610352Sjoerg "<conffile>"); 22710352Sjoerg exit(1); 22810352Sjoerg} 22954622Sbillf 23054622Sbillf 23154622Sbillf/* 23254622Sbillf * ======================================================================== 23354622Sbillf * parse_conf_file subsystem 23454622Sbillf * 23554622Sbillf */ 23654622Sbillf 23754622Sbillf/* helper routines for parse_conf_file */ 23854622Sbillf 23954622Sbillfvoid parse_one_file(char *filename); 24054622Sbillfvoid parse_line(char *line, int *fc, char **fv, int nf); 24154622Sbillfvoid add_srcdirs(int argc, char **argv); 24254622Sbillfvoid add_progs(int argc, char **argv); 24354622Sbillfvoid add_link(int argc, char **argv); 24454622Sbillfvoid add_libs(int argc, char **argv); 24554622Sbillfvoid add_libs_so(int argc, char **argv); 24654622Sbillfvoid add_buildopts(int argc, char **argv); 24754622Sbillfvoid add_special(int argc, char **argv); 24854622Sbillf 24954622Sbillfprog_t *find_prog(char *str); 25054622Sbillfvoid add_prog(char *progname); 25154622Sbillf 25254622Sbillf 25354622Sbillfvoid parse_conf_file(void) 25454622Sbillf{ 25554622Sbillf if (!is_nonempty_file(infilename)) 25654622Sbillf errx(1, "fatal: input file \"%s\" not found", infilename); 25754622Sbillf 25854622Sbillf parse_one_file(infilename); 25954622Sbillf if (readcache && is_nonempty_file(cachename)) { 26054622Sbillf reading_cache = 1; 26110352Sjoerg parse_one_file(cachename); 26254622Sbillf } 26310352Sjoerg} 26410352Sjoerg 26510352Sjoerg 26657527Sjoergvoid parse_one_file(char *filename) 26757527Sjoerg{ 26810352Sjoerg char *fieldv[MAXFIELDS]; 269147110Sjoerg int fieldc; 27057527Sjoerg void (*f)(int c, char **v); 271147110Sjoerg FILE *cf; 27257527Sjoerg char line[MAXLINELEN]; 273121945Sphk 274147110Sjoerg snprintf(line, sizeof(line), "reading %s", filename); 275147110Sjoerg status(line); 276147110Sjoerg strlcpy(curfilename, filename, sizeof(curfilename)); 27710352Sjoerg 27810352Sjoerg if ((cf = fopen(curfilename, "r")) == NULL) { 27957527Sjoerg warn("%s", curfilename); 28010352Sjoerg goterror = 1; 28110352Sjoerg return; 28210352Sjoerg } 28310352Sjoerg 28410352Sjoerg linenum = 0; 285147110Sjoerg while (fgets(line, MAXLINELEN, cf) != NULL) { 28657527Sjoerg linenum++; 28757527Sjoerg parse_line(line, &fieldc, fieldv, MAXFIELDS); 28857527Sjoerg 28957527Sjoerg if (fieldc < 1) 29057527Sjoerg continue; 29110352Sjoerg 29257527Sjoerg if (!strcmp(fieldv[0], "srcdirs")) 293147110Sjoerg f = add_srcdirs; 29457527Sjoerg else if(!strcmp(fieldv[0], "progs")) 29557527Sjoerg f = add_progs; 296147110Sjoerg else if(!strcmp(fieldv[0], "ln")) 29710352Sjoerg f = add_link; 29810352Sjoerg else if(!strcmp(fieldv[0], "libs")) 29978793Sache f = add_libs; 30010352Sjoerg else if(!strcmp(fieldv[0], "libs_so")) 30110352Sjoerg f = add_libs_so; 30210352Sjoerg else if(!strcmp(fieldv[0], "buildopts")) 3032490Sjkh f = add_buildopts; 30457527Sjoerg else if(!strcmp(fieldv[0], "special")) 30578793Sache f = add_special; 3062490Sjkh else { 30757527Sjoerg warnx("%s:%d: skipping unknown command `%s'", 30810352Sjoerg curfilename, linenum, fieldv[0]); 309147110Sjoerg goterror = 1; 310147110Sjoerg continue; 311147110Sjoerg } 31257527Sjoerg 31357527Sjoerg if (fieldc < 2) { 31457527Sjoerg warnx("%s:%d: %s %s", 31557527Sjoerg curfilename, linenum, fieldv[0], 31657527Sjoerg "command needs at least 1 argument, skipping"); 31757527Sjoerg goterror = 1; 31857527Sjoerg continue; 31910352Sjoerg } 32010352Sjoerg 32110352Sjoerg f(fieldc, fieldv); 322121945Sphk } 323121945Sphk 324121945Sphk if (ferror(cf)) { 32557527Sjoerg warn("%s", curfilename); 32610352Sjoerg goterror = 1; 32710352Sjoerg } 32810352Sjoerg fclose(cf); 32957527Sjoerg} 3302490Sjkh 3312490Sjkh 3322490Sjkhvoid parse_line(char *line, int *fc, char **fv, int nf) 33310352Sjoerg{ 33410352Sjoerg char *p; 33510352Sjoerg 3362490Sjkh p = line; 3372490Sjkh *fc = 0; 33857527Sjoerg 3392490Sjkh while (1) { 3402490Sjkh while (isspace(*p)) 341121945Sphk p++; 342121945Sphk 34310352Sjoerg if (*p == '\0' || *p == '#') 34410352Sjoerg break; 345121945Sphk 346121945Sphk if (*fc < nf) 347121945Sphk fv[(*fc)++] = p; 348121945Sphk 349147110Sjoerg while (*p && !isspace(*p) && *p != '#') 350147110Sjoerg p++; 351147110Sjoerg 35210352Sjoerg if (*p == '\0' || *p == '#') 35310352Sjoerg break; 35410352Sjoerg 35557527Sjoerg *p++ = '\0'; 35610352Sjoerg } 35710352Sjoerg 35810352Sjoerg if (*p) 35910352Sjoerg *p = '\0'; /* needed for '#' case */ 36010352Sjoerg} 36110352Sjoerg 36210352Sjoerg 36310352Sjoergvoid add_srcdirs(int argc, char **argv) 36457527Sjoerg{ 36557527Sjoerg int i; 36657527Sjoerg 36757527Sjoerg for (i = 1; i < argc; i++) { 36857527Sjoerg if (is_dir(argv[i])) 36957527Sjoerg add_string(&srcdirs, argv[i]); 37057527Sjoerg else { 37157527Sjoerg warnx("%s:%d: `%s' is not a directory, skipping it", 37257527Sjoerg curfilename, linenum, argv[i]); 37357527Sjoerg goterror = 1; 37457527Sjoerg } 37557527Sjoerg } 37657527Sjoerg} 37757527Sjoerg 37857527Sjoerg 37957527Sjoergvoid add_progs(int argc, char **argv) 38057527Sjoerg{ 38157527Sjoerg int i; 38257527Sjoerg 38357527Sjoerg for (i = 1; i < argc; i++) 38457527Sjoerg add_prog(argv[i]); 38557527Sjoerg} 38657527Sjoerg 38757527Sjoerg 38857527Sjoergvoid add_prog(char *progname) 38957527Sjoerg{ 39057527Sjoerg prog_t *p1, *p2; 39110352Sjoerg 39210352Sjoerg /* add to end, but be smart about dups */ 39310352Sjoerg 39410352Sjoerg for (p1 = NULL, p2 = progs; p2 != NULL; p1 = p2, p2 = p2->next) 39510352Sjoerg if (!strcmp(p2->name, progname)) 396147110Sjoerg return; 397147110Sjoerg 398147110Sjoerg p2 = malloc(sizeof(prog_t)); 399147110Sjoerg if(p2) { 400147110Sjoerg memset(p2, 0, sizeof(prog_t)); 401147110Sjoerg p2->name = strdup(progname); 40210352Sjoerg } 40357527Sjoerg if (!p2 || !p2->name) 4042490Sjkh out_of_memory(); 4052490Sjkh 4062490Sjkh p2->next = NULL; 40778793Sache if (p1 == NULL) 40878793Sache progs = p2; 40978793Sache else 41010352Sjoerg p1->next = p2; 41178793Sache 41278793Sache p2->ident = NULL; 413129114Sdds p2->srcdir = NULL; 414129114Sdds p2->realsrcdir = NULL; 415129114Sdds p2->objdir = NULL; 41610352Sjoerg p2->links = NULL; 41710352Sjoerg p2->libs = NULL; 418121945Sphk p2->libs_so = NULL; 419121945Sphk p2->objs = NULL; 42010352Sjoerg p2->keeplist = NULL; 4212490Sjkh p2->buildopts = NULL; 42210352Sjoerg p2->goterror = 0; 42357527Sjoerg 42457527Sjoerg if (list_mode) 42529018Sache printf("%s\n",progname); 42610352Sjoerg} 42757527Sjoerg 42857527Sjoerg 42929018Sachevoid add_link(int argc, char **argv) 4302490Sjkh{ 43110352Sjoerg int i; 43257527Sjoerg prog_t *p = find_prog(argv[1]); 43357527Sjoerg 43457527Sjoerg if (p == NULL) { 43510352Sjoerg warnx("%s:%d: no prog %s previously declared, skipping link", 43657527Sjoerg curfilename, linenum, argv[1]); 43710352Sjoerg goterror = 1; 43857527Sjoerg return; 43957527Sjoerg } 44010352Sjoerg 4412490Sjkh for (i = 2; i < argc; i++) { 4422490Sjkh if (list_mode) 44310352Sjoerg printf("%s\n",argv[i]); 44410352Sjoerg 4452490Sjkh add_string(&p->links, argv[i]); 44654622Sbillf } 44710352Sjoerg} 44829018Sache 44929018Sache 45010352Sjoergvoid add_libs(int argc, char **argv) 45110352Sjoerg{ 45210352Sjoerg int i; 453121945Sphk 45410352Sjoerg for(i = 1; i < argc; i++) { 455121945Sphk add_string(&libs, argv[i]); 45657527Sjoerg if ( in_list(&libs_so, argv[i]) ) 457121945Sphk warnx("%s:%d: " 458121945Sphk "library `%s' specified as dynamic earlier", 459121945Sphk curfilename, linenum, argv[i]); 46010352Sjoerg } 461121945Sphk} 46210352Sjoerg 46310352Sjoerg 46478793Sachevoid add_libs_so(int argc, char **argv) 46510352Sjoerg{ 46610352Sjoerg int i; 46710352Sjoerg 46810352Sjoerg for(i = 1; i < argc; i++) { 46957527Sjoerg add_string(&libs_so, argv[i]); 47057527Sjoerg if ( in_list(&libs, argv[i]) ) 47110352Sjoerg warnx("%s:%d: " 47210352Sjoerg "library `%s' specified as static earlier", 47310352Sjoerg curfilename, linenum, argv[i]); 47410352Sjoerg } 4752490Sjkh} 4762490Sjkh 47710352Sjoerg 47810352Sjoergvoid add_buildopts(int argc, char **argv) 4792490Sjkh{ 480121945Sphk int i; 481121945Sphk 482121945Sphk for (i = 1; i < argc; i++) 483121945Sphk add_string(&buildopts, argv[i]); 484121945Sphk} 48510352Sjoerg 48610352Sjoerg 487121945Sphkvoid add_special(int argc, char **argv) 488121945Sphk{ 4892490Sjkh int i; 49010352Sjoerg prog_t *p = find_prog(argv[1]); 49110352Sjoerg 49210352Sjoerg if (p == NULL) { 49310352Sjoerg if (reading_cache) 49410352Sjoerg return; 49510352Sjoerg 49610352Sjoerg warnx("%s:%d: no prog %s previously declared, skipping special", 49710352Sjoerg curfilename, linenum, argv[1]); 49829018Sache goterror = 1; 49910352Sjoerg return; 50010352Sjoerg } 50110352Sjoerg 50210352Sjoerg if (!strcmp(argv[2], "ident")) { 50310352Sjoerg if (argc != 4) 50410352Sjoerg goto argcount; 50510352Sjoerg if ((p->ident = strdup(argv[3])) == NULL) 50610352Sjoerg out_of_memory(); 50710352Sjoerg } else if (!strcmp(argv[2], "srcdir")) { 50810352Sjoerg if (argc != 4) 509147110Sjoerg goto argcount; 51010352Sjoerg if ((p->srcdir = strdup(argv[3])) == NULL) 51110352Sjoerg out_of_memory(); 51210352Sjoerg } else if (!strcmp(argv[2], "objdir")) { 51310352Sjoerg if(argc != 4) 51410352Sjoerg goto argcount; 51510352Sjoerg if((p->objdir = strdup(argv[3])) == NULL) 51610352Sjoerg out_of_memory(); 51710352Sjoerg } else if (!strcmp(argv[2], "objs")) { 51810352Sjoerg p->objs = NULL; 51910352Sjoerg for (i = 3; i < argc; i++) 52010352Sjoerg add_string(&p->objs, argv[i]); 52110352Sjoerg } else if (!strcmp(argv[2], "objpaths")) { 52210352Sjoerg p->objpaths = NULL; 52310352Sjoerg for (i = 3; i < argc; i++) 52410352Sjoerg add_string(&p->objpaths, argv[i]); 52510352Sjoerg } else if (!strcmp(argv[2], "keep")) { 52610352Sjoerg p->keeplist = NULL; 52710352Sjoerg for(i = 3; i < argc; i++) 528147110Sjoerg add_string(&p->keeplist, argv[i]); 52910352Sjoerg } else if (!strcmp(argv[2], "objvar")) { 53010352Sjoerg if(argc != 4) 53110352Sjoerg goto argcount; 53257527Sjoerg if ((p->objvar = strdup(argv[3])) == NULL) 53357527Sjoerg out_of_memory(); 53457527Sjoerg } else if (!strcmp(argv[2], "buildopts")) { 53557527Sjoerg p->buildopts = NULL; 53657527Sjoerg for (i = 3; i < argc; i++) 53757527Sjoerg add_string(&p->buildopts, argv[i]); 53857527Sjoerg } else if (!strcmp(argv[2], "lib")) { 53957527Sjoerg for (i = 3; i < argc; i++) 54057527Sjoerg add_string(&p->libs, argv[i]); 54157527Sjoerg } else { 54257527Sjoerg warnx("%s:%d: bad parameter name `%s', skipping line", 54357527Sjoerg curfilename, linenum, argv[2]); 54457527Sjoerg goterror = 1; 54557527Sjoerg } 54657527Sjoerg return; 54757527Sjoerg 54857527Sjoerg argcount: 54957527Sjoerg warnx("%s:%d: too %s arguments, expected \"special %s %s <string>\"", 55057527Sjoerg curfilename, linenum, argc < 4? "few" : "many", argv[1], argv[2]); 551147110Sjoerg goterror = 1; 55257527Sjoerg} 55357527Sjoerg 55457527Sjoerg 55557527Sjoergprog_t *find_prog(char *str) 55657527Sjoerg{ 55757527Sjoerg prog_t *p; 55857527Sjoerg 55957527Sjoerg for (p = progs; p != NULL; p = p->next) 56057527Sjoerg if (!strcmp(p->name, str)) 56157527Sjoerg return p; 56257527Sjoerg 56357527Sjoerg return NULL; 56457527Sjoerg} 56557527Sjoerg 56657527Sjoerg 56757527Sjoerg/* 56857527Sjoerg * ======================================================================== 56957527Sjoerg * gen_outputs subsystem 57057527Sjoerg * 571147110Sjoerg */ 57257527Sjoerg 57357527Sjoerg/* helper subroutines */ 57457527Sjoerg 57557527Sjoergvoid remove_error_progs(void); 57657527Sjoergvoid fillin_program(prog_t *p); 57757527Sjoergvoid gen_specials_cache(void); 57857527Sjoergvoid gen_output_makefile(void); 57957527Sjoergvoid gen_output_cfile(void); 58057527Sjoerg 58157527Sjoergvoid fillin_program_objs(prog_t *p, char *path); 58257527Sjoergvoid top_makefile_rules(FILE *outmk); 58357527Sjoergvoid prog_makefile_rules(FILE *outmk, prog_t *p); 58457527Sjoergvoid output_strlst(FILE *outf, strlst_t *lst); 585char *genident(char *str); 586char *dir_search(char *progname); 587 588 589void gen_outputs(void) 590{ 591 prog_t *p; 592 593 for (p = progs; p != NULL; p = p->next) 594 fillin_program(p); 595 596 remove_error_progs(); 597 gen_specials_cache(); 598 gen_output_cfile(); 599 gen_output_makefile(); 600 status(""); 601 fprintf(stderr, 602 "Run \"make -f %s\" to build crunched binary.\n", outmkname); 603} 604 605/* 606 * run the makefile for the program to find which objects are necessary 607 */ 608void fillin_program(prog_t *p) 609{ 610 char path[MAXPATHLEN]; 611 char line[MAXLINELEN]; 612 FILE *f; 613 614 snprintf(line, MAXLINELEN, "filling in parms for %s", p->name); 615 status(line); 616 617 if (!p->ident) 618 p->ident = genident(p->name); 619 620 /* look for the source directory if one wasn't specified by a special */ 621 if (!p->srcdir) { 622 p->srcdir = dir_search(p->name); 623 } 624 625 /* Determine the actual srcdir (maybe symlinked). */ 626 if (p->srcdir) { 627 snprintf(line, MAXLINELEN, "cd %s && echo -n `/bin/pwd`", 628 p->srcdir); 629 f = popen(line,"r"); 630 if (!f) 631 errx(1, "Can't execute: %s\n", line); 632 633 path[0] = '\0'; 634 fgets(path, sizeof path, f); 635 if (pclose(f)) 636 errx(1, "Can't execute: %s\n", line); 637 638 if (!*path) 639 errx(1, "Can't perform pwd on: %s\n", p->srcdir); 640 641 p->realsrcdir = strdup(path); 642 } 643 644 /* Unless the option to make object files was specified the 645 * the objects will be built in the source directory unless 646 * an object directory already exists. 647 */ 648 if (!makeobj && !p->objdir && p->srcdir) { 649 snprintf(line, sizeof line, "%s/%s", objprefix, p->realsrcdir); 650 if (is_dir(line)) { 651 if ((p->objdir = strdup(line)) == NULL) 652 out_of_memory(); 653 } else 654 p->objdir = p->realsrcdir; 655 } 656 657 /* 658 * XXX look for a Makefile.{name} in local directory first. 659 * This lets us override the original Makefile. 660 */ 661 snprintf(path, sizeof(path), "Makefile.%s", p->name); 662 if (is_nonempty_file(path)) { 663 snprintf(line, MAXLINELEN, "Using %s for %s", path, p->name); 664 status(line); 665 } else 666 if (p->srcdir) 667 snprintf(path, sizeof(path), "%s/Makefile", p->srcdir); 668 if (!p->objs && p->srcdir && is_nonempty_file(path)) 669 fillin_program_objs(p, path); 670 671 if (!p->srcdir && !p->objdir && verbose) 672 warnx("%s: %s: %s", 673 "warning: could not find source directory", 674 infilename, p->name); 675 if (!p->objs && verbose) 676 warnx("%s: %s: warning: could not find any .o files", 677 infilename, p->name); 678 679 if ((!p->srcdir || !p->objdir) && !p->objs) 680 p->goterror = 1; 681} 682 683void fillin_program_objs(prog_t *p, char *path) 684{ 685 char *obj, *cp; 686 int fd, rc; 687 FILE *f; 688 char *objvar="OBJS"; 689 strlst_t *s; 690 char line[MAXLINELEN]; 691 692 /* discover the objs from the srcdir Makefile */ 693 694 if ((fd = mkstemp(tempfname)) == -1) { 695 perror(tempfname); 696 exit(1); 697 } 698 if ((f = fdopen(fd, "w")) == NULL) { 699 warn("%s", tempfname); 700 goterror = 1; 701 return; 702 } 703 if (p->objvar) 704 objvar = p->objvar; 705 706 /* 707 * XXX include outhdrname (e.g. to contain Make variables) 708 */ 709 if (outhdrname[0] != '\0') 710 fprintf(f, ".include \"%s\"\n", outhdrname); 711 fprintf(f, ".include \"%s\"\n", path); 712 fprintf(f, ".NOTPARALLEL:\n.NO_PARALLEL:\n.POSIX:\n"); 713 if (buildopts) { 714 fprintf(f, "BUILDOPTS+="); 715 output_strlst(f, buildopts); 716 } 717 fprintf(f, ".if defined(PROG)\n"); 718 fprintf(f, "%s?=${PROG}.o\n", objvar); 719 fprintf(f, ".endif\n"); 720 fprintf(f, "loop:\n\t@echo 'OBJS= '${%s}\n", objvar); 721 722 fprintf(f, "crunchgen_objs:\n" 723 "\t@cd %s && make -f %s $(BUILDOPTS) $(%s_OPTS)", 724 p->srcdir, tempfname, p->ident); 725 for (s = p->buildopts; s != NULL; s = s->next) 726 fprintf(f, " %s", s->str); 727 fprintf(f, " loop\n"); 728 729 fclose(f); 730 731 snprintf(line, MAXLINELEN, "cd %s && make -f %s -Q crunchgen_objs", 732 p->srcdir, tempfname); 733 if ((f = popen(line, "r")) == NULL) { 734 warn("submake pipe"); 735 goterror = 1; 736 return; 737 } 738 739 while(fgets(line, MAXLINELEN, f)) { 740 if (strncmp(line, "OBJS= ", 6)) { 741 warnx("make error: %s", line); 742 goterror = 1; 743 continue; 744 } 745 746 cp = line + 6; 747 while (isspace(*cp)) 748 cp++; 749 750 while(*cp) { 751 obj = cp; 752 while (*cp && !isspace(*cp)) 753 cp++; 754 if (*cp) 755 *cp++ = '\0'; 756 add_string(&p->objs, obj); 757 while (isspace(*cp)) 758 cp++; 759 } 760 } 761 762 if ((rc=pclose(f)) != 0) { 763 warnx("make error: make returned %d", rc); 764 goterror = 1; 765 } 766 767 unlink(tempfname); 768} 769 770void remove_error_progs(void) 771{ 772 prog_t *p1, *p2; 773 774 p1 = NULL; p2 = progs; 775 while (p2 != NULL) { 776 if (!p2->goterror) 777 p1 = p2, p2 = p2->next; 778 else { 779 /* delete it from linked list */ 780 warnx("%s: %s: ignoring program because of errors", 781 infilename, p2->name); 782 if (p1) 783 p1->next = p2->next; 784 else 785 progs = p2->next; 786 p2 = p2->next; 787 } 788 } 789} 790 791void gen_specials_cache(void) 792{ 793 FILE *cachef; 794 prog_t *p; 795 char line[MAXLINELEN]; 796 797 snprintf(line, MAXLINELEN, "generating %s", cachename); 798 status(line); 799 800 if ((cachef = fopen(cachename, "w")) == NULL) { 801 warn("%s", cachename); 802 goterror = 1; 803 return; 804 } 805 806 fprintf(cachef, "# %s - parm cache generated from %s by crunchgen " 807 " %s\n\n", 808 cachename, infilename, CRUNCH_VERSION); 809 810 for (p = progs; p != NULL; p = p->next) { 811 fprintf(cachef, "\n"); 812 if (p->srcdir) 813 fprintf(cachef, "special %s srcdir %s\n", 814 p->name, p->srcdir); 815 if (p->objdir) 816 fprintf(cachef, "special %s objdir %s\n", 817 p->name, p->objdir); 818 if (p->objs) { 819 fprintf(cachef, "special %s objs", p->name); 820 output_strlst(cachef, p->objs); 821 } 822 if (p->objpaths) { 823 fprintf(cachef, "special %s objpaths", p->name); 824 output_strlst(cachef, p->objpaths); 825 } 826 } 827 fclose(cachef); 828} 829 830 831void gen_output_makefile(void) 832{ 833 prog_t *p; 834 FILE *outmk; 835 char line[MAXLINELEN]; 836 837 snprintf(line, MAXLINELEN, "generating %s", outmkname); 838 status(line); 839 840 if ((outmk = fopen(outmkname, "w")) == NULL) { 841 warn("%s", outmkname); 842 goterror = 1; 843 return; 844 } 845 846 fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n", 847 outmkname, infilename, CRUNCH_VERSION); 848 849 if (outhdrname[0] != '\0') 850 fprintf(outmk, ".include \"%s\"\n", outhdrname); 851 852 top_makefile_rules(outmk); 853 for (p = progs; p != NULL; p = p->next) 854 prog_makefile_rules(outmk, p); 855 856 fprintf(outmk, "\n# ========\n"); 857 fclose(outmk); 858} 859 860 861void gen_output_cfile(void) 862{ 863 extern char *crunched_skel[]; 864 char **cp; 865 FILE *outcf; 866 prog_t *p; 867 strlst_t *s; 868 char line[MAXLINELEN]; 869 870 snprintf(line, MAXLINELEN, "generating %s", outcfname); 871 status(line); 872 873 if((outcf = fopen(outcfname, "w")) == NULL) { 874 warn("%s", outcfname); 875 goterror = 1; 876 return; 877 } 878 879 fprintf(outcf, 880 "/* %s - generated from %s by crunchgen %s */\n", 881 outcfname, infilename, CRUNCH_VERSION); 882 883 fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname); 884 for (cp = crunched_skel; *cp != NULL; cp++) 885 fprintf(outcf, "%s\n", *cp); 886 887 for (p = progs; p != NULL; p = p->next) 888 fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident); 889 890 fprintf(outcf, "\nstruct stub entry_points[] = {\n"); 891 for (p = progs; p != NULL; p = p->next) { 892 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 893 p->name, p->ident); 894 for (s = p->links; s != NULL; s = s->next) 895 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 896 s->str, p->ident); 897 } 898 899 fprintf(outcf, "\t{ EXECNAME, crunched_main },\n"); 900 fprintf(outcf, "\t{ NULL, NULL }\n};\n"); 901 fclose(outcf); 902} 903 904 905char *genident(char *str) 906{ 907 char *n, *s, *d; 908 909 /* 910 * generates a Makefile/C identifier from a program name, 911 * mapping '-' to '_' and ignoring all other non-identifier 912 * characters. This leads to programs named "foo.bar" and 913 * "foobar" to map to the same identifier. 914 */ 915 916 if ((n = strdup(str)) == NULL) 917 return NULL; 918 for (d = s = n; *s != '\0'; s++) { 919 if (*s == '-') 920 *d++ = '_'; 921 else if (*s == '_' || isalnum(*s)) 922 *d++ = *s; 923 } 924 *d = '\0'; 925 return n; 926} 927 928 929char *dir_search(char *progname) 930{ 931 char path[MAXPATHLEN]; 932 strlst_t *dir; 933 char *srcdir; 934 935 for (dir = srcdirs; dir != NULL; dir = dir->next) { 936 snprintf(path, MAXPATHLEN, "%s/%s", dir->str, progname); 937 if (!is_dir(path)) 938 continue; 939 940 if ((srcdir = strdup(path)) == NULL) 941 out_of_memory(); 942 943 return srcdir; 944 } 945 return NULL; 946} 947 948 949void top_makefile_rules(FILE *outmk) 950{ 951 prog_t *p; 952 953 if ( subtract_strlst(&libs, &libs_so) ) 954 fprintf(outmk, "# NOTE: Some LIBS declarations below overridden by LIBS_SO\n"); 955 956 fprintf(outmk, "LIBS+="); 957 output_strlst(outmk, libs); 958 959 fprintf(outmk, "LIBS_SO+="); 960 output_strlst(outmk, libs_so); 961 962 if (makeobj) { 963 fprintf(outmk, "MAKEOBJDIRPREFIX?=%s\n", objprefix); 964 fprintf(outmk, "MAKEENV=env MAKEOBJDIRPREFIX=$(MAKEOBJDIRPREFIX)\n"); 965 fprintf(outmk, "CRUNCHMAKE=$(MAKEENV) $(MAKE)\n"); 966 } else { 967 fprintf(outmk, "CRUNCHMAKE=$(MAKE)\n"); 968 } 969 970 if (buildopts) { 971 fprintf(outmk, "BUILDOPTS+="); 972 output_strlst(outmk, buildopts); 973 } 974 975 fprintf(outmk, "CRUNCHED_OBJS="); 976 for (p = progs; p != NULL; p = p->next) 977 fprintf(outmk, " %s.lo", p->name); 978 fprintf(outmk, "\n"); 979 980 fprintf(outmk, "SUBMAKE_TARGETS="); 981 for (p = progs; p != NULL; p = p->next) 982 fprintf(outmk, " %s_make", p->ident); 983 fprintf(outmk, "\nSUBCLEAN_TARGETS="); 984 for (p = progs; p != NULL; p = p->next) 985 fprintf(outmk, " %s_clean", p->ident); 986 fprintf(outmk, "\n\n"); 987 988 fprintf(outmk, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n"); 989 fprintf(outmk, "exe: %s\n", execfname); 990 fprintf(outmk, "%s: %s.o $(CRUNCHED_OBJS) $(SUBMAKE_TARGETS)\n", execfname, execfname); 991 fprintf(outmk, ".if defined(LIBS_SO) && !empty(LIBS_SO)\n"); 992 fprintf(outmk, "\t$(CC) -o %s %s.o $(CRUNCHED_OBJS) \\\n", 993 execfname, execfname); 994 fprintf(outmk, "\t\t-Xlinker -Bstatic $(LIBS) \\\n"); 995 fprintf(outmk, "\t\t-Xlinker -Bdynamic $(LIBS_SO)\n"); 996 fprintf(outmk, ".else\n"); 997 fprintf(outmk, "\t$(CC) -static -o %s %s.o $(CRUNCHED_OBJS) $(LIBS)\n", 998 execfname, execfname); 999 fprintf(outmk, ".endif\n"); 1000 fprintf(outmk, "\tstrip %s\n", execfname); 1001 fprintf(outmk, "realclean: clean subclean\n"); 1002 fprintf(outmk, "clean:\n\trm -f %s *.lo *.o *_stub.c\n", execfname); 1003 fprintf(outmk, "subclean: $(SUBCLEAN_TARGETS)\n"); 1004} 1005 1006 1007void prog_makefile_rules(FILE *outmk, prog_t *p) 1008{ 1009 strlst_t *lst; 1010 1011 fprintf(outmk, "\n# -------- %s\n\n", p->name); 1012 1013 fprintf(outmk, "%s_OBJDIR=", p->ident); 1014 if (p->objdir) 1015 fprintf(outmk, "%s", p->objdir); 1016 else 1017 fprintf(outmk, "$(MAKEOBJDIRPREFIX)/$(%s_REALSRCDIR)\n", 1018 p->ident); 1019 fprintf(outmk, "\n"); 1020 1021 fprintf(outmk, "%s_OBJPATHS=", p->ident); 1022 if (p->objpaths) 1023 output_strlst(outmk, p->objpaths); 1024 else { 1025 for (lst = p->objs; lst != NULL; lst = lst->next) { 1026 fprintf(outmk, " $(%s_OBJDIR)/%s", p->ident, lst->str); 1027 } 1028 fprintf(outmk, "\n"); 1029 } 1030 1031 if (p->srcdir && p->objs) { 1032 fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir); 1033 fprintf(outmk, "%s_REALSRCDIR=%s\n", p->ident, p->realsrcdir); 1034 1035 fprintf(outmk, "%s_OBJS=", p->ident); 1036 output_strlst(outmk, p->objs); 1037 if (p->buildopts != NULL) { 1038 fprintf(outmk, "%s_OPTS+=", p->ident); 1039 output_strlst(outmk, p->buildopts); 1040 } 1041#if 0 1042 fprintf(outmk, "$(%s_OBJPATHS): %s_make\n\n", p->ident, p->ident); 1043#endif 1044 fprintf(outmk, "%s_make:\n", p->ident); 1045 fprintf(outmk, "\t(cd $(%s_SRCDIR) && ", p->ident); 1046 if (makeobj) 1047 fprintf(outmk, "$(CRUNCHMAKE) obj && "); 1048 fprintf(outmk, "\\\n"); 1049 fprintf(outmk, "\t\t$(CRUNCHMAKE) $(BUILDOPTS) $(%s_OPTS) depend &&", 1050 p->ident); 1051 fprintf(outmk, "\\\n"); 1052 fprintf(outmk, "\t\t$(CRUNCHMAKE) $(BUILDOPTS) $(%s_OPTS) " 1053 "$(%s_OBJS))", 1054 p->ident, p->ident); 1055 fprintf(outmk, "\n"); 1056 fprintf(outmk, "%s_clean:\n", p->ident); 1057 fprintf(outmk, "\t(cd $(%s_SRCDIR) && $(CRUNCHMAKE) $(BUILDOPTS) clean cleandepend)\n\n", 1058 p->ident); 1059 } else { 1060 fprintf(outmk, "%s_make:\n", p->ident); 1061 fprintf(outmk, "\t@echo \"** cannot make objs for %s\"\n\n", 1062 p->name); 1063 } 1064 1065 if (p->libs) { 1066 fprintf(outmk, "%s_LIBS=", p->ident); 1067 output_strlst(outmk, p->libs); 1068 } 1069 1070 fprintf(outmk, "%s_stub.c:\n", p->name); 1071 fprintf(outmk, "\techo \"" 1072 "int _crunched_%s_stub(int argc, char **argv, char **envp)" 1073 "{return main(argc,argv,envp);}\" >%s_stub.c\n", 1074 p->ident, p->name); 1075 fprintf(outmk, "%s.lo: %s_stub.o $(%s_OBJPATHS)", 1076 p->name, p->name, p->ident); 1077 if (p->libs) 1078 fprintf(outmk, " $(%s_LIBS)", p->ident); 1079 1080 fprintf(outmk, "\n"); 1081 fprintf(outmk, "\tld -dc -r -o %s.lo %s_stub.o $(%s_OBJPATHS)", 1082 p->name, p->name, p->ident); 1083 if (p->libs) 1084 fprintf(outmk, " $(%s_LIBS)", p->ident); 1085 fprintf(outmk, "\n"); 1086 fprintf(outmk, "\tcrunchide -k _crunched_%s_stub ", p->ident); 1087 for (lst = p->keeplist; lst != NULL; lst = lst->next) 1088 fprintf(outmk, "-k _%s ", lst->str); 1089 fprintf(outmk, "%s.lo\n", p->name); 1090} 1091 1092void output_strlst(FILE *outf, strlst_t *lst) 1093{ 1094 for (; lst != NULL; lst = lst->next) 1095 if ( strlen(lst->str) ) 1096 fprintf(outf, " %s", lst->str); 1097 fprintf(outf, "\n"); 1098} 1099 1100 1101/* 1102 * ======================================================================== 1103 * general library routines 1104 * 1105 */ 1106 1107void status(char *str) 1108{ 1109 static int lastlen = 0; 1110 int len, spaces; 1111 1112 if (!verbose) 1113 return; 1114 1115 len = strlen(str); 1116 spaces = lastlen - len; 1117 if (spaces < 1) 1118 spaces = 1; 1119 1120 fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " "); 1121 fflush(stderr); 1122 lastlen = len; 1123} 1124 1125 1126void out_of_memory(void) 1127{ 1128 err(1, "%s: %d: out of memory, stopping", infilename, linenum); 1129} 1130 1131 1132void add_string(strlst_t **listp, char *str) 1133{ 1134 strlst_t *p1, *p2; 1135 1136 /* add to end, but be smart about dups */ 1137 1138 for (p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next) 1139 if (!strcmp(p2->str, str)) 1140 return; 1141 1142 p2 = malloc(sizeof(strlst_t)); 1143 if (p2) { 1144 p2->next = NULL; 1145 p2->str = strdup(str); 1146 } 1147 if (!p2 || !p2->str) 1148 out_of_memory(); 1149 1150 if (p1 == NULL) 1151 *listp = p2; 1152 else 1153 p1->next = p2; 1154} 1155 1156int subtract_strlst(strlst_t **lista, strlst_t **listb) 1157{ 1158 int subtract_count = 0; 1159 strlst_t *p1; 1160 for (p1 = *listb; p1 != NULL; p1 = p1->next) 1161 if ( in_list(lista, p1->str) ) { 1162 warnx("Will compile library `%s' dynamically", p1->str); 1163 strcat(p1->str, ""); 1164 subtract_count++; 1165 } 1166 return subtract_count; 1167} 1168 1169int in_list(strlst_t **listp, char *str) 1170{ 1171 strlst_t *p1; 1172 for (p1 = *listp; p1 != NULL; p1 = p1->next) 1173 if (!strcmp(p1->str, str)) 1174 return 1; 1175 return 0; 1176} 1177 1178int is_dir(char *pathname) 1179{ 1180 struct stat buf; 1181 1182 if (stat(pathname, &buf) == -1) 1183 return 0; 1184 1185 return S_ISDIR(buf.st_mode); 1186} 1187 1188int is_nonempty_file(char *pathname) 1189{ 1190 struct stat buf; 1191 1192 if (stat(pathname, &buf) == -1) 1193 return 0; 1194 1195 return S_ISREG(buf.st_mode) && buf.st_size > 0; 1196} 1197