scc

simple c99 compiler
git clone git://git.simple-cc.org/scc
Log | Files | Refs | Submodules | README | LICENSE

commit 245b0baa3192753a724b2529b84a0716a9892bbf
parent 8f382566b9ee9d121277e4c45708bbd40c45a98b
Author: Roberto E. Vargas Caballero <k0ga@shike2.com>
Date:   Tue, 16 Jan 2024 21:56:37 +0100

make: Initial version

This is the first version of make that can help
with the portability of the build system. More
work and testing is required and expected in the
coming days.

Diffstat:
Msrc/cmd/Makefile | 1+
Asrc/cmd/make/Makefile | 20++++++++++++++++++++
Asrc/cmd/make/defaults.c | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/cmd/make/main.c | 328+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/cmd/make/make.h | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/cmd/make/parser.c | 815+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/cmd/make/posix.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/cmd/make/rules.c | 507+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 1844 insertions(+), 0 deletions(-)

diff --git a/src/cmd/Makefile b/src/cmd/Makefile @@ -5,6 +5,7 @@ DIRS =\ ld\ as\ cc\ + make\ PROJECTDIR = ../.. include $(PROJECTDIR)/scripts/rules.mk diff --git a/src/cmd/make/Makefile b/src/cmd/make/Makefile @@ -0,0 +1,20 @@ +.POSIX: + +PROJECTDIR = ../../.. +include $(PROJECTDIR)/scripts/rules.mk + +OBJS =\ + defaults.o\ + main.o\ + parser.o\ + rules.o\ + $(DRIVER).o\ + +TARGET = $(BINDIR)/scc-make + +all: $(TARGET) + +$(BINDIR)/scc-make: $(OBJS) + $(CC) $(PROJ_LDFLAGS) $(OBJS) $(PROJ_LDLIBS) -o $@ + +$(OBJS): make.h diff --git a/src/cmd/make/defaults.c b/src/cmd/make/defaults.c @@ -0,0 +1,61 @@ +char defaults[] = + ".SUFFIXES: .o .c .y .l .a .sh .f\n" + + "AR = ar\n" + "ARFLAGS = -rv\n" + "CC = c99\n" + "CFLAGS = -O\n" + "FC = fort77\n" + "FFLAGS = -O 1\n" + "LDFLAGS =\n" + "LEX = lex\n" + "LFLAGS =\n" + "YACC = yacc\n" + "YFLAGS =\n" + "SHELL = /bin/sh\n" + + ".c:\n" + "\t${CC} ${CFLAGS} ${LDFLAGS} -o $@ $<\n" + + ".f:\n" + "\t${FC} ${FFLAGS} ${LDFLAGS} -o $@ $<\n" + + ".sh:\n" + "\tcp $< $@\n" + "\tchmod a+x $@\n" + + ".c.o:\n" + "\t${CC} ${CFLAGS} -c $<\n" + + ".f.o:\n" + "\t${FC} ${FFLAGS} -c $<\n" + + ".y.o:\n" + "\t${YACC} ${YFLAGS} $<\n" + "\t${CC} ${CFLAGS} -c y.tab.c\n" + "\trm -f y.tab.c\n" + "\tmv y.tab.o $@\n" + + ".l.o:\n" + "\t${LEX} ${LFLAGS} $<\n" + "\t${CC} ${CFLAGS} -c lex.yy.c\n" + "\trm -f lex.yy.c\n" + "\tmv lex.yy.o $@\n" + + ".y.c:\n" + "\t${YACC} ${YFLAGS} $<\n" + "\tmv y.tab.c $@\n" + + ".l.c:\n" + "\t${LEX} ${LFLAGS} $<\n" + "\tmv lex.yy.c $@\n" + + ".c.a:\n" + "\t${CC} -c ${CFLAGS} $<\n" + "\t${AR} ${ARFLAGS} $@ $*.o\n" + "\trm -f $*.o\n" + + ".f.a:\n" + "\t${FC} -c ${FFLAGS} $<\n" + "\t${AR} ${ARFLAGS} $@ $*.o\n" + "\trm -f $*.o\n"; diff --git a/src/cmd/make/main.c b/src/cmd/make/main.c @@ -0,0 +1,328 @@ +#include <errno.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "make.h" + +#ifndef SIGINT +#define SIGINT -1 +#endif + +#ifndef SIGTERM +#define SIGTERM -1 +#endif + +#ifndef SIGQUIT +#define SIGQUIT -1 +#endif + +#ifndef SIGHUP +#define SIGHUP -1 +#endif + +int kflag, dflag, nflag, iflag, sflag; +int eflag, pflag, tflag; +int exitstatus; +sig_atomic_t stop; + +static int hasmake; + +void +debug(char *fmt, ...) +{ + va_list va; + + if (!dflag) + return; + + va_start(va, fmt); + vfprintf(stdout, fmt, va); + fputc('\n', stdout); + va_end(va); +} + +int +hash(char *name) +{ + int c; + unsigned h = 5381; + + while (c = *name++) + h = h*33 ^ c; + + return h; +} + +void * +emalloc(size_t siz) +{ + void *p; + + if ((p = malloc(siz)) == NULL) { + perror("make"); + exit(EXIT_FAILURE); + } + + return p; +} + +void * +erealloc(void *p, size_t siz) +{ + if ((p = realloc(p, siz)) == NULL) { + perror("make"); + exit(EXIT_FAILURE); + } + + return p; +} + +char * +estrdup(char *s) +{ + size_t len; + + len = strlen(s) + 1; + return memcpy(emalloc(len), s, len); +} + +static void +sighandler(int signo) +{ + stop = signo; +} + +static void +parsedefault(void) +{ + if (parse("makefile")) + return; + if (parse("Makefile")) + return; +} + +static void +usage(void) +{ + fputs("usage: make [-eiknprSstd] [-f file] [-j jobs] " + "[macro=value ...] [target ...]\n", + stderr); + exit(EXIT_FAILURE); +} + +static char * +getarg(char **args, char ***argv) +{ + char *s; + + if ((*args)[1]) { + s = (*args) + 1; + *args += strlen(s) - 1; + return s; + } + + if (!argv) + usage(); + + if ((*argv)[1] == NULL) + usage(); + (*argv)++; + + return **argv; +} + +static void +appendmakeflags(char *text) +{ + int n; + char *s, *t; + + s = getmacro("MAKEFLAGS"); + n = snprintf(NULL, 0, "%s %s", s, text); + + t = emalloc(n+1); + snprintf(t, n+1, "%s %s", s, text); + setmacro("MAKEFLAGS", t, EXPORT); + + free(t); +} + +static void +parseflag(int flag, char **args, char ***argv) +{ + char *arg; + + switch (flag) { + case 'f': + arg = getarg(args, argv); + if (strcmp(arg, "-") == 0) + arg = NULL; + if (!parse(arg)) + error("%s: %s", arg, strerror(errno)); + hasmake = 1; + break; + case 'e': + eflag = 1; + appendmakeflags("-e"); + break; + case 'i': + iflag = 1; + appendmakeflags("-i"); + break; + case 'k': + kflag = 1; + appendmakeflags("-k"); + break; + case 'n': + nflag = 1; + appendmakeflags("-n"); + break; + case 'p': + pflag = 1; + break; + case 'r': + addtarget(".SUFFIXES", 0); + appendmakeflags("-r"); + break; + case 'S': + kflag = 0; + appendmakeflags("-S"); + break; + case 's': + sflag = 1; + appendmakeflags("-s"); + break; + case 't': + tflag = 1; + appendmakeflags("-t"); + break; + case 'd': + dflag = 1; + appendmakeflags("-d"); + break; + case 'j': + default: + usage(); + } +} + +static int +assign(char *s, int export) +{ + int pos; + char *t; + + if ((t = strchr(s, '=')) == NULL) + return 0; + + pos = t - s; + + appendmakeflags(s); + t = estrdup(s); + t[pos] = '\0'; + + setmacro(t, t+pos+1, export); + free(t); +} + +static void +parseargv(char ***argv, int export) +{ + char *s; + + for ( ; **argv; ++*argv) { + s = **argv; + if (s[0] != '-') { + if (!assign(s, export)) + break; + continue; + } + while (*++s) + parseflag(*s, &s, argv); + } +} + + + +static void +parsemakeflags(void) +{ + size_t len1, len2, n; + char *s, *t, **oargv, **argv, *flags; + + setmacro("MAKEFLAGS", "", EXPORT); + + if ((flags = getenv("MAKEFLAGS")) == NULL) + return; + + while (*flags == ' ' || *flags == '\t') + flags++; + + if (flags[0] != '-') { + while (*flags) { + parseflag(*flags, &flags, NULL); + flags++; + } + } else { + argv = emalloc(sizeof(char *) * 2); + argv[0] = flags; + argv[1] = NULL; + + s = flags; + for (n = 2; ; ++s) { + len1 = strcspn(s, " \t"); + if (s[len1] == '\0') + break; + len2 = strspn(s+len1, " \t"); + s[len1] = '\0'; + s += len1 + len2; + + argv = erealloc(argv, sizeof(char *) * (n+1)); + argv[n-1] = s; + argv[n] = NULL; + } + + oargv = argv; + parseargv(&argv, NOEXPORT); + if (*argv != NULL) + error("invalid MAKEFLAGS variable"); + free(oargv); + } +} + +int +main(int argc, char *argv[]) +{ + signal(SIGINT, sighandler); + signal(SIGHUP, sighandler); + signal(SIGTERM, sighandler); + signal(SIGQUIT, sighandler); + + inject(defaults); + parsemakeflags(); + + ++argv; + parseargv(&argv, EXPORT); + + if (!hasmake) + parsedefault(); + + if (pflag) { + dumpmacros(); + dumprules(); + } + + if (!*argv) { + build(NULL); + } else { + while (*argv) + build(*argv++); + } + + exit(exitstatus); + + return 0; +} diff --git a/src/cmd/make/make.h b/src/cmd/make/make.h @@ -0,0 +1,64 @@ +#include <stddef.h> +#include <time.h> + +typedef struct target Target; + +enum { + NOEXPORT, + EXPORT, +}; + +struct target { + char *name; + char *target; + char *req; + time_t stamp; + int defined; + + int ndeps; + struct target **deps; + + int nactions; + char **actions; + + struct target *next; +}; + +extern void *emalloc(size_t); +extern void *erealloc(void *, size_t); +extern char *estrdup(char *); + +extern void dumprules(void); +extern void dumpmacros(void); + +extern char *expandstring(char *, Target *); +extern void addtarget(char *, int); +extern void inject(char *); +extern int build(char *); +extern int hash(char *); +extern int parse(char *); +extern void debug(char *, ...); +extern void error(char *, ...); +extern void warning(char *, ...); +extern void adddep(char *, char *); +extern void addrule(char *, char **, int); + +extern char *getmacro(char *); +extern void setmacro(char *, char *, int); + +/* system depdendant */ +extern time_t stamp(char *); +extern int launch(char *, int); +extern int putenv(char *); + +/* main.c */ +extern int kflag, dflag, nflag, iflag, sflag; +extern int eflag, pflag, tflag; +extern int exitstatus; + +#ifdef SIGABRT +extern sig_atomic_t stop; +#endif + +/* defaults.c */ +extern char defaults[]; diff --git a/src/cmd/make/parser.c b/src/cmd/make/parser.c @@ -0,0 +1,815 @@ +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "make.h" + +#define MAXREPL 30 +#define TABSIZ 64 +#define MAXTOKEN 256 +#define ITEM 128 + +typedef struct macro Macro; + +enum inputype { + FTFILE, + FTEXPAN, +}; + +struct loc { + char *fname; + int lineno; +}; + +struct input { + int siz; + int type; + + FILE *fp; + struct loc loc; + + int pos; + char *buf; + + struct input *prev; +}; + +struct macro { + char *name; + char *value; + + struct macro *next; +}; + +static struct input *input; +static char token[MAXTOKEN]; +static int tok; +static Macro *htab[TABSIZ]; + +void +dumpmacros(void) +{ + Macro **pp, *p; + + for (pp = htab; pp < &htab[TABSIZ]; ++pp) { + for (p = *pp; p; p = p->next) + printf("%s = %s\n", p->name, p->value); + } +} + +static Macro * +lookup(char *name) +{ + Macro *mp; + int h = hash(name) & TABSIZ-1; + + for (mp = htab[h]; mp && strcmp(mp->name, name); mp = mp->next) + ; + + if (mp) + return mp; + + mp = emalloc(sizeof(*mp)); + mp->name = estrdup(name); + mp->value = NULL; + mp->next = htab[h]; + htab[h] = mp; + + return mp; +} + +void +setmacro(char *name, char *val, int export) +{ + int n; + char *buf; + Macro *mp; + + mp = lookup(name); + free(mp->value); + mp->value = estrdup(val); + + if (export && strcmp(name, "SHELL") != 0) { + n = snprintf(NULL, 0, "%s=%s", name, val); + buf = emalloc(n+1); + snprintf(buf, n+1, "%s=%s", name, val); + putenv(buf); + } +} + +char * +getmacro(char *name) +{ + int hide; + char *s, *t; + Macro *mp = lookup(name); + + hide = 0; + if (!strcmp(name, "SHELL") || !strcmp(name, "MAKEFLAGS")) + hide = 1; + + s = mp->value; + if (!s && !hide) + s = getenv(name); + if (!s) + s = ""; + + if (eflag && !hide) { + t = getenv(name); + if (t) + s = t; + } + + return s; +} + +static struct loc * +getloc(void) +{ + struct input *ip; + + for (ip = input; ip && ip->type != FTFILE; ip = ip->prev) + ; + if (!ip) + return NULL; + + return &ip->loc; +} + + +void +error(char *fmt, ...) +{ + va_list va; + struct loc *loc; + + fprintf(stderr, "make: error: "); + if ((loc = getloc()) != NULL) + fprintf(stderr, "%s:%d: ", loc->fname, loc->lineno); + + va_start(va, fmt); + vfprintf(stderr, fmt, va); + va_end(va); + putc('\n', stderr); + + exit(EXIT_FAILURE); +} + +void +warning(char *fmt, ...) +{ + va_list va; + struct loc *loc; + + fprintf(stderr, "make: warning: "); + if ((loc = getloc()) != NULL) + fprintf(stderr, "%s:%d: ", loc->fname, loc->lineno); + + va_start(va, fmt); + vfprintf(stderr, fmt, va); + va_end(va); + putc('\n', stderr); +} + +static void +pop(void) +{ + struct input *ip = input->prev; + + if (input->type == FTFILE) { + fclose(input->fp); + free(input->loc.fname); + } + free(input->buf); + free(input); + + input = ip; +} + +static void +push(int type, ...) +{ + int len, pos; + FILE *fp = NULL; + char *buf, *s, *fname = NULL; + va_list va; + struct input *ip; + + va_start(va, type); + switch (type) { + case FTFILE: + s = va_arg(va, char *); + fp = va_arg(va, FILE *); + fname = estrdup(s); + buf = emalloc(BUFSIZ); + pos = len = BUFSIZ; + break; + case FTEXPAN: + s = va_arg(va, char *); + buf = estrdup(s); + pos = 0; + len = strlen(s); + break; + } + va_end(va); + + ip = emalloc(sizeof(*ip)); + ip->siz = len; + ip->buf = buf; + ip->type = type; + ip->fp = fp; + ip->loc.fname = fname; + ip->loc.lineno = 1; + ip->pos = pos; + ip->prev = input; + + input = ip; +} + +static char * +trim(char *s) +{ + size_t len; + + while (isspace(*s)) + s++; + + for (len = strlen(s); len > 0 && isspace(s[len-1]); --len) + s[len-1] = '\0'; + + return s; +} + +static void +include(char *s) +{ + int len; + FILE *fp; + char *fil, *t; + + s = trim(s); + fil = expandstring(s, NULL); + + t = trim(fil); + if (strlen(t) != 0) { + debug("including '%s'", t); + if ((fp = fopen(t, "r")) == NULL) + error("opening %s:%s", t, strerror(errno)); + push(FTFILE, t, fp); + } + +end: + free(fil); +} + +static char * +nextline(void) +{ + char *s, *lim; + int c, comment; + FILE *fp = input->fp; + + assert(input->type == FTFILE); + +repeat: + comment = 0; + if (feof(fp)) + return NULL; + + lim = &input->buf[input->siz]; + for (s = input->buf; s < lim; ) { + c = getc(fp); + if (c == '\n' || c == EOF) { + input->loc.lineno++; + *s++ = c; + break; + } + if (c == '#') + comment = 1; + if (c > UCHAR_MAX || c < 0) + error("invalid character '%c' (%d)", c, c); + if (!comment) + *s++ = c; + } + + + if (s == lim) + error("too long line"); + if (ferror(fp)) + error(strerror(errno)); + *s = '\0'; + + if (!strcmp(input->buf, "")) + goto repeat; + + if (strncmp(input->buf, "include", 7) == 0) { + input->pos = input->siz; + include(input->buf+7); + goto repeat; + } + + input->pos = 0; + + + return input->buf; +} + +static int +empty(struct input *ip) +{ + return ip->pos == ip->siz || ip->buf[ip->pos] == '\0'; +} + +static int +moreinput(void) +{ + while (input) { + if (!empty(input)) + break; + + switch (input->type) { + case FTEXPAN: + pop(); + break; + case FTFILE: + if (!nextline()) + pop(); + break; + } + } + + return input != NULL; +} + +static int +nextc(void) +{ + if (!moreinput()) + return EOF; + + return input->buf[input->pos++]; +} + +static int +back(int c) +{ + if (c == EOF) + return c; + input->buf[--input->pos] = c; +} + +static void +skipspaces(void) +{ + int c; + + for (c = nextc(); c == ' ' || c == '\t'; c = nextc()) + ; + back(c); +} + +static int +validchar(int c) +{ + if (c == EOF) + return 0; + return c == '.' || c == '/' || c == '_' || c == '-' || isalnum(c); +} + +static int +item(void) +{ + int c; + char *s; + + for (s = token; s < &token[MAXTOKEN] - 1; *s++ = c) { + c = nextc(); + if (!validchar(c)) + break; + } + + if (s >= &token[MAXTOKEN] - 1) + error("token too long"); + if (s == token) + error("invalid empty token"); + *s = '\0'; + back(c); + + return ITEM; +} + +static void +expandmacro(char *name, char *repl, char *to) +{ + int pos, siz, replsiz, tosiz; + char *s, *t, *p, *buf; + + s = expandstring(getmacro(name), NULL); + + pos = 0; + buf = NULL; + tosiz = strlen(to); + replsiz = strlen(repl); + + t = s; + for (pos = 0; *t; pos += siz) { + if (replsiz > 0 && strncmp(t, repl, replsiz) == 0) { + siz = tosiz; + p = to; + t += replsiz; + } else { + siz = 1; + p = t; + t++; + } + + buf = erealloc(buf, pos + siz + 1); + memcpy(buf+pos, p, siz); + } + + if (pos > 0) { + buf[pos] = '\0'; + push(FTEXPAN, buf); + free(buf); + } + free(s); +} + +static void +expandsimple(Target *tp) +{ + char *s; + Target **p; + int len, c; + + switch (c = nextc()) { + case '@': + if (!tp || !tp->target) + return; + push(FTEXPAN, tp->target); + break; + case '<': + if (!tp || !tp->req) + return; + push(FTEXPAN, tp->req); + break; + case '*': + if (!tp || !tp->target) + return; + s = strrchr(tp->target, '.'); + if (!s) { + push(FTEXPAN, tp->target); + return; + } + + len = s - tp->target; + s = emalloc(len+1); + memcpy(s, tp->target, len); + s[len] = '\0'; + push(FTEXPAN, s); + free(s); + break; + case '?': + if (!tp) + return; + + if (tp->req && stamp(tp->req) > tp->stamp) + push(FTEXPAN, tp->req); + + for (p = tp->deps; p && *p; ++p) { + if (stamp((*p)->name) > tp->stamp) + push(FTEXPAN, (*p)->name); + } + break; + default: + token[0] = c; + token[1] = '\0'; + expandmacro(token, NULL, NULL); + break; + } +} + +static void +expansion(Target *tp) +{ + int delim, c, repli, toi, namei, st; + char name[MAXTOKEN], repl[MAXREPL], to[MAXREPL]; + + c = nextc(); + if (c == '(') + delim = ')'; + else if (c == '{') + delim = '}'; + else + delim = 0; + + if (!delim) { + back(c); + expandsimple(tp); + } else { + st = namei = repli = toi = 0; + while ((c = nextc()) != EOF) { + if (c == delim) + break; + + switch (st) { + case 0: + if (c == ':') { + st = 1; + continue; + } + if (!validchar(c)) + error("invalid macro name in expansion"); + if (namei == MAXTOKEN-1) + error("expansion text too long"); + name[namei++] = c; + break; + case 1: + if (c == '=') { + st = 2; + continue; + } + if (repli == MAXREPL-1) + error("macro replacement too big"); + repl[repli++] = c; + break; + case 2: + if (toi == MAXREPL-1) + error("macro substiturion too big"); + to[toi++] = c; + break; + } + } + + if (c == EOF) + error("found eof while parsing expansion"); + if (st > 0 && (namei == 0 || repli == 0 || to == 0)) + error("invalid macro expansion"); + + name[namei] = '\0'; + repl[repli] = '\0'; + to[toi] = '\0'; + expandmacro(name, repl, to); + } +} + +/* + * Horrible hack to do string expansion. + * We cannot use normal push and nextc because that + * would consume characters of the current file too. + * For that reason it cleans the input and it recovers + * it later. + */ +char * +expandstring(char *line, Target *tp) +{ + int c, n; + char *s; + struct input *ip = input; + + input = NULL; + push(FTEXPAN, line); + + n = 0; + s = NULL; + while ((c = nextc()) != EOF) { + if (c == '$') { + if ((c = nextc()) != '$') { + back(c); + expansion(tp); + continue; + } + } + + s = erealloc(s, ++n); + s[n-1] = c; + } + + s = erealloc(s, n+1); + s[n] = '\0'; + input = ip; + + return s; +} + +static int +next(void) +{ + int c; + +repeat: + skipspaces(); + + switch (c = nextc()) { + case EOF: + strcpy(token, "<EOF>"); + return tok = EOF; + case '$': + if ((c = nextc()) == '$') + goto single; + back(c); + expansion(NULL); + goto repeat; + case ';': + case ':': + case '=': + case '\n': + single: + token[0] = c; + token[1] = '\0'; + return tok = c; + default: + back(c); + return tok = item(); + } +} + +static char * +getln(void) +{ + int n, c; + char *line; + + n = 0; + line = NULL; + while ((c = nextc()) != EOF) { + line = erealloc(line, n+1); + if (c == '\n') + break; + if (c == '\\') { + if ((c = nextc()) != '\n') { + back(c); + c = '\\'; + } else { + skipspaces(); + c = ' '; + } + } + + line[n++] = c; + } + if (c == EOF) + error("EOF while looking for end of line"); + line[n] = '\0'; + + return line; +} + +static char * +getcmd(void) +{ + int n, c; + char *line; + + n = 0; + line = NULL; + while ((c = nextc()) != EOF) { + line = erealloc(line, n+1); + if (c == '\n') + break; + if (c == '\\') { + if ((c = nextc()) == '\n') + continue; + back(c); + c = '\\'; + } + line[n++] = c; + } + if (c == EOF) + error("EOF while looking for end of command"); + line[n] = '\0'; + + return line; +} + +static void +rule(char *targets[], int ntargets) +{ + int c, i, j, ndeps, nactions; + char **actions, **deps = NULL; + + if (ntargets == 0) + error("missing target"); + + for (ndeps = 0; next() == ITEM; ++ndeps) { + deps = erealloc(deps, (ndeps+1) * sizeof(char *)); + deps[ndeps] = estrdup(token); + } + + if (tok != '\n' && tok != ';') + error("garbage at the end of the line"); + + nactions = 0; + actions = NULL; + if (tok == ';') { + nactions++; + actions = erealloc(actions, nactions * sizeof(char *)); + actions[nactions-1] = getcmd(); + } + + while ((c = nextc()) == '\t') { + nactions++; + actions = erealloc(actions, nactions * sizeof(char *)); + actions[nactions-1] = getcmd(); + } + back(c); + + for (i = 0; i < ntargets; i++) { + addtarget(targets[i], ndeps); + for (j = 0; j < ndeps; j++) + adddep(targets[i], deps[j]); + if (nactions > 0) + addrule(targets[i], actions, nactions); + } + + for (i = 0; i < ndeps; i++) + free(deps[i]); + free(deps); + + for (i = 0; i < nactions; i++) + free(actions[i]); + free(actions); +} + +static void +assign(char *macros[], int n) +{ + int len, c; + char *defs; + + if (n != 1) + error("invalid macro definition"); + + skipspaces(); + defs = getln(); + setmacro(*macros, defs, NOEXPORT); + free(defs); +} + +void +parseinput(void) +{ + int i, n; + char **targets; + + while (moreinput()) { + n = 0; + targets = NULL; + + next(); + if (tok == '\n' || tok == EOF) + continue; + + while (tok == ITEM) { + n++; + targets = erealloc(targets, n * sizeof(char *)); + targets[n-1] = estrdup(token); + next(); + } + + switch (tok) { + case ':': + rule(targets, n); + break; + case '=': + assign(targets, n); + break; + default: + error("unexpected token '%s'(%d)", token, tok); + } + + for (i = 0; i < n; i++) + free(targets[i]); + free(targets); + } +} + +int +parse(char *fname) +{ + FILE *fp; + + if (!fname) { + fp = stdin; + fname = "<stdin>"; + } else if ((fp = fopen(fname, "r")) == NULL) { + return 0; + } + + push(FTFILE, fname, fp); + parseinput(); + + return 1; +} + +void +inject(char *s) +{ + push(FTEXPAN, s); + parseinput(); +} diff --git a/src/cmd/make/posix.c b/src/cmd/make/posix.c @@ -0,0 +1,48 @@ +#include <sys/stat.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <errno.h> +#include <string.h> + +#include "make.h" + +time_t +stamp(char *name) +{ + struct stat st; + + if (stat(name, &st) < 0) + return -1; + + return st.st_mtime; +} + +int +launch(char *cmd, int ignore) +{ + int st; + pid_t pid; + char *name, *shell; + char *args[] = {NULL, "-ec" , cmd, (char *) 0}; + extern char **environ; + + switch (pid = fork()) { + case -1: + return -1; + case 0: + shell = getmacro("SHELL"); + + if (!ignore) + args[1] = "-c"; + if ((name = strrchr(shell, '/')) == NULL) + name = shell; + args[0] = name; + execve(shell, args, environ); + _exit(127); + default: + while (waitpid(pid, &st, 0) < 0 && errno == EINTR) + ; + return st; + } +} diff --git a/src/cmd/make/rules.c b/src/cmd/make/rules.c @@ -0,0 +1,507 @@ +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "make.h" + +#define TABSIZ 128 + +static Target *htab[TABSIZ], *deftarget; + +void +dumprules(void) +{ + int i; + Target **pp, **q, *p; + + for (pp = htab; pp < &htab[TABSIZ]; ++pp) { + for (p = *pp; p; p = p->next) { + if (!p->defined) + continue; + printf("%s:", p->name); + for (q = p->deps; q && *q; ++q) + printf(" %s", (*q)->name); + putchar('\n'); + for (i = 0; i < p->nactions; i++) + printf("\t%s\n", p->actions[i]); + putchar('\n'); + } + } +} + +static Target * +lookup(char *name) +{ + Target *tp; + int h = hash(name) & TABSIZ-1; + + for (tp = htab[h]; tp && strcmp(tp->name, name); tp = tp->next) + ; + + if (tp) + return tp; + + tp = emalloc(sizeof(*tp)); + tp->name = estrdup(name); + tp->target = tp->name; + tp->req = NULL; + tp->stamp = stamp(name); + tp->ndeps = 0; + tp->deps = NULL; + tp->actions = NULL; + tp->nactions = 0; + tp->next = htab[h]; + tp->defined = 0; + htab[h] = tp; + + return tp; +} + +static int +depends(char *target, char *dep) +{ + int i; + Target **p, *tp = lookup(target); + + for (p = tp->deps; p && *p; ++p) { + if (strcmp((*p)->name, target) == 0) + return 1; + } + + return 0; +} + +static int +is_suffix(char *s) +{ + int n; + + if (s[0] != '.') + return 0; + + for (n = 0; s = strchr(s, '.'); n++) + s++; + + return n == 2; +} + +void +addtarget(char *target, int ndeps) +{ + Target *tp = lookup(target); + + tp->defined = 1; + if (!deftarget && target[0] != '.') + deftarget = tp; + + if (strcmp(target, ".SUFFIXES") == 0 && ndeps == 0) { + free(tp->deps); + tp->deps = NULL; + tp->ndeps = 0; + } + + if (strcmp(target, ".DEFAULT") == 0) { + if (ndeps > 0) + error("DEFAULT rule with prerequisites"); + } + + if (strcmp(target, ".SILENT") == 0 && ndeps == 0) + sflag = 1; + + if (strcmp(target, ".IGNORE") == 0 && ndeps == 0) + iflag = 1; +} + +void +adddep(char *target, char *dep) +{ + int i; + size_t siz; + Target **p, *tp = lookup(target); + + if (depends(dep, target)) { + warning("circular dependency %s <- %s dropped", target, dep); + return; + } + + for (p = tp->deps; p && *p; ++p) { + if (strcmp((*p)->name, dep) == 0) + return; + } + + tp->ndeps++; + siz = (tp->ndeps + 1) * sizeof(Target *); + tp->deps = erealloc(tp->deps, siz); + tp->deps[tp->ndeps-1] = lookup(dep); + tp->deps[tp->ndeps] = NULL; + + debug("Adding dependency %s <- %s", target, dep); +} + +void +addrule(char *target, char **actions, int n) +{ + int i; + char **v; + Target *tp = lookup(target); + + debug("adding actions for target %s", target); + + if (tp->actions) { + debug("overring actions or target %s", target); + for (i = 0; i < tp->nactions; i++) + free(tp->actions[i]); + free(tp->actions); + } + + v = emalloc(n * sizeof(char *)); + for (i = 0; i < n; i++) + v[i] = estrdup(actions[i]); + + tp->nactions = n; + tp->actions = v; +} + +static int +execline(Target *tp, char *line, int ignore, int silence) +{ + char *s; + Target *p, **q; + int r, at, plus, minus; + + debug("executing '%s'", line); + + at = plus = minus = 0; + for (s = line; ; s++) { + switch (*s) { + case '@': + at = 1; + break; + case '-': + minus = 1; + break; + case '+': + plus = 1; + break; + default: + goto out_loop; + } + } + +out_loop: + if (tflag && !plus) + return 0; + + if (sflag || silence) + at = 1; + if (nflag) + at = 0; + if (!at) + puts(s); + + if (nflag && !plus) + return 0; + + if (minus || iflag || ignore) + ignore = 1; + + r = launch(s, ignore); + if (ignore) + return 1; + + return r; +} + +static int +touch(char *name, int silence, int ignore) +{ + char *cmd; + int r, n; + + n = snprintf(NULL, 0, "touch %s", name) + 1; + cmd = emalloc(n); + snprintf(cmd, n, "touch %s", name); + + if (!sflag && !silence) + puts(cmd); + + r = system(cmd); + free(cmd); + + if (ignore || iflag) + return 0; + + return r; +} + +static int +touchdeps(Target *tp, int silent, int ignore) +{ + int r; + Target **p; + + if (tp->req) { + r = touch(tp->req, silent, ignore); + if (r) + return r; + } + + for (p = tp->deps; p && *p; ++p) { + r = touch((*p)->name, silent, ignore); + if (r) + return r; + } + + return 0; +} + +static int +run(Target *tp) +{ + int err, r, i, ignore, silent; + char *s; + Target *p, **q; + + silent = 0; + p = lookup(".SILENT"); + for (q = p->deps; q && *q; ++q) { + if (strcmp((*q)->name, tp->name) == 0) + silent = 1; + } + + ignore = 0; + p = lookup(".IGNORE"); + for (q = p->deps; q && *q; ++q) { + if (strcmp((*q)->name, tp->name) == 0) + ignore = 1; + } + + err = 0; + if (tflag) { + r = touchdeps(tp, silent, ignore); + if (r) + return r; + } + + for (i = 0; i < tp->nactions; i++) { + s = expandstring(tp->actions[i], tp); + r = execline(tp, s, silent, ignore); + free(s); + + if (r) + return r; + } + + if (tflag) { + r = touch(tp->name, silent, ignore); + if (r) + return r; + } + + return 0; +} + +static int +enabled(char *suffix) +{ + Target **p, *tp = lookup(".SUFFIXES"); + + for (p = tp->deps; p && *p; ++p) { + if (strcmp(suffix, (*p)->name) == 0) + return 1; + } + + return 0; +} + +static Target * +inference(Target *tp) +{ + time_t t; + int tolen, r; + char *to, *from; + Target *q, **p, *suffixes; + char buf[20], fname[FILENAME_MAX]; + + debug("searching an inference rule for %s", tp->name); + + to = strrchr(tp->name, '.'); + if (to && !enabled(to)) + return NULL; + tolen = to ? to - tp->name : strlen(tp->name); + + if (!to) + to = ""; + + suffixes = lookup(".SUFFIXES"); + for (p = suffixes->deps; p && *p; ++p) { + from = (*p)->name; + debug("trying suffix %s", from); + + r = snprintf(buf, + sizeof(buf), + "%s%s", + from, to); + + if (r < 0 || r >= sizeof(buf)) + error("suffixes too long %s %s", from, to); + + q = lookup(buf); + if (!q->actions) + continue; + + r = snprintf(fname, + sizeof(fname), + "%*.*s%s", + tolen, tolen, tp->name, from); + + if (r < 0 || r >= sizeof(fname)) { + error("prerequisite name too long %s %s", + tp->name, from); + } + + debug("\tsearching prerequisite %s", fname); + + t = stamp(fname); + if (t == -1 || t <= tp->stamp) + continue; + + free(q->req); + q->req = estrdup(fname); + q->deps = tp->deps; + q->target = tp->name; + q->stamp = tp->stamp; + + debug("using inference rule %s with %s", q->name, fname); + return q; + } + + return NULL; +} + +static int +update(Target *tp) +{ + Target *p; + + debug("%s needs to be updated", tp->name); + + if (tp->actions) { + debug("using target rule to build %s", tp->name); + return run(tp); + } + + if ((p = inference(tp)) != NULL) { + debug("using inference rule %s", p->name); + return run(p); + } + + p = lookup(".DEFAULT"); + if (p->defined) { + debug("using default rule"); + return run(p); + } + + debug("not rule found to update %s", tp->name); + + if (!tp->defined) + error("don't know how to make %s", tp->name); + + return 0; +} + +static int +cleanup(Target *tp) +{ + int precious; + Target *p, **q; + + printf("make: signal %d arrived\n", stop); + + precious = 0; + p = lookup(".PRECIOUS"); + for (q = p->deps; q && *q; q++) { + if (strcmp((*q)->name, tp->name) == 0) { + precious = 1; + break; + } + } + + if (!precious) { + printf("make: trying to remove target %s\n", tp->name); + remove(tp->name); + } + + signal(stop, SIG_DFL); + raise(stop); +} + +static int +rebuild(Target *tp, int *buildp) +{ + Target **p, *q;; + int r, need, build, err; + + debug("checking rebuild of %s", tp->name); + + err = need = 0; + for (p = tp->deps; p && *p; ++p) { + if (stop) + cleanup(tp); + + q = *p; + debug("checking dependency %s", q->name); + + build = 0; + if (rebuild(q, &build) != 0) { + err = 1; + continue; + } + + if (build) { + debug("rebuild of %s forces rebuild of %s", + q->name, tp->name); + need = 1; + } else if (q->stamp > tp->stamp) { + debug("dependency %s is newer than %s", + q->name, tp->name); + need = 1; + } + } + + if (err) { + warning("target %s not remade because of errors", tp->name); + return 1; + } else if (tp->stamp == -1 || need) { + *buildp = 1; + r = update(tp); + if (r == 0) + return 0; + + exitstatus = 1; + + if (!kflag) + error("target %s: error %d", tp->name, r); + else + warning("target %s: error %d", tp->name, r); + return r; + } +} + +int +build(char *name) +{ + int build; + + if (!name) { + if (!deftarget) { + printf("make: no target to make\n"); + return 0; + } + name = deftarget->name; + } + + debug("checking target %s'", name); + return rebuild(lookup(name), &build); +}