commit 3a30aa6d16018ca853dadc3e60cab173c5a8b1c8 from: Benjamin Stürz date: Fri Jan 10 22:55:39 2025 UTC import new version of make(1) From now on, make will be developed in a separate repo: https://github.com/realchonk/bmk This decision has been made, to make make be usable for other projects. commit - 10729a4d604b072251ebfcd23aab8b33b1bf13d1 commit + 3a30aa6d16018ca853dadc3e60cab173c5a8b1c8 blob - 83eb3c7528fb2e0be5130dbd12d64d54a7855f93 (mode 644) blob + /dev/null --- MyMakefile +++ /dev/null @@ -1,40 +0,0 @@ -.SUBDIRS: make cc sys - -TOP != pwd - -## Path to the C Compiler -CC ??= cc - -## Path to Yacc -YACC ?= $./tools/bin/yacc - -## Default C Compiler Flags -CFLAGS ?= -O2 -CFLAGS += -ansi -Wall -Wno-deprecated-non-prototype -Wno-implicit-int - -## Default C Preprocssor Flags -CPPFLAGS ?= -D_GNU_SOURCE -D_BSD_SOURCE - -.EXPORTS: CC YACC CFLAGS CPPFLAGS - -## Clean even more things -distclean: clean - rm -rf tools - -## Get a list of all things to be done -todo: - @find . -name 'TODO*' | while read -r file; do \ - echo "=== $$file"; \ - cat "$$file"; \ - printf '\n\n'; \ - done | less - -.c: - ${CC} -o $@ $< ${CFLAGS} ${CPPFLAGS} - -.c.o: - ${CC} -c -o $@ $< ${CFLAGS} ${CPPFLAGS} - -include templates.mk - -.expand dir blob - /dev/null blob + 83eb3c7528fb2e0be5130dbd12d64d54a7855f93 (mode 644) --- /dev/null +++ Mkfile @@ -0,0 +1,40 @@ +.SUBDIRS: make cc sys + +TOP != pwd + +## Path to the C Compiler +CC ??= cc + +## Path to Yacc +YACC ?= $./tools/bin/yacc + +## Default C Compiler Flags +CFLAGS ?= -O2 +CFLAGS += -ansi -Wall -Wno-deprecated-non-prototype -Wno-implicit-int + +## Default C Preprocssor Flags +CPPFLAGS ?= -D_GNU_SOURCE -D_BSD_SOURCE + +.EXPORTS: CC YACC CFLAGS CPPFLAGS + +## Clean even more things +distclean: clean + rm -rf tools + +## Get a list of all things to be done +todo: + @find . -name 'TODO*' | while read -r file; do \ + echo "=== $$file"; \ + cat "$$file"; \ + printf '\n\n'; \ + done | less + +.c: + ${CC} -o $@ $< ${CFLAGS} ${CPPFLAGS} + +.c.o: + ${CC} -c -o $@ $< ${CFLAGS} ${CPPFLAGS} + +include templates.mk + +.expand dir blob - 0d0f9d51ff160408707381c0f26a8d8915b5d8cd blob + 326242bdd754e0da2f0eac18bb9ba06ada948dcb --- bootstrap +++ bootstrap @@ -5,7 +5,7 @@ CFLAGS='-ansi -O0 -g -w' mkdir -p tools/bin -${CC} -o 'tools/bin/mk' 'make/make.c' ${CFLAGS} +${CC} -o 'tools/bin/mk' 'make/mk.c' 'make/compats.c' ${CFLAGS} -DHAVE_CONFIG_H ./tools/bin/mk clean ./tools/bin/mk CC="${CC}" PREFIX="${PWD}/tools" cc/yacc/install blob - 984271dae43c9165d091b91773cb0bce0bda7525 (mode 644) blob + /dev/null --- cc/MyMakefile +++ /dev/null @@ -1,3 +0,0 @@ -.SUBDIRS: cc1 cpp irc yacc - -.expand dir blob - /dev/null blob + 984271dae43c9165d091b91773cb0bce0bda7525 (mode 644) --- /dev/null +++ cc/Mkfile @@ -0,0 +1,3 @@ +.SUBDIRS: cc1 cpp irc yacc + +.expand dir blob - a22d12933faf8a16999616fdbcf2587d88a479c9 (mode 644) blob + /dev/null --- cc/cc1/MyMakefile +++ /dev/null @@ -1,30 +0,0 @@ -NAME = cc1 -NOMAN = 1 -CFLAGS += -Wno-comment -YFLAGS = -d -v - -# TODO: fix out-of-tree build - -clean-extra: - rm -f y.tab.h y.output - -run: cc1 ../irc/irc - ./cc1 < test.c > test.ir - ../irc/irc < test.ir > test.asm - nasm -fbin -o /dev/null test.asm - (echo '=== IR ==='; cat test.ir; echo '=== ASM ==='; cat test.asm) | less - -cc1: lex.o parse.o gen.o main.o dt.o - ${CC} -o $@ $^ ${CFLAGS} ${CPPFLAGS} - -parse.o y.tab.h: parse.y - ${YACC} ${YFLAGS} parse.y - ${CC} -c -o ${.OBJDIR}/parse.o y.tab.c ${CFLAGS} ${CPPFLAGS} - rm -f y.tab.c - -lex.o: cc1.h y.tab.h -gen.o: cc1.h -main.o: cc1.h y.tab.h -dt.o: cc1.h - -.expand prog blob - /dev/null blob + a22d12933faf8a16999616fdbcf2587d88a479c9 (mode 644) --- /dev/null +++ cc/cc1/Mkfile @@ -0,0 +1,30 @@ +NAME = cc1 +NOMAN = 1 +CFLAGS += -Wno-comment +YFLAGS = -d -v + +# TODO: fix out-of-tree build + +clean-extra: + rm -f y.tab.h y.output + +run: cc1 ../irc/irc + ./cc1 < test.c > test.ir + ../irc/irc < test.ir > test.asm + nasm -fbin -o /dev/null test.asm + (echo '=== IR ==='; cat test.ir; echo '=== ASM ==='; cat test.asm) | less + +cc1: lex.o parse.o gen.o main.o dt.o + ${CC} -o $@ $^ ${CFLAGS} ${CPPFLAGS} + +parse.o y.tab.h: parse.y + ${YACC} ${YFLAGS} parse.y + ${CC} -c -o ${.OBJDIR}/parse.o y.tab.c ${CFLAGS} ${CPPFLAGS} + rm -f y.tab.c + +lex.o: cc1.h y.tab.h +gen.o: cc1.h +main.o: cc1.h y.tab.h +dt.o: cc1.h + +.expand prog blob - /dev/null blob + d5c9d99ba98379ff37c1079a01d6f6e8782fa8ae (mode 644) --- /dev/null +++ cc/bcom/Mkfile @@ -0,0 +1,16 @@ +NAME = bcom +NOMAN = 1 +OBJ = lex.o parse.o gen.o main.o + +clean-extra: + rm -f y.tab.h y.output + +bcom: ${OBJ} + ${CC} -o $@ ${OBJ:F} ${CFLAGS} ${LDFLAGS} + +parse.o: parse.y + ${YACC} ${YFLAGS} $< + ${CC} -c -o $@ y.tab.c ${CFLAGS} + rm -f y.tab.c + +.expand prog blob - 1d1ef98e9cc3ada8e662841a2b87d1341383ba5a (mode 644) blob + /dev/null --- cc/cpp/MyMakefile +++ /dev/null @@ -1,20 +0,0 @@ -NAME = cpp -NOMAN = 1 -CFLAGS += -g -O0 -Wno-implicit-function-declaration -Wno-comment -Wno-char-subscripts -CPPFLAGS += -DFLEXNAMES -DBSD2_86 -Di286=1 -Dunix - -test: cpp - ./cpp -P < cpp.c | grep -v '^$$' - -cpp: cpp.o cpy.o - ${CC} -o $@ $^ ${CFLAGS} ${CPPFLAGS} - -cpy.o: cpy.y yylex.c - ${YACC} cpy.y - ${CC} -c -o $@ y.tab.c ${CFLAGS} - rm -f y.tab.c - -clean-extra: - rm -f y.tab.c - -.expand prog blob - /dev/null blob + 1d1ef98e9cc3ada8e662841a2b87d1341383ba5a (mode 644) --- /dev/null +++ cc/cpp/Mkfile @@ -0,0 +1,20 @@ +NAME = cpp +NOMAN = 1 +CFLAGS += -g -O0 -Wno-implicit-function-declaration -Wno-comment -Wno-char-subscripts +CPPFLAGS += -DFLEXNAMES -DBSD2_86 -Di286=1 -Dunix + +test: cpp + ./cpp -P < cpp.c | grep -v '^$$' + +cpp: cpp.o cpy.o + ${CC} -o $@ $^ ${CFLAGS} ${CPPFLAGS} + +cpy.o: cpy.y yylex.c + ${YACC} cpy.y + ${CC} -c -o $@ y.tab.c ${CFLAGS} + rm -f y.tab.c + +clean-extra: + rm -f y.tab.c + +.expand prog blob - 64098d7a03574809cfbc18225d5c1c5bc95dc6ae (mode 644) blob + /dev/null --- cc/irc/MyMakefile +++ /dev/null @@ -1,10 +0,0 @@ -NAME = irc -NOMAN = 1 -CFLAGS += -Wno-comment - -run: irc - ./irc < test.ir > test.asm - nasm -fobj -o /dev/null test.asm - less test.asm - -.expand prog blob - /dev/null blob + 64098d7a03574809cfbc18225d5c1c5bc95dc6ae (mode 644) --- /dev/null +++ cc/irc/Mkfile @@ -0,0 +1,10 @@ +NAME = irc +NOMAN = 1 +CFLAGS += -Wno-comment + +run: irc + ./irc < test.ir > test.asm + nasm -fobj -o /dev/null test.asm + less test.asm + +.expand prog blob - 1516a7f6e8abd29003717e55198369fab486bd91 (mode 644) blob + /dev/null --- cc/yacc/MyMakefile +++ /dev/null @@ -1,16 +0,0 @@ -NAME = yacc -NOMAN = 1 -CFLAGS += -Wno-return-type -Wno-implicit-function-declaration -Wno-comment -CPPFLAGS = -DWORD32 -DPARSER=\"${PREFIX}/lib/yaccpar\" - -clean-extra: - rm -f y.tab.[ch] calc calc.c yacc.acts - -install-extra: - mkdir -p ${DESTDIR}${PREFIX}/lib - cp -f ${.OBJDIR}/yaccpar ${DESTDIR}${PREFIX}/lib - -yacc: y1.c y2.c y3.c y4.c - ${CC} -o $@ $^ ${CFLAGS} ${CPPFLAGS} - -.expand prog blob - /dev/null blob + 1516a7f6e8abd29003717e55198369fab486bd91 (mode 644) --- /dev/null +++ cc/yacc/Mkfile @@ -0,0 +1,16 @@ +NAME = yacc +NOMAN = 1 +CFLAGS += -Wno-return-type -Wno-implicit-function-declaration -Wno-comment +CPPFLAGS = -DWORD32 -DPARSER=\"${PREFIX}/lib/yaccpar\" + +clean-extra: + rm -f y.tab.[ch] calc calc.c yacc.acts + +install-extra: + mkdir -p ${DESTDIR}${PREFIX}/lib + cp -f ${.OBJDIR}/yaccpar ${DESTDIR}${PREFIX}/lib + +yacc: y1.c y2.c y3.c y4.c + ${CC} -o $@ $^ ${CFLAGS} ${CPPFLAGS} + +.expand prog blob - ddf101a7ebc5ef76b4394407ab7a45f1df5115c3 (mode 644) blob + /dev/null --- make/MyMakefile +++ /dev/null @@ -1,14 +0,0 @@ -NAME = make -NOMAN = 1 - -clean-extra: - rm -f ${.OBJDIR}/mk.vendor.c - -make: make.h - -mk.vendor.c: make.c make.h - printf '#define NEED_REALLOCARRAY 1\n' \ - | cat - make.h make.c \ - | sed '/^#include "make.h"/d' >$@ - -.expand prog blob - /dev/null blob + e4091c67dc99834e5b3ee5893bdd5587ef23c612 (mode 644) --- /dev/null +++ make/Mkfile @@ -0,0 +1,16 @@ +CPPFLAGS += -DHAVE_CONFIG_H +NAME = make +NOMAN = 1 + +clean-extra: + rm -f ${.OBJDIR}/mk.vendor.c + +make: mk.c mk.h + ${CC} -o $@ mk.c ${CFLAGS} ${CPPFLAGS} + +mk.vendor.c: mk.c mk.h + printf '#define NEED_REALLOCARRAY 1\n' \ + | cat - mk.h mk.c \ + | sed '/^#include "make.h"/d' >$@ + +.expand prog blob - dc5962929df8b8441970cf1cc5f97f656fbd83a9 (mode 644) blob + /dev/null --- make/make.c +++ /dev/null @@ -1,3241 +0,0 @@ -#if __linux__ -# define _GNU_SOURCE -# define _XOPEN_SOURCE 700 -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "make.h" - -#define new(T) ((T *)calloc (1, sizeof (T))) -#define MAKEFILE "MyMakefile" -#define SHELL "sh" - -static char *cpath, *objdir = NULL; -static int verbose = 0, cline = 0, conterr = 0; -static struct timespec time_zero; - -static struct macro m_shell = { - .next = NULL, - .enext = NULL, - .prepend = NULL, - .name = "SHELL", - .value = SHELL, - .lazy = 0, -}, m_make = { - .next = &m_shell, - .enext = &m_shell, - .prepend = NULL, - .name = "MAKE", - .value = NULL, - .lazy = 0, -}, m_dmake = { - .next = &m_make, - .enext = &m_make, - .prepend = NULL, - .name = ".MAKE", - .value = NULL, - .lazy = 0, -}, m_makeflags = { - .next = &m_dmake, - .enext = &m_dmake, - .prepend = NULL, - .name = "MAKEFLAGS", - .value = NULL, - .lazy = 0, -}, m_dmakeflags = { - .next = &m_makeflags, - .enext = &m_makeflags, - .prepend = NULL, - .name = ".MAKEFLAGS", - .value = NULL, - .lazy = 0, -}; - -static struct macro *globals = &m_dmakeflags; - -/* STRING BUFFER */ - -typedef struct string { - char *ptr; - size_t len, cap; -} str_t; - -static str_t tmpstr; - -str_new (s) -str_t *s; -{ - s->len = 0; - s->cap = 10; - s->ptr = malloc (s->cap + 1); - return 0; -} - -str_reserve (s, n) -str_t *s; -size_t n; -{ - if (s->cap == 0) { - s->cap = n; - s->ptr = malloc (s->cap + 1); - } else if ((s->len + n) > s->cap) { - for (s->cap *= 2; (s->len + n) > s->cap; s->cap *= 2); - s->ptr = realloc (s->ptr, s->cap + 1); - } - return 0; -} - -str_free (s) -str_t *s; -{ - free (s->ptr); - memset (s, 0, sizeof (*s)); - return 0; -} - -str_putc (s, ch) -str_t *s; -{ - str_reserve (s, 1); - s->ptr[s->len++] = ch; - return 0; -} - -str_write (s, t, n) -str_t *s; -char *t; -size_t n; -{ - str_reserve (s, n); - memcpy (s->ptr + s->len, t, n); - s->len += n; - return 0; -} - -str_puts (s, t) -str_t *s; -char *t; -{ - return str_write (s, t, strlen (t)); -} - -str_last (s) -str_t *s; -{ - return s->len > 0 ? s->ptr[s->len - 1] : EOF; -} - -str_pop (s) -str_t *s; -{ - return s->len > 0 ? s->ptr[--s->len] : EOF; -} - -str_chomp (s) -str_t *s; -{ - while (str_last (s) == '\n') - str_pop (s); - return 0; -} - -str_trim (s) -str_t *s; -{ - size_t i; - - while (isspace (str_last (s))) - str_pop (s); - - for (i = 0; i < s->len && isspace (s->ptr[i]); ++i); - - memmove (s->ptr, s->ptr + i, s->len - i); - s->len -= i; - return 0; -} - -str_reset (s) -str_t *s; -{ - if (s->cap == 0) { - str_new (s); - } else { - s->len = 0; - } - return 0; -} - -char * -str_get (s) -str_t *s; -{ - s->ptr[s->len] = '\0'; - return s->ptr; -} - -char * -str_release (s) -str_t *s; -{ - char *t; - s->ptr[s->len] = '\0'; - t = realloc (s->ptr, s->len + 1); - memset (s, 0, sizeof (*s)); - return t; -} - -/* STRING MISC */ - -char * -xstrcat (s, t) -char *s, *t; -{ - char *u; - size_t len_s, len_t; - - len_s = strlen (s); - len_t = strlen (t); - u = malloc (len_s + len_t + 1); - memcpy (u, s, len_s); - memcpy (u + len_s, t, len_t + 1); - return u; -} - -skip_ws (s) -char **s; -{ - for (; isspace (**s); ++*s); - return 0; -} - -char * -ltrim (s) -char *s; -{ - skip_ws (&s); - return s; -} - -char * -rtrim (s) -char *s; -{ - size_t i, len; - - len = strlen (s); - for (i = len; i > 0 && isspace (s[i - 1]); --i) - s[i - 1] = '\0'; - - return s; -} - -char * -trim (s) -char *s; -{ - return ltrim (rtrim (s)); -} - -starts_with (s, prefix) -char *s, *prefix; -{ - size_t len_s, len_p; - - len_s = strlen (s); - len_p = strlen (prefix); - - if (len_s < len_p) - return 0; - - return memcmp (s, prefix, len_p) == 0; -} - -/* PATH LOGIC */ - -static struct path path_null = { .type = PATH_NULL, .name = NULL }; -static struct path path_super = { .type = PATH_SUPER, .name = NULL }; -static struct path tmppath = { .type = PATH_NAME, .name = NULL }; - -/* return the number of path components (excl. PATH_NULL). */ -size_t -path_len (p) -struct path *p; -{ - size_t i; - - for (i = 0; p[i].type != PATH_NULL; ++i); - - return i; -} - -struct path * -path_cpy (old, old_len, new_len) -struct path *old; -size_t old_len, new_len; -{ - struct path *p; - - p = calloc (new_len + 1, sizeof (struct path)); - memcpy (p, old, old_len * sizeof (struct path)); - p[new_len].type = PATH_NULL; - - return p; -} - -struct path * -path_cat (old, comp) -struct path *old, *comp; -{ - struct path *p; - size_t len; - - len = path_len (old); - - switch (comp->type) { - case PATH_NULL: - p = path_cpy (old, len, len); - break; - case PATH_SUPER: - if (len > 0 && old[len - 1].type != PATH_SUPER) { - --len; - p = path_cpy (old, len, len); - break; - } - /* fallthrough */ - case PATH_NAME: - p = path_cpy (old, len, len + 1); - p[len] = *comp; - break; - } - - return p; -} - -path_write (s, p) -str_t *s; -struct path *p; -{ - size_t i; - - str_putc (s, '.'); - - for (i = 0; p[i].type != PATH_NULL; ++i) { - switch (p[i].type) { - case PATH_NULL: - abort (); - case PATH_SUPER: - str_puts (s, "/.."); - break; - case PATH_NAME: - str_putc (s, '/'); - str_puts (s, p[i].name); - break; - } - } - - return 0; -} - -char * -path_to_str (p) -struct path *p; -{ - str_reset (&tmpstr); - path_write (&tmpstr, p); - return str_get (&tmpstr); -} - -char * -path_basename (p) -struct path *p; -{ - char *s, *t; - - s = realpath (path_to_str (p), NULL); - t = strdup (basename (s)); - free (s); - - return t; -} - -char * -path_cat_str (dir, file) -struct path *dir; -char *file; -{ - str_reset (&tmpstr); - path_write (&tmpstr, dir); - str_putc (&tmpstr, '/'); - str_puts (&tmpstr, file); - return str_get (&tmpstr); -} - -struct path * -parse_path (s) -char *s; -{ - size_t len = 0, cap = 4; - struct path *p; - char *t; - - p = calloc (cap + 1, sizeof (struct path)); - - while ((t = strsep (&s, "/")) != NULL) { - if (*t == '\0' || strcmp (t, ".") == 0) - continue; - - if (len == cap) { - cap *= 2; - p = reallocarray (p, cap + 1, sizeof (struct path)); - } - - if (strcmp (t, "..") == 0) { - p[len].type = PATH_SUPER; - } else { - p[len].type = PATH_NAME; - p[len].name = strdup (t); - } - ++len; - } - p[len].type = PATH_NULL; - - return p; -} - -sc_path_into (out, sc) -str_t *out; -struct scope *sc; -{ - if (sc->parent == NULL) { - str_putc (out, '.'); - return 0; - } - - sc_path_into (out, sc->parent); - str_putc (out, '/'); - str_puts (out, sc->name); - return 0; -} - -char * -sc_path_str (sc) -struct scope *sc; -{ - str_reset (&tmpstr); - sc_path_into (&tmpstr, sc); - return str_get (&tmpstr); -} - -write_objdir (out, sc) -str_t *out; -struct scope *sc; -{ - if (objdir != NULL) { - str_puts (out, objdir); - str_putc (out, '/'); - sc_path_into (out, sc); - } else { - str_putc (out, '.'); - } - return 0; -} - -/* OTHER MISC */ - -struct filetime { - struct timespec t; - int obj; -}; - -get_mtime (out, sc, dir, name) -struct filetime *out; -struct scope *sc; -struct path *dir; -char *name; -{ - extern char *path_cat_str (); - struct stat st; - char *path; - - if (verbose >= 2) - printf ("get_mtime('%s'): ", name); - - path = path_cat_str (dir, name); - - if (lstat (path, &st) == 0) { - out->t = st.st_mtim; - out->obj = 0; - if (verbose >= 2) - printf ("found\n"); - return 0; - } - - if (objdir == NULL) - goto enoent; - - str_reset (&tmpstr); - write_objdir (&tmpstr, sc); - str_putc (&tmpstr, '/'); - str_puts (&tmpstr, name); - path = str_get (&tmpstr); - - if (lstat (path, &st) == 0) { - out->t = st.st_mtim; - out->obj = 1; - if (verbose >= 2) - printf ("found in obj\n"); - return 0; - } - -enoent: - out->t = time_zero; - out->obj = 0; - if (verbose >= 2) - printf ("not found\n"); - return -1; - -} - -struct timespec -now () -{ - struct timespec t; - clock_gettime (CLOCK_REALTIME, &t); - return t; -} - -tv_cmp (a, b) -struct timespec *a, *b; -{ - if (a->tv_sec < b->tv_sec) { - return -1; - } else if (a->tv_sec > b->tv_sec) { - return 1; - } else if (a->tv_nsec < b->tv_nsec) { - return -1; - } else if (a->tv_nsec > b->tv_nsec) { - return 1; - } else { - return 0; - } -} - -/* MKDIR */ - -mkdir_p (dir) -char *dir; -{ - struct stat st; - char *copy; - - if (verbose >= 2) - printf ("mkdir('%s');\n", dir); - - if (mkdir (dir, 0755) == 0) - return 0; - - if (errno == EEXIST) { - if (stat (dir, &st) != 0) - err (1, "mkdir_p('%s'): stat()", dir); - - if (S_ISDIR (st.st_mode)) - return 0; - - errx (1, "mkdir_p('%s'): Not a directory", dir); - } - - if (errno != ENOENT) - err (1, "mkdir_p('%s')", dir); - - copy = strdup (dir); - mkdir_p (dirname (copy)); - free (copy); - - if (mkdir (dir, 0755) != 0) - err (1, "mkdir('%s')", dir); - - return 0; -} - -sc_mkdir_p (sc) -struct scope *sc; -{ - if (objdir == NULL) - return 0; - - str_reset (&tmpstr); - str_puts (&tmpstr, objdir); - str_putc (&tmpstr, '/'); - sc_path_into (&tmpstr, sc); - - mkdir_p (str_get (&tmpstr)); - return 0; -} - -/* MACORS MISC */ - -/* is macro name */ -ismname (ch) -{ - return isalnum (ch) || ch == '_' || ch == '.'; -} - -/* SEARCHING */ - -struct macro * -find_emacro (sc, name) -struct scope *sc; -char *name; -{ - struct macro *m; - - if (sc == NULL) - return NULL; - - for (m = sc->dir->emacros; m != NULL; m = m->enext) { - if (strcmp (m->name, name) == 0) - return m; - } - - return find_emacro (sc->parent, name); -} - -struct macro * -find_macro (sc, name) -struct scope *sc; -char *name; -{ - struct macro *m; - - for (m = sc->dir->macros; m != NULL; m = m->next) { - if (strcmp (m->name, name) == 0) - return m; - } - - m = find_emacro (sc->parent, name); - if (m != NULL) - return m; - - for (m = globals; m != NULL; m = m->next) { - if (strcmp (m->name, name) == 0) - return m; - } - - return NULL; -} - -struct template * -find_template (sc, name) -struct scope *sc; -char *name; -{ - struct template *tm; - - for (tm = sc->dir->templates; tm != NULL; tm = tm->next) { - if (strcmp (tm->name, name) == 0) - return tm; - } - - return sc->parent != NULL ? find_template (sc->parent, name) : NULL; -} - -struct file * -find_file (dir, name) -struct directory *dir; -char *name; -{ - struct file *f; - - for (f = dir->ftail; f != NULL; f = f->prev) { - if (strcmp (name, f->name) == 0) - return f; - } - - return NULL; -} - -struct scope * -find_subdir (sc, name) -struct scope *sc; -char *name; -{ - struct scope *sub; - - for (sub = sc->dir->subdirs; sub != NULL; sub = sub->next) { - if (strcmp (sub->name, name) == 0) - return sub; - } - return NULL; -} - -/* MACRO EXPANSION */ - -struct expand_ctx { - char *target; - struct dep *deps, *infdeps; - struct dep *dep0; - int free_target; -}; - -ectx_init (ctx, target, dep0, deps, infdeps) -struct expand_ctx *ctx; -char *target; -struct dep *dep0; -struct dep *deps, *infdeps; -{ - ctx->target = target; - ctx->dep0 = dep0; - ctx->deps = deps; - ctx->infdeps = infdeps; - ctx->free_target = 0; - return 0; -} - -ectx_file (ctx, sc, f) -struct expand_ctx *ctx; -struct scope *sc; -struct file *f; -{ - str_t tmp; - - if (objdir != NULL) { - str_new (&tmp); - write_objdir (&tmp, sc); - str_putc (&tmp, '/'); - str_puts (&tmp, f->name); - ctx->target = str_release (&tmp); - ctx->free_target = 1; - } else { - ctx->target = f->name; - ctx->free_target = 0; - } - - ctx->deps = ctx->dep0 = f->dhead; - ctx->infdeps = f->inf != NULL ? f->inf->dhead : NULL; - if (ctx->dep0 == NULL && ctx->infdeps != NULL) - ctx->dep0 = ctx->infdeps; - - return 0; -} - -ectx_free (ctx) -struct expand_ctx *ctx; -{ - if (ctx->free_target) - free (ctx->target); - return 0; -} - -replace_into (out, s, old, new) -str_t *out; -char *s, *old, *new; -{ - size_t len_s, len_old; - - len_s = strlen (s); - len_old = strlen (old); - - if (len_s < len_old || memcmp (s + len_s - len_old, old, len_old) != 0) { - str_puts (out, s); - return 0; - } - - str_write (out, s, len_s - len_old); - str_puts (out, new); - return 0; -} - -replace_all_into (out, s, old, new) -str_t *out; -char *s, *old, *new; -{ - char *t; - int x = 0; - - while ((t = strsep (&s, " \t")) != NULL) { - if (*t == '\0') - continue; - - replace_into (out, t, old, new); - str_putc (out, ' '); - x = 1; - } - - if (x) - str_pop (out); - - return 0; -} - -pr_export (out, sc, prefix, m, ctx) -str_t *out; -struct scope *sc; -struct path *prefix; -struct macro *m; -struct expand_ctx *ctx; -{ - extern expand_macro_into (); - - str_puts (out, m->name); - str_puts (out, "='"); - expand_macro_into (out, sc, prefix, m, m->name, ctx); - str_putc (out, '\''); - return 0; -} - -dep_write (out, sc, dep) -str_t *out; -struct scope *sc; -struct dep *dep; -{ - if (dep->obj && objdir != NULL) { - write_objdir (out, sc); - str_putc (out, '/'); - } - path_write (out, dep->path); - return 0; -} - -expand_special_into (out, sc, prefix, name, ctx) -str_t *out; -struct scope *sc; -struct path *prefix; -char *name; -struct expand_ctx *ctx; -{ - struct scope *sub; - struct macro *m; - struct dep *dep; - int i, j; - - /* TODO: .SUBDIRS, .EXPORTS */ - - if (strcmp (name, ".SUBDIRS") == 0) { - if (sc->type != SC_DIR) { - invsc: - errx (1, "%s: invalid scope type", sc_path_str (sc)); - } - - sub = sc->dir->subdirs; - if (sub == NULL) - return 0; - - str_puts (out, sub->name); - for (sub = sub->next; sub != NULL; sub = sub->next) { - str_putc (out, ' '); - str_puts (out, sub->name); - } - } else if (strcmp (name, ".EXPORTS") == 0) { - if (sc->type != SC_DIR) - goto invsc; - - m = sc->dir->emacros; - if (m == NULL) - return 0; - - pr_export (out, sc, prefix, m, ctx); - for (m = m->enext; m != NULL; m = m->enext) { - str_putc (out, ' '); - pr_export (out, sc, prefix, m, ctx); - } - - } else if (strcmp (name, ".OBJDIR") == 0) { - write_objdir (out, sc); - } else if (strcmp (name, ".TARGET") == 0) { - if (ctx == NULL) - errx (1, "%s: cannot use $@ or ${.TARGET} here", sc_path_str (sc)); - str_puts (out, ctx->target); - } else if (strcmp (name, ".IMPSRC") == 0) { - if (ctx == NULL) - errx (1, "%s: cannot use $< or ${.IMPSRC} here", sc_path_str (sc)); - - if (ctx->dep0 == NULL) - return 0; - - dep_write (out, sc, ctx->dep0); - } else if (strcmp (name, ".ALLSRC") == 0) { - if (ctx == NULL) - errx (1, "%s: cannot use $^ or ${.ALLSRC} here", sc_path_str (sc)); - - for (dep = ctx->deps; dep != NULL; dep = dep->next) { - str_putc (out, ' '); - dep_write (out, sc, dep); - } - for (dep = ctx->infdeps; dep != NULL; dep = dep->next) { - str_putc (out, ' '); - dep_write (out, sc, dep); - } - } else if (strcmp (name, ".TOPDIR") == 0) { - str_putc (out, '.'); - for (sub = sc->parent; sub != NULL; sub = sub->parent) - str_puts (out, "/.."); - } else if (strcmp (name, ".MAKEFILES") == 0) { - for (i = 0, sub = sc; sub != NULL; ++i, sub = sub->parent) { - for (j = 0; j < i; ++j) - str_puts (out, "../"); - str_puts (out, sub->makefile); - str_putc (out, ' '); - } - str_pop (out); - } - return 0; -} - -/* ${name} just the value of macro called `name` - * ${name:old=new} replace `old` with `new`, must be the last modifier - * ${name:U} replace each word with its upper case equivalent - * ${name:L} replace each word with its lower case equivalent - * ${name:F} try searching for files in either ${.OBJDIR} or source directory - * ${name:E} replace each word with its suffix - * ${name:R} replace each word with everything but its suffix - * ${name:H} replace each word with its dirname() equvialent - * ${name:T} replace each word with its basename() equvialent - * ${name:m1:m2...} multiple modifiers can be combined - * ${name:Mpattern} select only words that match pattern - * ${name:Npattern} opposite of :Mpattern - * TODO: - * somehow make this function shorter - */ -subst2 (out, sc, prefix, s, ctx) -str_t *out; -struct scope *sc; -struct path *prefix; -char **s; -struct expand_ctx *ctx; -{ - extern char *expand_macro (); - extern expand_macro_into (); - extern subst (); - struct macro *m; - struct filetime ft; - char *orig = *s, *t, *u, *v, *w, *pattern; - str_t name, old, new; - - /* parse macro name */ - str_new (&name); - while (**s != '\0') { - if (ismname (**s)) { - str_putc (&name, **s); - ++*s; - } else if (**s == '$') { - ++*s; - subst (&name, sc, prefix, s, ctx); - } else { - break; - } - } - - m = find_macro (sc, str_get (&name)); - if (**s == '}') { - ++*s; - expand_macro_into (out, sc, prefix, m, str_get (&name), ctx); - str_free (&name); - return 0; - } - - v = expand_macro (sc, prefix, m, str_get (&name), ctx); - str_free (&name); - - str_new (&old); - - while (**s == ':') { - ++*s; - - /* parse modifier, TODO: move this into a function */ - for (str_reset (&old); **s != '\0' && **s != '}' && **s != ':' && **s != '='; ) { - switch (**s) { - case '$': - ++*s; - subst (&old, sc, prefix, s, ctx); - break; - case '\\': - ++*s; - /* fallthrough */ - default: - str_putc (&old, **s); - ++*s; - break; - } - } - - if (**s == '=') { - ++*s; - str_new (&new); - - /* TODO: move this into a function */ - for (str_new (&new); **s != '\0' && **s != '}'; ) { - switch (**s) { - case '$': - ++*s; - subst (&new, sc, prefix, s, ctx); - break; - case '\\': - ++*s; - /* fallthrough */ - default: - str_putc (&new, **s); - ++*s; - break; - } - } - - replace_all_into (out, v, str_get (&old), str_get (&new)); - str_free (&new); - goto ret; - } else if (strcmp (str_get (&old), "U") == 0) { - str_new (&new); - - for (t = v; *t != '\0'; ++t) - str_putc (&new, toupper (*t)); - - free (v); - v = str_release (&new); - } else if (strcmp (str_get (&old), "L") == 0) { - str_new (&new); - - for (t = v; *t != '\0'; ++t) - str_putc (&new, tolower (*t)); - - free (v); - v = str_release (&new); - } else if (strcmp (str_get (&old), "F") == 0) { - str_new (&new); - - for (t = v; (w = strsep (&t, " \t")) != NULL; ) { - if (*w == '\0') - continue; - - if (get_mtime (&ft, sc, prefix, w) == 0 && ft.obj) { - write_objdir (&new, sc); - str_putc (&new, '/'); - } - str_puts (&new, w); - str_putc (&new, ' '); - } - str_pop (&new); - - free (v); - v = str_release (&new); - } else if (strcmp (str_get (&old), "E") == 0) { - str_new (&new); - - for (t = v; (w = strsep (&t, " \t")) != NULL; ) { - if (*w == '\0') - continue; - - u = strrchr (w, '.'); - if (u == NULL || strchr (u, '/') != NULL) - continue; - - str_puts (&new, u); - str_putc (&new, ' '); - } - - str_pop (&new); - free (v); - v = str_release (&new); - } else if (strcmp (str_get (&old), "R") == 0) { - str_new (&new); - - for (t = v; (w = strsep (&t, " \t")) != NULL; ) { - if (*w == '\0') - continue; - - u = strrchr (w, '.'); - if (u != NULL && strchr (u, '/') == NULL) - *u = '\0'; - - str_puts (&new, w); - str_putc (&new, ' '); - } - - str_pop (&new); - free (v); - v = str_release (&new); - } else if (strcmp (str_get (&old), "H") == 0) { - str_new (&new); - - for (t = v; (w = strsep (&t, " \t")) != NULL; ) { - if (*w == '\0') - continue; - - str_puts (&new, dirname (w)); - str_putc (&new, ' '); - } - - str_pop (&new); - free (v); - v = str_release (&new); - } else if (strcmp (str_get (&old), "T") == 0) { - str_new (&new); - - for (t = v; (w = strsep (&t, " \t")) != NULL; ) { - if (*w == '\0') - continue; - - str_puts (&new, basename (w)); - str_putc (&new, ' '); - } - - str_pop (&new); - free (v); - v = str_release (&new); - } else if (str_get (&old)[0] == 'M') { - str_new (&new); - - pattern = str_get (&old) + 1; - - for (t = v; (w = strsep (&t, " \t")) != NULL; ) { - if (*w == '\0') - continue; - - if (fnmatch (pattern, w, 0) != 0) - continue; - - str_puts (&new, w); - str_putc (&new, ' '); - } - - str_pop (&new); - free (v); - v = str_release (&new); - } else if (str_get (&old)[0] == 'N') { - str_new (&new); - - pattern = str_get (&old) + 1; - - for (t = v; (w = strsep (&t, " \t")) != NULL; ) { - if (*w == '\0') - continue; - - if (fnmatch (pattern, w, 0) == 0) - continue; - - str_puts (&new, w); - str_putc (&new, ' '); - } - - str_pop (&new); - free (v); - v = str_release (&new); - } else { - errx (1, "%s: invalid modifier: ':%s' in '${%s'", sc_path_str (sc), str_get (&old), orig); - } - } - - str_puts (out, v); - -ret: - if (**s != '}') - goto invalid; - ++*s; - str_free (&old); - free (v); - return 0; -invalid: - errx (1, "%s: invalid macro expansion: '${%s', s = '%s'", sc_path_str (sc), orig, *s); -} - -subst (out, sc, prefix, s, ctx) -str_t *out; -struct scope *sc; -struct path *prefix; -char **s; -struct expand_ctx *ctx; -{ - char *t; - int ch; - - ch = **s; - ++*s; - switch (ch) { - case '$': - str_putc (out, '$'); - break; - case '.': - expand_special_into (out, sc, prefix, ".TOPDIR", ctx); - break; - case '@': - expand_special_into (out, sc, prefix, ".TARGET", ctx); - break; - case '<': - expand_special_into (out, sc, prefix, ".IMPSRC", ctx); - break; - case '^': - expand_special_into (out, sc, prefix, ".ALLSRC", ctx); - break; - case '*': - t = ".IMPSRC:T}"; - subst2 (out, sc, prefix, &t, ctx); - break; - case '{': - subst2 (out, sc, prefix, s, ctx); - break; - case '(': - errx (1, "%s: syntax error: $(...) syntax is reserved for future use, please use ${...} instead.", sc_path_str (sc)); - default: - errx (1, "%s: syntax error: invalid escape sequence: $%c%s", sc_path_str (sc), ch, *s); - } - - return 0; -} - -expand_into (out, sc, prefix, s, ctx) -str_t *out; -struct scope *sc; -struct path *prefix; -char *s; -struct expand_ctx *ctx; -{ - while (*s != '\0') { - if (*s != '$') { - str_putc (out, *s++); - continue; - } - ++s; - subst (out, sc, prefix, &s, ctx); - } - return 0; -} - -expand_macro_into (out, sc, prefix, m, name, ctx) -str_t *out; -struct scope *sc; -struct path *prefix; -struct macro *m; -char *name; -struct expand_ctx *ctx; -{ - if (m == NULL) - return expand_special_into (out, sc, prefix, name, ctx); - - if (m->prepend != NULL) { - expand_macro_into (out, sc, prefix, m->prepend, m->prepend->name, ctx); - str_putc (out, ' '); - } - - if (m->value == NULL) - return 0; - - if (m->lazy) { - expand_into (out, sc, prefix, m->value, ctx); - } else { - str_puts (out, m->value); - } - return 0; -} - -char * -expand_macro (sc, prefix, m, name, ctx) -struct scope *sc; -struct path *prefix; -struct macro *m; -char *name; -struct expand_ctx *ctx; -{ - str_t tmp; - - str_new (&tmp); - expand_macro_into (&tmp, sc, prefix, m, name, ctx); - return str_release (&tmp); -} - -char * -expand (sc, prefix, s, ctx) -struct scope *sc; -struct path *prefix; -char *s; -struct expand_ctx *ctx; -{ - str_t out; - - str_new (&out); - expand_into (&out, sc, prefix, s, ctx); - str_trim (&out); - return str_release (&out); -} - -/* COMMAND EXECUTION */ - -char * -evalcom (sc, dir, cmd) -struct scope *sc; -struct path *dir; -char *cmd; -{ - char *args[] = { - m_shell.value, - "-c", - expand (sc, dir, cmd, NULL), - NULL, - }; - ssize_t i, n; - str_t data; - pid_t pid; - int pipefd[2]; - char buf[64 + 1]; - - if (pipe (pipefd) != 0) - err (1, "pipe()"); - - pid = fork (); - if (pid == -1) - err (1, "fork()"); - - if (pid == 0) { - close (STDOUT_FILENO); - close (pipefd[0]); - if (dup (pipefd[1]) != STDOUT_FILENO) - err (1, "failed to dup"); - close (STDIN_FILENO); - if (open ("/dev/null", O_RDONLY) != STDIN_FILENO) - err (1, "failed to open /dev/null"); - close (pipefd[1]); - - if (chdir (path_to_str (dir)) != 0) - err (1, "failed to chdir"); - - execvp (m_shell.value, args); - err (1, "failed to launch shell"); - } else { - close (pipefd[1]); - - str_new (&data); - - while ((n = read (pipefd[0], buf, sizeof (buf) - 1)) > 0) { - for (i = 0; i < n; ++i) - str_putc (&data, buf[i]); - } - close (pipefd[0]); - wait (NULL); - } - - free (args[2]); - str_chomp (&data); - - return str_release (&data); -} - -runcom (sc, prefix, cmd, ctx, rule) -struct scope *sc; -struct path *prefix; -char *cmd, *rule; -struct expand_ctx *ctx; -{ - char *ecmd; - pid_t pid; - int q = 0, ws, ign = 0; - - if (*cmd == '@') { - q = 1; - ++cmd; - } else if (*cmd == '-') { - ign = 1; - ++cmd; - } else if (verbose < 0) { - q = 1; - } - - ecmd = expand (sc, prefix, cmd, ctx); - - if (!q) { - printf ("[%s%s%s] $ %s\n", - path_to_str (prefix), - rule != NULL ? "/" : "", - rule != NULL ? rule : "", - verbose ? ecmd : cmd - ); - } - - pid = fork (); - if (pid == 0) { - close (STDIN_FILENO); - if (open ("/dev/null", O_RDONLY) != STDIN_FILENO) - warn ("%d: open('/dev/null')", STDIN_FILENO); - - if (chdir (path_to_str (prefix)) != 0) - err (1, "chdir()"); - - execlp ( - m_shell.value, - m_shell.value, - ign ? "-c" : "-ec", - ecmd, - NULL - ); - err (1, "exec('%s')", ecmd); - } else { - free (ecmd); - if (waitpid (pid, &ws, 0) != pid) { - warn ("waitpid(%d)", (int)pid); - return 255; - } - - if (!WIFEXITED (ws)) { - warnx ("%d: process didn't exit", (int)pid); - return 255; - } - - return ign ? 0 : WEXITSTATUS (ws); - } -} - -/* EXPRESSION PARSER */ - -is_truthy (s) -char *s; -{ - char *endp; - long x; - - if (*s == '\0') - return 0; - - x = strtol (s, &endp, 0); - - return *endp != '\0' || x != 0; -} - -e_command (sc, prefix, s, cmd, arg) -struct scope *sc; -struct path *prefix; -char **s, *cmd; -str_t *arg; -{ - char *orig = *s; - - *s += strlen (cmd); - skip_ws (s); - if (**s != '(') - errx (1, "%s:%d: expected '(' after 'defined': %s", cpath, cline, orig); - - str_new (arg); - for (++*s; **s != ')'; ++*s) - str_putc (arg, **s); - ++*s; - - str_trim (arg); - return 0; -} - -e_atom (sc, prefix, s, val) -struct scope *sc; -struct path *prefix; -char **s; -str_t *val; -{ - str_t arg; - int x; - - skip_ws (s); - - if (**s == '"') { - ++*s; - - while (**s != '"') { - if (**s == '$') { - ++*s; - subst (val, sc, prefix, s, NULL); - } else { - str_putc (val, **s); - ++*s; - } - } - ++*s; - } else if (starts_with (*s, "defined")) { - e_command (sc, prefix, s, "defined", &arg); - x = find_macro (sc, str_get (&arg)) != NULL; - comm: - str_putc (val, x ? '1' : '0'); - str_free (&arg); - } else if (starts_with (*s, "target")) { - e_command (sc, prefix, s, "target", &arg); - x = find_file (sc->dir, str_get (&arg)) != NULL; - goto comm; - } else { - errx (1, "%s:%d: invalid expression: '%s'", cpath, cline, *s); - } - - return 0; -} - -e_unary (sc, prefix, s, val) -struct scope *sc; -struct path *prefix; -char **s; -str_t *val; -{ - skip_ws (s); - if (**s != '!') - return e_atom (sc, prefix, s, val); - ++*s; - - e_unary (sc, prefix, s, val); - - if (is_truthy (str_get (val))) { - str_reset (val); - str_putc (val, '0'); - } else { - str_reset (val); - str_putc (val, '1'); - } - - return 0; -} - -enum { - COMP_EQ, - COMP_NE, - COMP_LT, - COMP_LE, - COMP_GT, - COMP_GE, -}; - -e_comp (sc, prefix, s) -struct scope *sc; -struct path *prefix; -char **s; -{ - str_t left, right; - char *sl, *sr, *el, *er; - long il, ir; - int cmp, x, icmp; - - str_new (&left); - e_unary (sc, prefix, s, &left); - - skip_ws (s); - - if (starts_with (*s, "==")) { - cmp = COMP_EQ; - *s += 2; - } else if (starts_with (*s, "!=")) { - cmp = COMP_NE; - *s += 2; - } else if (**s == '<') { - ++*s; - if (**s == '=') { - cmp = COMP_LE; - ++*s; - } else { - cmp = COMP_LT; - ++*s; - } - } else if (**s == '>') { - ++*s; - if (**s == '=') { - cmp = COMP_GE; - ++*s; - } else { - cmp = COMP_GT; - ++*s; - } - } else { - str_trim (&left); - x = is_truthy (str_get (&left)); - str_free (&left); - return x; - } - - skip_ws (s); - str_new (&right); - e_unary (sc, prefix, s, &right); - - str_trim (&left); - str_trim (&right); - - sl = str_get (&left); - sr = str_get (&right); - il = strtol (sl, &el, 0); - ir = strtol (sr, &er, 0); - icmp = *sl != '\0' && *sr != '\0' && *el == '\0' && *er == '\0'; - x = icmp ? il - ir : strcmp (sl, sr); - switch (cmp) { - case COMP_EQ: - x = x == 0; - break; - case COMP_NE: - x = x != 0; - break; - case COMP_LT: - x = x < 0; - break; - case COMP_LE: - x = x <= 0; - break; - case COMP_GT: - x = x > 0; - break; - case COMP_GE: - x = x >= 0; - break; - default: - abort (); - } - - str_free (&left); - str_free (&right); - return x; -} - -e_and (sc, prefix, s) -struct scope *sc; -struct path *prefix; -char **s; -{ - int x; - - x = e_comp (sc, prefix, s); - while (skip_ws (s), starts_with (*s, "&&")) { - *s += 2; - x &= e_comp (sc, prefix, s); - } - - return x; -} - -e_or (sc, prefix, s) -struct scope *sc; -struct path *prefix; -char **s; -{ - int x; - - x = e_and (sc, prefix, s); - while (skip_ws (s), starts_with (*s, "||")) { - *s += 2; - x |= e_and (sc, prefix, s); - } - - return x; -} - -parse_expr (sc, prefix, s) -struct scope *sc; -struct path *prefix; -char *s; -{ - s = trim (s); - return e_or (sc, prefix, &s); -} - -/* PARSER */ - -char * -readline (file, ln) -FILE *file; -int *ln; -{ - str_t line; - int ch, eof = 1; - - str_new (&line); - - while (1) { - ch = fgetc (file); - switch (ch) { - case EOF: - goto ret; - case '\n': - eof = 0; - ++*ln; - goto ret; - case '\\': - ch = fgetc (file); - if (ch == '\n') { - ++*ln; - } else { - str_putc (&line, '\\'); - str_putc (&line, ch); - } - eof = 0; - break; - default: - str_putc (&line, ch); - eof = 0; - break; - } - } - -ret: - return eof ? NULL : str_release (&line); -} - -struct scope * -new_subdir (parent, name) -struct scope *parent; -char *name; -{ - struct scope *sub; - - sub = new (struct scope); - sub->next = parent->dir->subdirs; - sub->type = SC_DIR; - sub->name = name; - sub->parent = parent; - sub->makefile = NULL; - sub->created = 0; - parent->dir->subdirs = sub; - - return sub; -} - -struct file * -new_file (name, rule, time, dhead, dtail, help, inf, obj) -char *name, *help; -struct rule *rule; -struct timespec time; -struct dep *dhead, *dtail; -struct inference *inf; -{ - struct file *f; - - f = new (struct file); - f->next = f->prev = NULL; - f->name = name; - f->rule = rule; - f->dhead = dhead; - f->dtail = dtail; - f->mtime = time; - f->help = help; - f->inf = inf; - f->obj = obj; - f->err = 0; - - return f; -} - -struct dep * -new_dep (path) -struct path *path; -{ - struct dep *d; - - d = new (struct dep); - d->next = d->prev = NULL; - d->path = path; - - return d; -} - -struct dep * -new_dep_name (name) -char *name; -{ - struct path *p; - - p = calloc (2, sizeof (struct path)); - p[0].type = PATH_NAME; - p[0].name = name; - p[1].type = PATH_NULL; - - return new_dep (p); -} - -dir_add_file (dir, f) -struct directory *dir; -struct file *f; -{ - assert (f->next == NULL && f->prev == NULL); - - if (dir->fhead != NULL) { - f->prev = dir->ftail; - dir->ftail->next = f; - } else { - dir->fhead = f; - } - dir->ftail = f; - return 0; -} - -file_add_deps (file, dhead, dtail) -struct file *file; -struct dep *dhead, *dtail; -{ - assert (dhead->prev == NULL); - assert (dtail->next == NULL); - - if (file->dhead != NULL) { - dhead->prev = file->dtail; - file->dtail->next = dhead; - } else { - file->dhead = dhead; - } - file->dtail = dtail; - return 0; -} - -file_add_dep (file, dep) -struct file *file; -struct dep *dep; -{ - return file_add_deps (file, dep, dep); -} - - -/* .SUBDIRS: cc make sys */ -parse_subdirs (sc, dir, s) -struct scope *sc; -struct path *dir; -char *s; -{ - struct scope *sub; - char *subdir, *name, *path; - - while ((subdir = strsep (&s, " \t")) != NULL) { - if (*subdir == '\0') - continue; - - name = strdup (trim (subdir)); - sub = new_subdir (sc, name); - sub->type = SC_DIR; - sub->makefile = MAKEFILE; - - path = path_cat_str (dir, sub->name); - if (access (path, F_OK) != 0) - errx (1, "%s:%d: directory not found: %s", cpath, cline, sub->name); - } - - return 0; -} - -/* .FOREIGN: libfoo libbar */ -parse_foreign (sc, dir, s) -struct scope *sc; -struct path *dir; -char *s; -{ - struct scope *sub; - char *subdir, *name; - - while ((subdir = strsep (&s, " \t")) != NULL) { - if (*subdir == '\0') - continue; - - name = strdup (trim (subdir)); - sub = new_subdir (sc, name); - sub->type = SC_CUSTOM; - sub->makefile = NULL; - sub->custom = new (struct custom); - sub->custom->test = NULL; - sub->custom->exec = NULL; - } - - return 0; -} - -/* .EXPORTS: CC CFLAGS */ -parse_exports (sc, dir, s) -struct scope *sc; -struct path *dir; -char *s; -{ - struct macro *m; - char *name; - - while ((name = strsep (&s, " \t")) != NULL) { - if (*name == '\0') - continue; - - /* check if the macro is already exported */ - for (m = sc->dir->emacros; m != NULL; m = m->enext) { - if (strcmp (m->name, name) == 0) - goto cont; /* already exported */ - } - - m = find_macro (sc, name); - if (m == NULL) - errx (1, "%s:%d: no such macro: '%s'", cpath, cline, name); - - m->enext = sc->dir->emacros; - sc->dir->emacros = m; - - cont:; - } - - return 0; -} - -try_add_custom (sc, f) -struct scope *sc; -struct file *f; -{ - struct scope *sub; - char *name; - size_t len; - int ch; - - len = strlen (f->name); - ch = f->name[len - 1]; - if (ch != '?' && ch != '!') - return 0; - - name = strdup (f->name); - name[len - 1] = '\0'; - sub = find_subdir (sc, name); - if (sub == NULL) - errx (1, "%s: not a subdir: %s", sc_path_str (sc), name); - - if (sub->type != SC_CUSTOM) - errx (1, "%s: not a custom subdir: %s", sc_path_str (sc), name); - - if (ch == '?') { - sub->custom->test = f; - } else { - sub->custom->exec = f; - } - - free (name); - return 1; -} - -/* TODO: impl .SUFFIXES: */ -is_inf (s) -char *s; -{ - char *d; - int x; - - if (*s != '.') - return 0; - ++s; - d = strchr (s, '.'); - - if (d == NULL) { - return strlen (s) <= 3; - } - - *d = '\0'; - x = strlen (s) <= 3 && strlen (d + 1) <= 3; - *d = '.'; - return x; -} - -struct rule * -parse_rule (sc, dir, s, t, help) -struct scope *sc; -struct path *dir; -char *s, *t, *help; -{ - struct inference *inf; - struct filetime ft; - struct rule *r; - struct file *f; - struct dep *dep, *dhead, *dtail; - char *u, *v, *p; - int flag; - - r = new (struct rule); - r->code = NULL; - dhead = dtail = NULL; - - *t = '\0'; - - /* parse deps */ - v = u = expand (sc, dir, t + 1, NULL); - while ((p = strsep (&v, " \t")) != NULL) { - if (*p == '\0') - continue; - - dep = new_dep (parse_path (p)); - if (dhead != NULL) { - dep->prev = dtail; - dtail->next = dep; - } else { - dhead = dep; - } - dtail = dep; - } - free (u); - - /* parse targets */ - u = expand (sc, dir, s, NULL); - flag = 1; - if (is_inf (u)) { - p = strchr (u + 1, '.'); - inf = new (struct inference); - inf->next = sc->dir->infs; - inf->rule = r; - inf->dhead = dhead; - inf->dtail = dtail; - - if (p != NULL) { - *p = '\0'; - inf->from = strdup (u); - *p = '.'; - inf->to = strdup (p); - } else { - inf->from = strdup (u); - inf->to = ""; - } - - sc->dir->infs = inf; - } else { - v = u; - while ((p = strsep (&v, " \t")) != NULL) { - if (*p == '\0') - continue; - /* TODO: check name */ - - f = find_file (sc->dir, p); - if (f == NULL) { - get_mtime (&ft, sc, dir, p); - - f = new_file ( - /* name */ strdup (p), - /* rule */ r, - /* time */ ft.t, - /* dhead*/ dhead, - /* dtail*/ dtail, - /* help */ help, - /* inf */ NULL, - /* obj */ ft.obj - ); - /* TODO: maybe first do try_add_custom()? */ - dir_add_file (sc->dir, f); - try_add_custom (sc, f); - continue; - } - - flag = 0; - - if (f->help == NULL) - f->help = help; - - file_add_deps (f, dhead, dtail); - } - } - free (u); - return flag ? r : NULL; -} - -parse_assign (sc, dir, s, t, help) -struct scope *sc; -struct path *dir; -char *s, *t, *help; -{ - struct macro *m, *m2; - char *v; - - m = new (struct macro); - m->next = sc->dir->macros; - m->enext = NULL; - m->help = help; - m->prepend = NULL; - - if (t[-1] == '!') { - t[-1] = '\0'; - m->lazy = 0; - m->value = evalcom (sc, dir, trim (t + 1)); - } else if (t[-1] == '?') { - if (t[-2] == '?') { - t[-2] = '\0'; - v = getenv (trim (s)); - m->value = v != NULL ? v : strdup (trim (t + 1)); - } else { - t[-1] = '\0'; - m2 = find_macro (sc, trim (s)); - m->value = m2 != NULL ? m2->value : strdup (trim (t + 1)); - } - m->lazy = 1; - } else if (t[-1] == ':') { - /* handle both `:=` and `::=` */ - t[t[-2] == ':' ? -2 : -1] = '\0'; - m->lazy = 0; - m->value = expand (sc, dir, trim (t + 1), NULL); - } else if (t[-1] == '+') { - t[-1] = '\0'; - m->value = strdup (trim (t + 1)); - m->prepend = find_macro (sc, trim (s)); - m->lazy = 1; - } else { - m->lazy = 1; - m->value = strdup (trim (t + 1)); - } - m->name = strdup (trim (s)); - sc->dir->macros = m; - return 0; -} - - -#define IF_VAL 0x01 -#define IF_HAS 0x02 -#define MAX_IFSTACK 16 - -walkifstack (s, n) -char *s; -size_t n; -{ - size_t i; - - for (i = 0; i < n; ++i) { - if (!(s[i] & IF_VAL)) - return 0; - } - return 1; -} - -is_directive (out, s, name) -char **out, *s, *name; -{ - size_t len; - - if (*s != '.') - return 0; - ++s; - - skip_ws (&s); - len = strlen (name); - if (strncmp (s, name, len) != 0) - return 0; - - s += len; - - if (*s != '\0' && !isspace (*s)) - return 0; - - if (out != NULL) - *out = trim (s); - return 1; -} - -is_target (out, s, name) -char **out, *s, *name; -{ - size_t len; - - len = strlen (name); - if (strncmp (s, name, len) != 0) - return 0; - - s += len; - skip_ws (&s); - if (*s != ':') - return 0; - ++s; - - if (out != NULL) - *out = trim (s); - - return 1; -} - -do_parse (sc, dir, path, file) -struct scope *sc; -struct path *dir; -char *path; -FILE *file; -{ - extern parse (); - struct template *tm; - struct rule *r = NULL; - size_t len, cap, iflen = 0; - char *s, *t, *u, *help = NULL; - char ifstack[MAX_IFSTACK]; - int x, run; - FILE *tfile; - str_t text; - - assert (sc->type == SC_DIR); - cpath = path; - - if (verbose >= 3) { - printf ("Parsing dir '%s' ...\n", path_to_str (dir)); - } - - for (; (s = readline (file, &cline)) != NULL; free (s)) { - run = walkifstack (ifstack, iflen); - if (s[0] == '#' && s[1] == '#') { - help = expand (sc, dir, trim (s + 2), NULL); - continue; - } else if (s[0] == '#' || *trim (s) == '\0') { - continue; - } else if (starts_with (s, "include ")) { - if (!run) - goto cont; - - t = expand (sc, dir, s + 8, NULL); - if (*t == '/') { - u = t; - } else { - u = strdup (path_cat_str (dir, t)); - } - parse (sc, dir, u); - if (u != t) - free (u); - free (t); - } else if (is_directive (&t, s, "include")) { - if (!run) - goto cont; - - errx (1, "%s:%d: please use .SUBDIRS: or .FOREIGN: now", path, cline); - } else if (is_directive (&t, s, "if")) { - if (iflen == MAX_IFSTACK) - errx (1, "%s:%d: maximum .if depth of %d reached", path, cline, MAX_IFSTACK); - x = parse_expr (sc, dir, t) & 0x01; - ifstack[iflen++] = x * (IF_VAL | IF_HAS); - } else if (is_directive (NULL, s, "else")) { - if (iflen == 0) - errx (1, "%s:%d: not in .if", path, cline); - t = &ifstack[iflen - 1]; - *t = (!(*t & IF_HAS) * (IF_VAL | IF_HAS)) | (*t & IF_HAS); - } else if (is_directive (&t, s, "elif")) { - if (iflen == 0) - errx (1, "%s:%d: not in .if", path, cline); - x = parse_expr (sc, dir, t); - t = &ifstack[iflen - 1]; - *t = ((!(*t & IF_HAS) && x) * (IF_VAL | IF_HAS)) | (*t & IF_HAS); - } else if (is_directive (NULL, s, "endif")) { - if (iflen == 0) - errx (1, "%s:%d: not in .if", path, cline); - --iflen; - } else if (is_directive (&t, s, "template")) { - str_new (&text); - - tm = new (struct template); - tm->next = sc->dir->templates; - tm->name = strdup (t); - - for (free (s); (s = readline (file, &cline)) != NULL; free (s)) { - if (is_directive (NULL, s, "endt") || is_directive (NULL, s, "endtemplate")) - break; - - str_puts (&text, s); - str_putc (&text, '\n'); - } - - if (run) { - tm->text = str_release (&text); - sc->dir->templates = tm; - } else { - free (tm); - str_free (&text); - } - } else if (is_directive (&t, s, "expand")) { - if (!run) - goto cont; - tm = find_template (sc, t); - if (tm == NULL) - errx (1, "%s:%d: no such template: %s", cpath, cline, t); - tfile = fmemopen (tm->text, strlen (tm->text), "r"); - do_parse (sc, dir, "template", tfile); - fclose (tfile); - } else if (is_target (&t, s, ".DEFAULT")) { - if (run) - sc->dir->default_file = strdup (t); - } else if (is_target (NULL, s, ".POSIX")) { - if (run) - warnx ("%s:%d: this is not a POSIX-compatible make", path, cline); - } else if (is_target (NULL, s, ".SUFFIXES")) { - if (run) - warnx ("%s:%d: this make doesn't require .SUFFIXES", path, cline); - } else if (is_target (&t, s, ".SUBDIRS")) { - if (run) - parse_subdirs (sc, dir, t); - } else if (is_target (&t, s, ".FOREIGN")) { - if (run) - parse_foreign (sc, dir, t); - } else if (is_target (&t, s, ".EXPORTS")) { - if (run) - parse_exports (sc, dir, t); - } else if (s[0] == '\t') { - if (!run) - goto cont; - - if (r == NULL) - errx (1, "%s:%d: syntax error", path, cline); - - if (len == cap) { - cap *= 2; - r->code = reallocarray (r->code, cap + 1, sizeof (char *)); - } - - r->code[len++] = strdup (s + 1); - r->code[len] = NULL; - } else if ((t = strchr (s, '=')) != NULL) { - if (!run) - goto cont; - - /* TODO: check name */ - *t = '\0'; - parse_assign (sc, dir, s, t, help); - } else if ((t = strchr (s, ':')) != NULL) { - if (!run) - goto cont; - - r = parse_rule (sc, dir, s, t, help); - if (r == NULL) - goto cont; - - len = 0; - cap = 1; - r->code = calloc (cap + 1, sizeof (char *)); - r->code[0] = NULL; - } else { - warnx ("%s:%d: invalid line: %s", path, cline, s); - } - - cont: - help = NULL; - } - - return 0; -} - -parse (sc, dir, path) -struct scope *sc; -struct path *dir; -char *path; -{ - FILE *file; - - file = fopen (path, "r"); - if (file == NULL) - err (1, "open(\"%s\")", path); - - if (sc->dir == NULL) { - sc->dir = new (struct directory); - sc->dir->subdirs = NULL; - sc->dir->fhead = NULL; - sc->dir->ftail = NULL; - sc->dir->done = 0; - } else if (sc->dir->done) { - errx (1, "%s: parsing this file again?", path); - } - - do_parse (sc, dir, path, file); - sc->dir->done = 1; - - fclose (file); - return 0; -} - -parse_dir (sc, dir) -struct scope *sc; -struct path *dir; -{ - char *path; - - path = strdup (path_cat_str (dir, sc->makefile)); - parse (sc, dir, path); - free (path); - - return 0; -} - -struct scope * -parse_recursive (dir, makefile) -struct path *dir; -char *makefile; -{ - struct path *mfpath, *ppath; - struct scope *sc, *parent; - char *path, *name; - - tmppath.name = makefile; - mfpath = path_cat (dir, &tmppath); - path = path_to_str (mfpath); - if (access (path, F_OK) != 0) { - sc = NULL; - goto ret; - } - - ppath = path_cat (dir, &path_super); - parent = parse_recursive (ppath, makefile); - if (parent == NULL) - parent = parse_recursive (ppath, MAKEFILE); - free (ppath); - - name = path_basename (dir); - - if (parent != NULL) { - if (parent->type != SC_DIR) - errx (1, "%s: invalid parent type", path_to_str (mfpath)); - - for (sc = parent->dir->subdirs; sc != NULL; sc = sc->next) { - if (strcmp (sc->name, name) == 0) { - if (sc->type != SC_DIR) - errx (1, "%s: invalid type", path_to_str (mfpath)); - goto parse; - } - } - - goto create; - } else { - create: - sc = new (struct scope); - sc->type = SC_DIR; - sc->name = name; - sc->dir = NULL; - } - -parse: - sc->makefile = makefile; - sc->parent = parent; - path = strdup (path_to_str (mfpath)); - parse (sc, dir, path); - free (path); - -ret: - free (mfpath); - return sc; -} - -struct path * -parse_subdir (prefix, sub) -struct path *prefix; -struct scope *sub; -{ - struct path *np; - - tmppath.name = sub->name; - np = path_cat (prefix, &tmppath); - parse_dir (sub, np); - return np; -} - -/* INFERENCE RULES */ - -char * -replace_suffix (name, sufx) -char *name, *sufx; -{ - char *out, *ext; - size_t len_name, len_sufx; - - ext = strrchr (name, '.'); - len_name = ext != NULL ? ext - name : strlen (name); - len_sufx = strlen (sufx); - - out = malloc (len_name + len_sufx + 1); - memcpy (out, name, len_name); - memcpy (out + len_name, sufx, len_sufx); - out[len_name + len_sufx] = '\0'; - - return out; -} - -/* instantiate a new file from an inference rule */ -struct file * -inst_inf (sc, inf, name) -struct scope *sc; -struct inference *inf; -char *name; -{ - struct file *f; - struct dep *dep; - - dep = new_dep_name (replace_suffix (name, inf->from)); - - f = new_file ( - /* name */ strdup (name), - /* rule */ inf->rule, - /* time */ time_zero, - /* deps */ dep, - /* dtail*/ dep, - /* help */ NULL, - /* inf */ inf, - /* obj */ 0 - ); - dir_add_file (sc->dir, f); - - return f; -} - -/* instantiate an inference rule on an existing file */ -inf_inst_file (f, inf) -struct file *f; -struct inference *inf; -{ - struct dep *dep; - - assert (f->inf == NULL); - /* TODO: this is ugly */ - assert (f->rule == NULL || f->rule->code == NULL || *f->rule->code == NULL); - - dep = new_dep_name (replace_suffix (f->name, inf->from)); - - /* prepend dependency to file */ - if (f->dhead != NULL) { - dep->next = f->dhead; - f->dhead->prev = dep; - } - f->dhead = dep; - f->inf = inf; - f->rule = inf->rule; - return 0; -} - -struct inference * -find_inf (sc, dir, name) -struct scope *sc; -struct path *dir; -char *name; -{ - extern struct file *try_find (); - struct inference *inf; - struct file *sf; - char *sn, *base; - char *ext; - - ext = strrchr (name, '.'); - base = strdup (name); - if (ext == NULL) { - ext = ""; - } else { - base[ext - name] = '\0'; - } - - for (inf = sc->dir->infs; inf != NULL; inf = inf->next) { - if (strcmp (inf->to, ext) == 0) { - sn = xstrcat (base, inf->from); - sf = find_file (sc->dir, sn); - if (sf == NULL) - sf = try_find (sc, dir, sn); - free (sn); - if (sf != NULL) - break; - } - } - - free (base); - return inf != NULL || sc->parent == NULL ? inf : find_inf (sc->parent, dir, name); -} - -struct file * -try_find (sc, dir, name) -struct scope *sc; -struct path *dir; -char *name; -{ - struct inference *inf; - struct filetime ft; - struct file *f; - - get_mtime (&ft, sc, dir, name); - if (tv_cmp (&ft.t, &time_zero) <= 0) { - inf = find_inf (sc, dir, name); - if (inf == NULL) - return NULL; - return inst_inf (sc, inf, name); - } - - f = new_file ( - /* name */ strdup (name), - /* rule */ NULL, - /* time */ ft.t, - /* deps */ NULL, - /* dtail*/ NULL, - /* help */ NULL, - /* inf */ NULL, - /* obj */ ft.obj - ); - dir_add_file (sc->dir, f); - - return f; -} - -/* BUILDING */ - -struct build { - struct timespec t; - struct file *f; - int obj; -}; - -build_init (out, t, f, obj) -struct build *out; -struct timespec t; -struct file *f; -{ - out->t = t; - out->f = f; - out->obj = obj; - return 0; -} - -build_deps (sc, dhead, prefix, mt, maxt, needs_update) -struct scope *sc; -struct dep *dhead; -struct path *prefix; -struct timespec *mt, *maxt; -int *needs_update; -{ - extern build_dir (); - struct build b; - struct dep *dep; - int ec = 0; - - for (dep = dhead; dep != NULL; dep = dep->next) { - if (build_dir (&b, sc, dep->path, prefix) == 0) { - dep->obj = b.obj; - - if (tv_cmp (&b.t, mt) >= 0) - *needs_update = 1; - if (tv_cmp (&b.t, maxt) > 0) - *maxt = b.t; - } else { - ec = 1; - if (!conterr) - break; - } - } - - return ec; -} - -/* TODO: refactor this function, to be less complicated */ -build_file (out, sc, name, prefix) -struct build *out; -struct scope *sc; -char *name; -struct path *prefix; -{ - extern build_dir (); - int needs_update; - struct scope *sub; - struct path *new_prefix, xpath[2]; - struct file *f; - struct timespec maxt; - struct inference *inf; - struct expand_ctx ctx; - struct filetime ft; - struct dep *dep, xdep; - struct build b; - char **s; - int ec; - - if (verbose >= 2) { - printf ("dir %s", path_to_str (prefix)); - if (name) - printf (" (%s)", name); - printf (" ...\n"); - } - - if (!sc->created) { - sc_mkdir_p (sc); - sc->created = 1; - } - - switch (sc->type) { - case SC_DIR: - /* lazily parse subdirectories */ - if (sc->dir == NULL) - parse_dir (sc, prefix); - - /* if no rule specified to build, use the default rule */ - if (name == NULL && sc->dir->default_file != NULL) - name = sc->dir->default_file; - - if (name != NULL) { - /* try finding an explicitly defined file */ - f = find_file (sc->dir, name); - - if (f == NULL) { - /* try finding and building a subdirectory */ - sub = find_subdir (sc, name); - if (sub != NULL) { - tmppath.name = name; - new_prefix = path_cat (prefix, &tmppath); - ec = build_file (out, sub, NULL, new_prefix); - free (new_prefix); - return ec; - } - - /* try finding an inference rule */ - f = try_find (sc, prefix, name); - if (f == NULL) - errx (1, "%s: no such file: %s", sc_path_str (sc), name); - } else { - get_mtime (&ft, sc, prefix, name); - f->mtime = ft.t; - f->obj = ft.obj; - } - } else { - f = sc->dir->fhead; - if (f == NULL) - errx (1, "%s: nothing to build", sc_path_str (sc)); - } - - /* if this file has no rule, try to find an inference rule */ - if (f->rule == NULL || *f->rule->code == NULL) { - /* try finding an inference rule */ - inf = name != NULL ? find_inf (sc, prefix, name) : NULL; - - if (inf != NULL) { - /* instantiate inference rule */ - inf_inst_file (f, inf); - } else if (f->rule == NULL) { - if (tv_cmp (&f->mtime, &time_zero) > 0) { - build_init (out, f->mtime, f, f->obj); - return 0; - } else { - errx (1, "%s: no rule to build: %s", sc_path_str (sc), name); - } - } - } - - needs_update = (tv_cmp (&f->mtime, &time_zero) <= 0); - maxt = f->mtime; - - if (f->err) - return 1; - - /* build dependencies and record timestamps */ - if (build_deps (sc, f->dhead, prefix, &f->mtime, &maxt, &needs_update) != 0) { - f->err = 1; - if (!conterr) - return 1; - } - - /* build dependencies from inference rule */ - if (f->inf != NULL) { - if (build_deps (sc, f->inf->dhead, prefix, &f->mtime, &maxt, &needs_update) != 0) { - f->err = 1; - if (!conterr) - return 1; - } - } - - if (!needs_update) { - build_init (out, f->mtime, f, f->obj); - return 0; - } - - if (f->err) - return 1; - - s = f->rule->code; - - /* rule is a "sum" rule, so doesn't need to be built */ - if (s == NULL || *s == NULL) { - build_init (out, maxt, f, f->obj); - return 0; - } - - /* run commands */ - ectx_file (&ctx, sc, f); - for (; *s != NULL; ++s) { - if (runcom (sc, prefix, *s, &ctx, name) != 0) { - fprintf (stderr, "%s: command failed: %s\n", sc_path_str (sc), *s); - f->err = 1; - return 1; - } - } - ectx_free (&ctx); - - /* update timestamp */ - get_mtime (&ft, sc, prefix, f->name); - f->mtime = ft.t; - f->obj = ft.obj; - build_init (out, f->mtime, f, f->obj); - return 0; - case SC_CUSTOM: - /* run the "subdir?" rule, to test if the target needs to be updated */ - new_prefix = path_cat (prefix, &path_super); - if (name != NULL) { - xpath[0].type = PATH_NAME; - xpath[0].name = name; - xpath[1].type = PATH_NULL; - } - - xdep.next = xdep.prev = NULL; - xdep.path = xpath; - xdep.obj = 0; - - f = sc->custom->test; - if (f != NULL) { - assert (f->inf == NULL); - - ec = 0; - for (dep = f->dhead; dep != NULL; dep = dep->next) { - if (build_dir (&b, sc->parent, dep->path, new_prefix) != 0) { - ec = 1; - if (!conterr) - return ec; - } - } - - if (ec != 0) - return ec; - - ectx_init ( - /* ctx */ &ctx, - /* target */ prefix[path_len (prefix) - 1].name, - /* dep0 */ name != NULL ? &xdep : NULL, - /* deps */ f->dhead, - /* infdeps*/ NULL - ); - - needs_update = 0; - for (s = f->rule->code; *s != NULL; ++s) { - if (runcom (sc->parent, new_prefix, *s, &ctx, name) != 0) { - needs_update = 1; - break; - } - } - } else { - needs_update = 1; - } - - if (!needs_update) { - if (name != NULL && get_mtime (&ft, sc, prefix, name) == 0) { - build_init (out, ft.t, NULL, ft.obj); - } else { - build_init (out, time_zero, NULL, 0); - } - return 0; - } - - /* run the "subdir!" rule */ - f = sc->custom->exec; - if (f == NULL) - errx (1, "%s: missing '%s!' rule", sc_path_str (sc->parent), sc->name); - assert (f->inf == NULL); - - ectx_init ( - /* ctx */ &ctx, - /* target */ prefix[path_len (prefix) - 1].name, - /* dep0 */ name != NULL ? &xdep : NULL, - /* deps */ f->dhead, - /* infdeps*/ NULL - ); - - ec = 0; - for (dep = f->dhead; dep != NULL; dep = dep->next) { - if (build_dir (&b, sc->parent, dep->path, new_prefix) != 0) { - ec = 1; - if (!conterr) - return ec; - } - } - if (ec != 0) - return ec; - - for (s = f->rule->code; *s != NULL; ++s) { - if (runcom (sc->parent, new_prefix, *s, &ctx, name) != 0) { - fprintf (stderr, "%s: command failed: %s\n", sc_path_str (sc->parent), *s); - return 1; - } - } - - free (new_prefix); - if (name != NULL && get_mtime (&ft, sc, prefix, name) == 0) { - build_init (out, ft.t, NULL, ft.obj); - } else { - build_init (out, now (), NULL, 0); - } - return 0; - } - - abort (); -} - -build_dir (out, sc, path, prefix) -struct build *out; -struct scope *sc; -struct path *path, *prefix; -{ - struct path *new_prefix; - struct scope *sub; - int ec; - - switch (path[0].type) { - case PATH_SUPER: - new_prefix = path_cat (prefix, &path[0]); - ec = build_dir (out, sc->parent, path + 1, new_prefix); - free (new_prefix); - return ec; - case PATH_NULL: - return build_file (out, sc, NULL, prefix); - case PATH_NAME: - if (path[1].type == PATH_NULL) - return build_file (out, sc, path[0].name, prefix); - - if (sc->type != SC_DIR) - errx (1, "%s: invalid path", sc_path_str (sc)); - - if (sc->dir == NULL) - parse_dir (sc, prefix); - - new_prefix = path_cat (prefix, &path[0]); - - sub = find_subdir (sc, path[0].name); - if (sub == NULL) - errx (1, "%s: invalid subdir: %s", sc_path_str (sc), path[0].name); - - ec = build_dir (out, sub, path + 1, new_prefix); - free (new_prefix); - return ec; - } - - abort (); -} - -build (out, sc, path) -struct build *out; -struct scope *sc; -struct path *path; -{ - return build_dir (out, sc, path, &path_null); -} - -/* HELP */ - -help_macros (sc) -struct scope *sc; -{ - struct macro *m; - - assert (sc->dir != NULL); - - for (m = sc->dir->macros; m != NULL; m = m->next) { - if (m->help == NULL) - continue; - - printf ("%-30s- %s\n", m->name, m->help); - } - - return 0; -} - -help_files (prefix, sc) -struct path *prefix; -struct scope *sc; -{ - struct path *new_prefix; - struct scope *sub; - struct file *f; - char *p; - int n; - - p = path_to_str (prefix); - if (strcmp (p, ".") == 0) { - p = NULL; - } else { - p += 2; /* skip ./ */ - } - - for (f = sc->dir->fhead; f != NULL; f = f->next) { - if (f->help == NULL) - continue; - - n = 0; - if (p != NULL) - n += printf ("%s/", p); - n += printf ("%s", f->name); - printf ("%-*s- %s\n", n < 30 ? 30 - n : 0, "", f->help); - } - - if (!verbose) - return 0; - - for (sub = sc->dir->subdirs; sub != NULL; sub = sub->next) { - if (sub->type != SC_DIR) - continue; - - new_prefix = parse_subdir (prefix, sub); - help_files (new_prefix, sub); - free (new_prefix); - } - - return 0; -} - -help (prefix, sc) -struct path *prefix; -struct scope *sc; -{ - extern usage (); - usage (1); - - fputs ("\nOPTIONS:\n", stderr); - fputs ("-C dir - chdir(dir)\n", stderr); - fputs ("-f file - read `file` instead of \"" MAKEFILE "\"\n", stderr); - fputs ("-o objdir - put build artifacts into objdir\n", stderr); - fputs ("-V var - print expanded version of var\n", stderr); - fputs ("-h - print help page\n", stderr); - fputs ("-hv - print help page, recursively\n", stderr); - fputs ("-p - dump tree\n", stderr); - fputs ("-pv - dump tree, recursively\n", stderr); - fputs ("-s - do not echo commands\n", stderr); - fputs ("-k - continue processing after errors are encountered\n", stderr); - fputs ("-S - stop processing when errors are encountered (default)\n", stderr); - fputs ("-v - verbose output\n", stderr); - - fputs ("\nMACROS:\n", stderr); - help_macros (sc); - - fputs ("\nTARGETS:\n", stderr); - help_files (prefix, sc); - - return 1; -} - -/* DUMP */ - -print_sc (prefix, sc) -struct path *prefix; -struct scope *sc; -{ - struct path *new_prefix; - struct inference *inf; - struct scope *sub; - struct macro *m; - struct dep *dep; - struct file *f; - struct rule *r; - char **s; - - if (verbose) - printf ("=== %s\n", sc_path_str (sc)); - - if (sc->type != SC_DIR || sc->dir == NULL) - errx (1, "%s: print_sc(): must be of type SC_DIR", sc_path_str (sc)); - - if (sc->dir->default_file != NULL) - printf (".DEFAULT: %s\n", sc->dir->default_file); - - for (m = sc->dir->macros; m != NULL; m = m->next) { - if (m->help != NULL) - printf ("\n## %s\n", m->help); - printf ("%s %s= %s\n", m->name, m->prepend != NULL ? "+" : "", m->value); - } - - printf ("\n"); - - for (f = sc->dir->fhead; f != NULL; f = f->next) { - if (f->help != NULL) - printf ("## %s\n", f->help); - printf ("%s:", f->name); - - for (dep = f->dhead; dep != NULL; dep = dep->next) - printf (" %s", path_to_str (dep->path)); - if (f->inf != NULL) { - for (dep = f->inf->dhead; dep != NULL; dep = dep->next) - printf (" %s", path_to_str (dep->path)); - } - printf ("\n"); - - r = f->rule; - if (r != NULL) { - for (s = r->code; *s != NULL; ++s) - printf ("\t%s\n", *s); - } - printf ("\n"); - } - - for (inf = sc->dir->infs; inf != NULL; inf = inf->next) { - printf ("%s%s:", inf->from, inf->to); - for (dep = inf->dhead; dep != NULL; dep = dep->next) - printf (" %s", path_to_str (dep->path)); - printf ("\n"); - for (s = inf->rule->code; *s != NULL; ++s) - printf ("\t%s\n", *s); - printf ("\n"); - } - - for (sub = sc->dir->subdirs; sub != NULL; sub = sub->next) { - switch (sub->type) { - case SC_DIR: - printf (".include %s, DIR", sub->name); - if (sc->makefile != NULL) - printf (", %s", sc->makefile); - printf ("\n"); - break; - case SC_CUSTOM: - printf (".include %s, CUSTOM\n", sub->name); - break; - } - } - - if (verbose) { - for (sub = sc->dir->subdirs; sub != NULL; sub = sub->next) { - if (sub->type != SC_DIR) - continue; - - printf ("\n"); - - new_prefix = parse_subdir (prefix, sub); - print_sc (new_prefix, sub); - free (new_prefix); - } - } - - return 0; -} - -/* MAIN */ - -usage (uc) -{ - fprintf (stderr, "%s: %s [-hkpsSv] [-C dir] [-f makefile] [-o objdir] [-V var] [target...]\n", uc ? "USAGE" : "usage", m_make.value); - return 1; -} - -do_V (sc, V) -struct scope *sc; -char *V; -{ - size_t len; - char *s; - - if (strchr (V, '$') != 0) { - s = V; - } else { - len = strlen (V); - s = malloc (len + 4); - s[0] = '$'; - s[1] = '{'; - memcpy (s + 2, V, len); - s[len + 2] = '}'; - s[len + 3] = '\0'; - } - - puts (expand (sc, &path_null, s, NULL)); - return 0; -} - -main (argc, argv) -char **argv; -{ - str_t cmdline; - struct scope *sc; - struct path *path; - struct macro *m; - struct build b; - char *s, *cd = NULL, *makefile = MAKEFILE, *V = NULL, *odir = NULL; - int i, option, pr = 0, n = 0, dohelp = 0; - - m_dmake.value = m_make.value = argv[0]; - - str_new (&cmdline); - while ((option = getopt (argc, argv, "hpsvkSC:f:V:o:")) != -1) { - switch (option) { - case 'h': - dohelp = 1; - break; - case 'p': - str_puts (&cmdline, " -p"); - pr = 1; - break; - case 's': - str_puts (&cmdline, " -s"); - verbose = -1; - break; - case 'v': - str_puts (&cmdline, " -v"); - ++verbose; - break; - case 'C': - cd = optarg; - break; - case 'f': - makefile = optarg; - break; - case 'V': - V = optarg; - break; - case 'o': - odir = optarg; - break; - case 'k': - conterr = 1; - break; - case 'S': - conterr = 0; - break; - case '?': - return usage (0); - default: - errx (1, "unexpected option: -%c", option); - } - } - - if (odir != NULL) { - mkdir_p (odir); - objdir = realpath (odir, malloc (PATH_MAX)); - if (objdir == NULL) - err (1, "realpath()"); - - if (verbose >= 3) - printf ("objdir = '%s'\n", objdir); - } - - if (cd != NULL && chdir (cd) != 0) - err (1, "chdir()"); - - argv += optind; - argc -= optind; - - for (i = 0; i < argc; ++i) { - s = strchr (argv[i], '='); - if (s == NULL) - continue; - - str_putc (&cmdline, ' '); - str_puts (&cmdline, argv[i]); - - *s = '\0'; - m = new (struct macro); - m->next = globals; - m->enext = NULL; - m->prepend = NULL; - m->name = trim (argv[i]); - m->value = trim (s + 1); - m->lazy = 0; - globals = m; - - argv[i] = NULL; - } - - str_trim (&cmdline); - m_dmakeflags.value = m_makeflags.value = str_release (&cmdline); - - path = parse_path ("."); - sc = parse_recursive (path, makefile); - if (sc == NULL) - errx (1, "failed to find or parse makefile"); - - if (dohelp) - return help (path, sc); - - if (pr) { - print_sc (path, sc); - return 0; - } - - if (V != NULL) - return do_V (sc, V); - - free (path); - - for (i = 0; i < argc; ++i) { - if (argv[i] == NULL) - continue; - - path = parse_path (argv[i]); - if (build (&b, sc, path) != 0) - return 1; - free (path); - ++n; - } - - return n == 0 ? build (&b, sc, &path_null) : 0; -} - blob - /dev/null blob + 93e383701877156201b933a7b55064f62d35766d (mode 644) --- /dev/null +++ make/compats.c @@ -0,0 +1,300 @@ +#include "config.h" +#include +#include +#include +#include +#if HAVE_LIMITS_H +# include +#endif +#include +#include + +#ifndef PATH_MAX +# ifdef _POSIX_PATH_MAX +# define PATH_MAX _POSIX_PATH_MAX +# else +# define PATH_MAX 256 +# endif +#endif + +#ifndef HAVE_REALLOCARRAY +void * +reallocarray (ptr, num, size) +void *ptr; +size_t num, size; +{ + size_t nb; + + if (num == 0 || size == 0) + abort (); + + nb = num * size; + if (nb < num) + abort (); + + return realloc (ptr, nb); +} +#endif /* HAVE_REALLOCARRAY */ + +#ifndef HAVE_ERR_H + +void errx (eval, fmt, a, b, c, d) +char *fmt; +long a, b, c, d; +{ + fputs ("mk: error: ", stderr); + fprintf (stderr, fmt, a, b, c, d); + fputc ('\n', stderr); + exit (eval); +} + +err (eval, fmt, a, b, c, d) +char *fmt; +long a, b, c, d; +{ + fputs ("mk: error: ", stderr); + fprintf (stderr, fmt, a, b, c, d); + if (errno != 0) + fprintf (stderr, ": %s", strerror (errno)); + fputc ('\n', stderr); + exit (eval); +} + +warnx (fmt, a, b, c, d) +char *fmt; +long a, b, c, d; +{ + fputs ("mk: warn: ", stderr); + fprintf (stderr, fmt, a, b, c, d); + fputc ('\n', stderr); +} + +warn (fmt, a, b, c, d) +char *fmt; +long a, b, c, d; +{ + fputs ("mk: warn: ", stderr); + fprintf (stderr, fmt, a, b, c, d); + if (errno != 0) + fprintf (stderr, ": %s", strerror (errno)); + fputc ('\n', stderr); +} +#endif /* HAVE_ERR_H */ + +#ifndef HAVE_FNMATCH +/* TODO: provide an actual implementation of fnmatch() */ +fnmatch (pattern, string, flags) +char *pattern, *string; +{ + return 0; +} +#endif + +#ifndef HAVE_BASENAME +char * +basename (s) +char *s; +{ + char *t; + + if (s == NULL || *s == '\0') + return "."; + + /* remove any trailing slashes */ + for (t = s + strlen (s); t > s && t[-1] == '/'; --t); + + if (s == t) + return "/"; + + *t = '\0'; + for (; t > s && t[-1] != '/'; --t); + return t; +} +#endif + +#ifndef HAVE_DIRNAME +char * +dirname (s) +char *s; +{ + char *t; + + if (s == NULL || *s == '\0') + return "."; + + /* remove any trailing slashes */ + for (t = s + strlen (s); t > s && t[-1] == '/'; --t); + + /* remove basename */ + for (; t > s && t[-1] != '/'; --t); + + /* remove any trailing slashes */ + for (; t > s && t[-1] == '/'; --t); + + if (s == t) + return "/"; + + *t = '\0'; + return s; +} +#endif + +#ifndef HAVE_STRDUP +char *strdup (s) +char *s; +{ + size_t len; + char *out; + + len = strlen (s) + 1; + out = malloc (len); + memcmp (out, s, len); + + return out; +} +#endif + +#ifndef HAVE_REALPATH +/* TODO: unfuck this unholy shit-infested junk */ +char * +realpath (path, resolved) +char *path, *resolved; +{ + if (resolved == NULL) + resolved = malloc (PATH_MAX); + + if (*path == '/') { + /* TODO: this is unsafe and stupid! */ + strncpy (resolved, path, PATH_MAX - 1); + resolved[PATH_MAX - 1] = '\0'; + } else { + /* TODO: this is even more unsafe and stupid!!! */ + getcwd (resolved, PATH_MAX); + strcat (resolved, "/"); + strcat (resolved, path); + } + + return resolved; +} +#endif + +#ifndef HAVE_STRSEP +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Get next token from string *stringp, where tokens are possibly-empty + * strings separated by characters from delim. + * + * Writes NULs into the string at *stringp to end tokens. + * delim need not remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, strsep returns NULL. + */ +char * +strsep (stringp, delim) +char **stringp, *delim; +{ + char *s, *spanp, *tok; + int c, sc; + + if ((s = *stringp) == NULL) + return NULL; + + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) { + s = NULL; + } else { + s[-1] = 0; + } + *stringp = s; + return tok; + } + } while (sc != 0); + } + /* NOTREACHED */ +} +#endif /* HAVE_STRSEP */ + +#ifndef HAVE_FMEMOPEN +FILE * +fmemopen (buffer, size, mode) +void *buffer; +size_t size; +char *mode; +{ + char *path, template[16]; + FILE *file; + + assert (mode != NULL); + memcpy (template, "/tmp/tmp.XXXXXX", 15); + + path = mktemp (template); + if (path == NULL) + return NULL; + + file = fopen (path, "w"); + if (file == NULL) + return NULL; + + fwrite (buffer, 1, size, file); + return freopen (path, mode, file); +} +#endif + +#ifndef HAVE_MEMMOVE +void * +memmove (dest, src, len) +void *dest, *src; +size_t len; +{ + unsigned char *d, *s; + + d = dest; + s = src; + + if (d < s) { + for (; len > 0; --len) + *d++ = *s++; + } else if (d > s) { + d += len; + s += len; + for (; len > 0; --len) + *--d = *--s; + } + + return dest; +} +#endif blob - dd858b3192b02ccf2382d12995d7b32a4ab44593 (mode 644) blob + /dev/null --- make/make.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef FILE_MAKE_H -#define FILE_MAKE_H -#include - -enum path_type { - PATH_NULL, - PATH_SUPER, - PATH_NAME, -}; -struct path { - enum path_type type; - char *name; -}; - -struct template { - struct template *next; - char *name; - char *text; -}; - -enum scope_type { - SC_DIR, - SC_CUSTOM, -}; -struct scope { - struct scope *next; - enum scope_type type; - char *name; /* optional */ - struct scope *parent; /* optional */ - char *makefile; /* required */ - int created; - union { - struct directory *dir; /* optional */ - struct custom *custom; /* required */ - }; -}; - -struct directory { - struct scope *subdirs; - struct file *fhead, *ftail; - struct macro *macros; - struct macro *emacros; /* exported macros */ - struct inference *infs; - struct template *templates; - char *default_file; - int done; -}; - -struct custom { - struct file *test, *exec; -}; - -struct dep { - struct dep *next, *prev; - struct path *path; - int obj; -}; - -struct file { - struct file *next, *prev; - char *name; - struct rule *rule; /* optional */ - struct dep *dhead, *dtail; - struct inference *inf; /* optional */ - struct timespec mtime; - char *help; /* optional */ - int obj, err; -}; - -struct inference { - struct inference *next; - char *from, *to; - struct rule *rule; - struct dep *dhead, *dtail; -}; - -struct rule { - char **code; -}; - -struct macro { - struct macro *next, *enext, *prepend; - char *name; - char *value; - char *help; - int lazy; -}; - -#endif /* FILE_MAKE_H */ blob - /dev/null blob + 880d2ea47648e60b0467c0970b55b2e665c427a8 (mode 644) --- /dev/null +++ make/compats.h @@ -0,0 +1,88 @@ +#ifndef __dead +# define __dead +#endif + +#ifndef HAVE_REALLOCARRAY +extern void *reallocarray (); +#endif /* HAVE_REALLOCARRAY */ + +#ifdef HAVE_ERR_H +# include +#else +extern __dead void errx (); +extern __dead void err (); +extern __dead void warnx (); +extern __dead void warn (); +#endif /* HAVE_ERR_H */ + +#ifdef HAVE_FNMATCH_H +# include +#else +extern fnmatch (); +#endif + +#ifdef HAVE_LIBGEN_H +# include +#else +extern char *basename (); +extern char *dirname (); +#endif /* HAVE_LIBGEN_H */ + +#ifndef HAVE_STRDUP +extern char *strdup (); +#endif + +#ifndef HAVE_STRSEP +extern char *strsep (); +#endif + +#ifndef HAVE_TIMESPEC +struct timespec { + time_t tv_sec; + long tv_nsec; +}; +#endif + +#ifndef HAVE_REALPATH +extern char *realpath (); +#endif + +#ifndef HAVE_FMEMOPEN +extern FILE *fmemopen (); +#endif + +#ifndef HAVE_MEMMOVE +extern void *memmove (); +#endif + +#ifndef WIFEXITED +# define WIFEXITED(ws) (((ws) & 0x00ff) == 0x0000) +#endif + +#ifndef WEXITSTATUS +# define WEXITSTATUS(ws) (((ws) >> 8) & 0xff) +#endif + +#ifndef PATH_MAX +# ifdef _POSIX_PATH_MAX +# define PATH_MAX _POSIX_PATH_MAX +# else +# define PATH_MAX 256 +# endif +#endif + +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif + +#ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 +#endif + +#ifndef STDERR_FILENO +# define STDERR_FILENO 2 +#endif + +#ifndef HAVE_LSTAT +# define lstat(fd, st) (stat ((fd), (st))) +#endif blob - /dev/null blob + ad2efb6621688c22652583365ad0e0e40e663c2c (mode 644) --- /dev/null +++ make/config.h @@ -0,0 +1,122 @@ +/* Define to 1 if you have the `basename' function. */ +#define HAVE_BASENAME 1 + +/* Does your shell support '-e' properly? */ +/* #undef HAVE_BROKEN_SHELL */ + +/* Define to 1 if you have the `clock_gettime' function. */ +#define HAVE_CLOCK_GETTIME 1 + +/* Define to 1 if your compiler supports C99s designated initializers */ +#define HAVE_DESIGNATED_INITIALIZERS 1 + +/* Define to 1 if you have the `dirname' function. */ +#define HAVE_DIRNAME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ERR_H 1 + +/* Define to 1 if you have the `fmemopen' function. */ +#define HAVE_FMEMOPEN 1 + +/* Define to 1 if you have the `fnmatch' function. */ +#define HAVE_FNMATCH 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FNMATCH_H 1 + +/* Define to 1 if you have the `fork' function. */ +#define HAVE_FORK 1 + +/* Define to 1 if you have the `ftime' function. */ +/* #undef HAVE_FTIME */ + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIBGEN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the `lstat' function. */ +#define HAVE_LSTAT 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `reallocarray' function. */ +#define HAVE_REALLOCARRAY 1 + +/* Define to 1 if you have the `realpath' function. */ +#define HAVE_REALPATH 1 + +/* Define to 1 if you have the 'st_mtim' field in 'struct stat' */ +#define HAVE_STAT_MTIM 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strsep' function. */ +#define HAVE_STRSEP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the 'timespec' struct */ +#define HAVE_TIMESPEC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vfork' function. */ +#define HAVE_VFORK 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_VFORK_H */ + +/* Define to 1 if `fork' works. */ +#define HAVE_WORKING_FORK 1 + +/* Define to 1 if `vfork' works. */ +#define HAVE_WORKING_VFORK 1 + +/* Default name of the makefile */ +#define MAKEFILE "Mkfile" + +/* Default value for SHELL variable */ +#define SHELL "sh" + +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + blob - /dev/null blob + cd83a897a5e509cb6744b5000d83ed998c87042e (mode 644) --- /dev/null +++ make/mk.c @@ -0,0 +1,3467 @@ +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#if HAVE_SYS_WAIT_H +# include +#endif +#if HAVE_SYS_TIME_H +# include +#endif +#if HAVE_FTIME +# include +#endif +#include +#include +#if HAVE_LIMITS_H +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include "compats.h" +#include "mk.h" + +#define new(T) ((T *)calloc (1, sizeof (T))) + +#ifndef MAKEFILE +# define MAKEFILE "Mkfile" +#endif + +#ifndef SHELL +# define SHELL "sh" +#endif + +static char *cpath, *objdir = NULL; +static int verbose = 0, cline = 0, conterr = 0; +static struct timespec time_zero; + +#if HAVE_DESIGNATED_DECLARATORS +# define FIELD(name, value) .name = value +#else +# define FIELD(name, value) value +#endif + +static struct macro m_shell = { + FIELD (next, NULL), + FIELD (enext, NULL), + FIELD (prepend, NULL), + FIELD (name, "SHELL"), + FIELD (value, SHELL), + FIELD (help, NULL), + FIELD (lazy, 0), +}, m_make = { + FIELD (next, &m_shell), + FIELD (enext, &m_shell), + FIELD (prepend, NULL), + FIELD (name, "MAKE"), + FIELD (value, NULL), + FIELD (help, NULL), + FIELD (lazy, 0), +}, m_dmake = { + FIELD (next, &m_make), + FIELD (enext, &m_make), + FIELD (prepend, NULL), + FIELD (name, ".MAKE"), + FIELD (value, NULL), + FIELD (help, NULL), + FIELD (lazy, 0), +}, m_makeflags = { + FIELD (next, &m_dmake), + FIELD (enext, &m_dmake), + FIELD (prepend, NULL), + FIELD (name, "MAKEFLAGS"), + FIELD (value, NULL), + FIELD (help, NULL), + FIELD (lazy, 0), +}, m_dmakeflags = { + FIELD (next, &m_makeflags), + FIELD (enext, &m_makeflags), + FIELD (prepend, NULL), + FIELD (name, ".MAKEFLAGS"), + FIELD (value, NULL), + FIELD (help, NULL), + FIELD (lazy, 0), +}; + +static struct macro *globals = &m_dmakeflags; + +/* STRING BUFFER */ + +typedef struct string { + char *ptr; + size_t len, cap; +} str_t; + +static str_t tmpstr; + +str_new (s) +str_t *s; +{ + s->len = 0; + s->cap = 10; + s->ptr = malloc (s->cap + 1); + return 0; +} + +str_reserve (s, n) +str_t *s; +size_t n; +{ + if (s->cap == 0) { + s->cap = n; + s->ptr = malloc (s->cap + 1); + } else if ((s->len + n) > s->cap) { + for (s->cap *= 2; (s->len + n) > s->cap; s->cap *= 2); + s->ptr = realloc (s->ptr, s->cap + 1); + } + return 0; +} + +str_free (s) +str_t *s; +{ + free (s->ptr); + memset (s, 0, sizeof (*s)); + return 0; +} + +str_putc (s, ch) +str_t *s; +{ + str_reserve (s, 1); + s->ptr[s->len++] = ch; + return 0; +} + +str_write (s, t, n) +str_t *s; +char *t; +size_t n; +{ + str_reserve (s, n); + memcpy (s->ptr + s->len, t, n); + s->len += n; + return 0; +} + +str_puts (s, t) +str_t *s; +char *t; +{ + return str_write (s, t, strlen (t)); +} + +str_last (s) +str_t *s; +{ + return s->len > 0 ? s->ptr[s->len - 1] : EOF; +} + +str_pop (s) +str_t *s; +{ + return s->len > 0 ? s->ptr[--s->len] : EOF; +} + +str_chomp (s) +str_t *s; +{ + while (str_last (s) == '\n') + str_pop (s); + return 0; +} + +str_trim (s) +str_t *s; +{ + size_t i; + + while (isspace (str_last (s))) + str_pop (s); + + for (i = 0; i < s->len && isspace (s->ptr[i]); ++i); + + memmove (s->ptr, s->ptr + i, s->len - i); + s->len -= i; + return 0; +} + +str_reset (s) +str_t *s; +{ + if (s->cap == 0) { + str_new (s); + } else { + s->len = 0; + } + return 0; +} + +char * +str_get (s) +str_t *s; +{ + s->ptr[s->len] = '\0'; + return s->ptr; +} + +char * +str_release (s) +str_t *s; +{ + char *t; + s->ptr[s->len] = '\0'; + t = realloc (s->ptr, s->len + 1); + memset (s, 0, sizeof (*s)); + return t; +} + +/* STRING MISC */ + +char * +xstrcat (s, t) +char *s, *t; +{ + char *u; + size_t len_s, len_t; + + len_s = strlen (s); + len_t = strlen (t); + u = malloc (len_s + len_t + 1); + memcpy (u, s, len_s); + memcpy (u + len_s, t, len_t + 1); + return u; +} + +skip_ws (s) +char **s; +{ + for (; isspace (**s); ++*s); + return 0; +} + +char * +ltrim (s) +char *s; +{ + skip_ws (&s); + return s; +} + +char * +rtrim (s) +char *s; +{ + size_t i, len; + + len = strlen (s); + for (i = len; i > 0 && isspace (s[i - 1]); --i) + s[i - 1] = '\0'; + + return s; +} + +char * +trim (s) +char *s; +{ + return ltrim (rtrim (s)); +} + +starts_with (s, prefix) +char *s, *prefix; +{ + size_t len_s, len_p; + + len_s = strlen (s); + len_p = strlen (prefix); + + if (len_s < len_p) + return 0; + + return memcmp (s, prefix, len_p) == 0; +} + +/* PATH LOGIC */ + +static struct path path_null = { FIELD (type, PATH_NULL), FIELD (name, NULL) }; +static struct path path_super = { FIELD (type, PATH_SUPER), FIELD (name, NULL) }; +static struct path tmppath = { FIELD (type, PATH_NAME), FIELD (name, NULL) }; + +/* return the number of path components (excl. PATH_NULL). */ +size_t +path_len (p) +struct path *p; +{ + size_t i; + + for (i = 0; p[i].type != PATH_NULL; ++i); + + return i; +} + +struct path * +path_cpy (old, old_len, new_len) +struct path *old; +size_t old_len, new_len; +{ + struct path *p; + + p = calloc (new_len + 1, sizeof (struct path)); + memcpy (p, old, old_len * sizeof (struct path)); + p[new_len].type = PATH_NULL; + + return p; +} + +struct path * +path_cat (old, comp) +struct path *old, *comp; +{ + struct path *p; + size_t len; + + len = path_len (old); + + switch (comp->type) { + case PATH_NULL: + p = path_cpy (old, len, len); + break; + case PATH_SUPER: + if (len > 0 && old[len - 1].type != PATH_SUPER) { + --len; + p = path_cpy (old, len, len); + break; + } + /* fallthrough */ + case PATH_NAME: + p = path_cpy (old, len, len + 1); + p[len] = *comp; + break; + default: + abort (); + } + + return p; +} + +path_write (s, p) +str_t *s; +struct path *p; +{ + size_t i; + + str_putc (s, '.'); + + for (i = 0; p[i].type != PATH_NULL; ++i) { + switch (p[i].type) { + case PATH_NULL: + abort (); + case PATH_SUPER: + str_puts (s, "/.."); + break; + case PATH_NAME: + str_putc (s, '/'); + str_puts (s, p[i].name); + break; + } + } + + return 0; +} + +char * +path_to_str (p) +struct path *p; +{ + str_reset (&tmpstr); + path_write (&tmpstr, p); + return str_get (&tmpstr); +} + +char * +path_basename (p) +struct path *p; +{ + char *s, *t; + + /* TODO: maybe allocate the buffer beforehand */ + s = realpath (path_to_str (p), NULL); + t = strdup (basename (s)); + free (s); + + return t; +} + +char * +path_cat_str (dir, file) +struct path *dir; +char *file; +{ + str_reset (&tmpstr); + path_write (&tmpstr, dir); + str_putc (&tmpstr, '/'); + str_puts (&tmpstr, file); + return str_get (&tmpstr); +} + +struct path * +parse_path (s) +char *s; +{ + size_t len = 0, cap = 4; + struct path *p; + char *t; + + p = calloc (cap + 1, sizeof (struct path)); + + while ((t = strsep (&s, "/")) != NULL) { + if (*t == '\0' || strcmp (t, ".") == 0) + continue; + + if (len == cap) { + cap *= 2; + p = reallocarray (p, cap + 1, sizeof (struct path)); + } + + if (strcmp (t, "..") == 0) { + p[len].type = PATH_SUPER; + } else { + p[len].type = PATH_NAME; + p[len].name = strdup (t); + } + ++len; + } + p[len].type = PATH_NULL; + + return p; +} + +sc_path_into (out, sc) +str_t *out; +struct scope *sc; +{ + if (sc->parent == NULL) { + str_putc (out, '.'); + return 0; + } + + sc_path_into (out, sc->parent); + str_putc (out, '/'); + str_puts (out, sc->name); + return 0; +} + +char * +sc_path_str (sc) +struct scope *sc; +{ + str_reset (&tmpstr); + sc_path_into (&tmpstr, sc); + return str_get (&tmpstr); +} + +write_objdir (out, sc) +str_t *out; +struct scope *sc; +{ + if (objdir != NULL) { + str_puts (out, objdir); + str_putc (out, '/'); + sc_path_into (out, sc); + } else { + str_putc (out, '.'); + } + return 0; +} + +/* OTHER MISC */ + +struct filetime { + struct timespec t; + int obj; +}; + +#if HAVE_STAT_MTIM +# define stat_get_mtime(mt, st) mt = st.st_mtim +#else +# define stat_get_mtime(mt, st) mt.tv_sec = st.st_mtime, mt.tv_nsec = 0 +#endif + +get_mtime (out, sc, dir, name) +struct filetime *out; +struct scope *sc; +struct path *dir; +char *name; +{ + extern char *path_cat_str (); + struct stat st; + char *path; + + if (verbose >= 2) + printf ("get_mtime('%s'): ", name); + + path = path_cat_str (dir, name); + + if (lstat (path, &st) == 0) { + stat_get_mtime (out->t, st); + out->obj = 0; + if (verbose >= 2) + printf ("found\n"); + return 0; + } + + if (objdir == NULL) + goto enoent; + + str_reset (&tmpstr); + write_objdir (&tmpstr, sc); + str_putc (&tmpstr, '/'); + str_puts (&tmpstr, name); + path = str_get (&tmpstr); + + if (lstat (path, &st) == 0) { + stat_get_mtime (out->t, st); + out->obj = 1; + if (verbose >= 2) + printf ("found in obj\n"); + return 0; + } + +enoent: + out->t = time_zero; + out->obj = 0; + if (verbose >= 2) + printf ("not found\n"); + return -1; + +} + +struct timespec +now () +{ + struct timespec t; + +#if HAVE_CLOCK_GETTIME + clock_gettime (CLOCK_REALTIME, &t); +#elif HAVE_GETTIMEOFDAY + struct timeval tv; + gettimeofday (&tv, NULL); + t.tv_sec = tv.tv_sec; + t.tv_nsec = tv.tv_sec; +#elif HAVE_FTIME + struct timeb tb; + ftime (&tb); + t.tv_sec = tb.time; + t.tv_nsec = (long)tb.millitm * 1000000; +#else + time_t tx; + tx = time (NULL); + t.tv_sec = tx; + t.tv_nsec = 0; +#endif + + return t; +} + +tv_cmp (a, b) +struct timespec *a, *b; +{ + if (a->tv_sec < b->tv_sec) { + return -1; + } else if (a->tv_sec > b->tv_sec) { + return 1; + } else if (a->tv_nsec < b->tv_nsec) { + return -1; + } else if (a->tv_nsec > b->tv_nsec) { + return 1; + } else { + return 0; + } +} + +#if NDEBUG +# define sc_dir(sc) ((sc)->dir) +# define sc_custom(sc) ((sc)->custom) +#else +struct directory * +sc_dir (sc) +struct scope *sc; +{ + assert (sc->type == SC_DIR); + return sc->inner.dir; +} + +struct custom * +sc_custom (sc) +struct scope *sc; +{ + assert (sc->type == SC_CUSTOM); + return sc->inner.custom; +} +#endif + +/* MKDIR */ + +mkdir_p (dir) +char *dir; +{ + struct stat st; + char *copy; + + if (verbose >= 2) + printf ("mkdir('%s');\n", dir); + + if (mkdir (dir, 0755) == 0) + return 0; + + if (errno == EEXIST) { + if (stat (dir, &st) != 0) + err (1, "mkdir_p('%s'): stat()", dir); + + if ((st.st_mode & S_IFMT) == S_IFDIR) + return 0; + + errx (1, "mkdir_p('%s'): Not a directory", dir); + } + + if (errno != ENOENT) + err (1, "mkdir_p('%s')", dir); + + copy = strdup (dir); + mkdir_p (dirname (copy)); + free (copy); + + if (mkdir (dir, 0755) != 0) + err (1, "mkdir('%s')", dir); + + return 0; +} + +sc_mkdir_p (sc) +struct scope *sc; +{ + if (objdir == NULL) + return 0; + + str_reset (&tmpstr); + str_puts (&tmpstr, objdir); + str_putc (&tmpstr, '/'); + sc_path_into (&tmpstr, sc); + + mkdir_p (str_get (&tmpstr)); + return 0; +} + +/* MACORS MISC */ + +/* is macro name */ +ismname (ch) +{ + return isalnum (ch) || ch == '_' || ch == '.'; +} + +/* SEARCHING */ + +struct macro * +find_emacro (sc, name) +struct scope *sc; +char *name; +{ + struct macro *m; + + if (sc == NULL) + return NULL; + + for (m = sc_dir (sc)->emacros; m != NULL; m = m->enext) { + if (strcmp (m->name, name) == 0) + return m; + } + + return find_emacro (sc->parent, name); +} + +struct macro * +find_macro (sc, name) +struct scope *sc; +char *name; +{ + struct macro *m; + + for (m = sc_dir (sc)->macros; m != NULL; m = m->next) { + if (strcmp (m->name, name) == 0) + return m; + } + + m = find_emacro (sc->parent, name); + if (m != NULL) + return m; + + for (m = globals; m != NULL; m = m->next) { + if (strcmp (m->name, name) == 0) + return m; + } + + return NULL; +} + +struct template * +find_template (sc, name) +struct scope *sc; +char *name; +{ + struct template *tm; + + for (tm = sc_dir (sc)->templates; tm != NULL; tm = tm->next) { + if (strcmp (tm->name, name) == 0) + return tm; + } + + return sc->parent != NULL ? find_template (sc->parent, name) : NULL; +} + +struct file * +find_file (dir, name) +struct directory *dir; +char *name; +{ + struct file *f; + + for (f = dir->ftail; f != NULL; f = f->prev) { + if (strcmp (name, f->name) == 0) + return f; + } + + return NULL; +} + +struct scope * +find_subdir (sc, name) +struct scope *sc; +char *name; +{ + struct scope *sub; + + for (sub = sc_dir (sc)->subdirs; sub != NULL; sub = sub->next) { + if (strcmp (sub->name, name) == 0) + return sub; + } + return NULL; +} + +/* MACRO EXPANSION */ + +#define MAX_EXPAND_DEPTH 64 +struct expand_ctx { + char *target; + struct dep *deps, *infdeps; + struct dep *dep0; + int free_target, depth; +}; + +static struct expand_ctx ctx_null = { + FIELD (target, NULL), + FIELD (deps, NULL), + FIELD (infdeps, NULL), + FIELD (dep0, NULL), + FIELD (free_target, 0), + FIELD (depth, 0), +}; + +ectx_init (ctx, target, dep0, deps, infdeps) +struct expand_ctx *ctx; +char *target; +struct dep *dep0; +struct dep *deps, *infdeps; +{ + ctx->target = target; + ctx->dep0 = dep0; + ctx->deps = deps; + ctx->infdeps = infdeps; + ctx->free_target = 0; + ctx->depth = 0; + return 0; +} + +struct expand_ctx * +ectx_null () +{ + ctx_null.depth = 0; + return &ctx_null; +} + +ectx_file (ctx, sc, f) +struct expand_ctx *ctx; +struct scope *sc; +struct file *f; +{ + str_t tmp; + + if (objdir != NULL) { + str_new (&tmp); + write_objdir (&tmp, sc); + str_putc (&tmp, '/'); + str_puts (&tmp, f->name); + ctx->target = str_release (&tmp); + ctx->free_target = 1; + } else { + ctx->target = f->name; + ctx->free_target = 0; + } + + ctx->deps = ctx->dep0 = f->dhead; + ctx->infdeps = f->inf != NULL ? f->inf->dhead : NULL; + if (ctx->dep0 == NULL && ctx->infdeps != NULL) + ctx->dep0 = ctx->infdeps; + ctx->depth = 0; + + return 0; +} + +ectx_free (ctx) +struct expand_ctx *ctx; +{ + if (ctx->free_target) + free (ctx->target); + return 0; +} + +replace_into (out, s, old, new_str) +str_t *out; +char *s, *old, *new_str; +{ + size_t len_s, len_old; + + len_s = strlen (s); + len_old = strlen (old); + + if (len_s < len_old || memcmp (s + len_s - len_old, old, len_old) != 0) { + str_puts (out, s); + return 0; + } + + str_write (out, s, len_s - len_old); + str_puts (out, new_str); + return 0; +} + +replace_all_into (out, s, old, new_str) +str_t *out; +char *s, *old, *new_str; +{ + char *t; + int x = 0; + + while ((t = strsep (&s, " \t")) != NULL) { + if (*t == '\0') + continue; + + replace_into (out, t, old, new_str); + str_putc (out, ' '); + x = 1; + } + + if (x) + str_pop (out); + + return 0; +} + +pr_export (out, sc, prefix, m, ctx) +str_t *out; +struct scope *sc; +struct path *prefix; +struct macro *m; +struct expand_ctx *ctx; +{ + extern expand_macro_into (); + + str_puts (out, m->name); + str_puts (out, "='"); + expand_macro_into (out, sc, prefix, m, m->name, ctx); + str_putc (out, '\''); + return 0; +} + +dep_write (out, sc, dep) +str_t *out; +struct scope *sc; +struct dep *dep; +{ + if (dep->obj && objdir != NULL) { + write_objdir (out, sc); + str_putc (out, '/'); + } + path_write (out, dep->path); + return 0; +} + +expand_special_into (out, sc, prefix, name, ctx) +str_t *out; +struct scope *sc; +struct path *prefix; +char *name; +struct expand_ctx *ctx; +{ + struct scope *sub; + struct macro *m; + struct dep *dep; + int i, j; + + /* TODO: .SUBDIRS, .EXPORTS */ + assert (ctx != NULL); + + if (strcmp (name, ".SUBDIRS") == 0) { + if (sc->type != SC_DIR) { + invsc: + errx (1, "%s: invalid scope type", sc_path_str (sc)); + } + + sub = sc_dir (sc)->subdirs; + if (sub == NULL) + return 0; + + str_puts (out, sub->name); + for (sub = sub->next; sub != NULL; sub = sub->next) { + str_putc (out, ' '); + str_puts (out, sub->name); + } + } else if (strcmp (name, ".EXPORTS") == 0) { + if (sc->type != SC_DIR) + goto invsc; + + m = sc_dir (sc)->emacros; + if (m == NULL) + return 0; + + pr_export (out, sc, prefix, m, ctx); + for (m = m->enext; m != NULL; m = m->enext) { + str_putc (out, ' '); + pr_export (out, sc, prefix, m, ctx); + } + + } else if (strcmp (name, ".OBJDIR") == 0) { + write_objdir (out, sc); + } else if (strcmp (name, ".TARGET") == 0) { + if (ctx->target == NULL) + errx (1, "%s: cannot use $@ or ${.TARGET} here", sc_path_str (sc)); + str_puts (out, ctx->target); + } else if (strcmp (name, ".IMPSRC") == 0) { + if (ctx->dep0 == NULL) + errx (1, "%s: cannot use $< or ${.IMPSRC} here", sc_path_str (sc)); + + if (ctx->dep0 == NULL) + return 0; + + dep_write (out, sc, ctx->dep0); + } else if (strcmp (name, ".ALLSRC") == 0) { + if (ctx->target == NULL) + errx (1, "%s: cannot use $^ or ${.ALLSRC} here", sc_path_str (sc)); + + for (dep = ctx->deps; dep != NULL; dep = dep->next) { + str_putc (out, ' '); + dep_write (out, sc, dep); + } + for (dep = ctx->infdeps; dep != NULL; dep = dep->next) { + str_putc (out, ' '); + dep_write (out, sc, dep); + } + } else if (strcmp (name, ".TOPDIR") == 0) { + str_putc (out, '.'); + for (sub = sc->parent; sub != NULL; sub = sub->parent) + str_puts (out, "/.."); + } else if (strcmp (name, ".MAKEFILES") == 0) { + for (i = 0, sub = sc; sub != NULL; ++i, sub = sub->parent) { + for (j = 0; j < i; ++j) + str_puts (out, "../"); + str_puts (out, sub->makefile); + str_putc (out, ' '); + } + str_pop (out); + } + return 0; +} + +/* ${name} just the value of macro called `name` + * ${name:old=new} replace `old` with `new`, must be the last modifier + * ${name:U} replace each word with its upper case equivalent + * ${name:L} replace each word with its lower case equivalent + * ${name:F} try searching for files in either ${.OBJDIR} or source directory + * ${name:E} replace each word with its suffix + * ${name:R} replace each word with everything but its suffix + * ${name:H} replace each word with its dirname() equvialent + * ${name:T} replace each word with its basename() equvialent + * ${name:m1:m2...} multiple modifiers can be combined + * ${name:Mpattern} select only words that match pattern + * ${name:Npattern} opposite of :Mpattern + * TODO: + * somehow make this function shorter + */ +subst2 (out, sc, prefix, s, ctx) +str_t *out; +struct scope *sc; +struct path *prefix; +char **s; +struct expand_ctx *ctx; +{ + extern char *expand_macro (); + extern expand_macro_into (); + extern subst (); + struct macro *m; + struct filetime ft; + char *orig = *s, *t, *u, *v, *w, *pattern; + str_t name, old_str, new_str; + + ++ctx->depth; + if (ctx->depth >= MAX_EXPAND_DEPTH) + errx (1, "%s: reached maximum expansion depth", path_to_str (prefix)); + + /* parse macro name */ + str_new (&name); + while (**s != '\0') { + if (ismname (**s)) { + str_putc (&name, **s); + ++*s; + } else if (**s == '$') { + ++*s; + subst (&name, sc, prefix, s, ctx); + } else { + break; + } + } + + m = find_macro (sc, str_get (&name)); + if (**s == '}') { + ++*s; + expand_macro_into (out, sc, prefix, m, str_get (&name), ctx); + str_free (&name); + return 0; + } + + v = expand_macro (sc, prefix, m, str_get (&name), ctx); + str_free (&name); + + str_new (&old_str); + + while (**s == ':') { + ++*s; + + /* parse modifier, TODO: move this into a function */ + for (str_reset (&old_str); **s != '\0' && **s != '}' && **s != ':' && **s != '='; ) { + switch (**s) { + case '$': + ++*s; + subst (&old_str, sc, prefix, s, ctx); + break; + case '\\': + ++*s; + /* fallthrough */ + default: + str_putc (&old_str, **s); + ++*s; + break; + } + } + + if (**s == '=') { + ++*s; + + /* TODO: move this into a function */ + for (str_new (&new_str); **s != '\0' && **s != '}'; ) { + switch (**s) { + case '$': + ++*s; + subst (&new_str, sc, prefix, s, ctx); + break; + case '\\': + ++*s; + /* fallthrough */ + default: + str_putc (&new_str, **s); + ++*s; + break; + } + } + + replace_all_into (out, v, str_get (&old_str), str_get (&new_str)); + str_free (&new_str); + goto ret; + } else if (strcmp (str_get (&old_str), "U") == 0) { + str_new (&new_str); + + for (t = v; *t != '\0'; ++t) + str_putc (&new_str, toupper (*t)); + + free (v); + v = str_release (&new_str); + } else if (strcmp (str_get (&old_str), "L") == 0) { + str_new (&new_str); + + for (t = v; *t != '\0'; ++t) + str_putc (&new_str, tolower (*t)); + + free (v); + v = str_release (&new_str); + } else if (strcmp (str_get (&old_str), "F") == 0) { + str_new (&new_str); + + for (t = v; (w = strsep (&t, " \t")) != NULL; ) { + if (*w == '\0') + continue; + + if (get_mtime (&ft, sc, prefix, w) == 0 && ft.obj) { + write_objdir (&new_str, sc); + str_putc (&new_str, '/'); + } + str_puts (&new_str, w); + str_putc (&new_str, ' '); + } + str_pop (&new_str); + + free (v); + v = str_release (&new_str); + } else if (strcmp (str_get (&old_str), "E") == 0) { + str_new (&new_str); + + for (t = v; (w = strsep (&t, " \t")) != NULL; ) { + if (*w == '\0') + continue; + + u = strrchr (w, '.'); + if (u == NULL || strchr (u, '/') != NULL) + continue; + + str_puts (&new_str, u); + str_putc (&new_str, ' '); + } + + str_pop (&new_str); + free (v); + v = str_release (&new_str); + } else if (strcmp (str_get (&old_str), "R") == 0) { + str_new (&new_str); + + for (t = v; (w = strsep (&t, " \t")) != NULL; ) { + if (*w == '\0') + continue; + + u = strrchr (w, '.'); + if (u != NULL && strchr (u, '/') == NULL) + *u = '\0'; + + str_puts (&new_str, w); + str_putc (&new_str, ' '); + } + + str_pop (&new_str); + free (v); + v = str_release (&new_str); + } else if (strcmp (str_get (&old_str), "H") == 0) { + str_new (&new_str); + + for (t = v; (w = strsep (&t, " \t")) != NULL; ) { + if (*w == '\0') + continue; + + str_puts (&new_str, dirname (w)); + str_putc (&new_str, ' '); + } + + str_pop (&new_str); + free (v); + v = str_release (&new_str); + } else if (strcmp (str_get (&old_str), "T") == 0) { + str_new (&new_str); + + for (t = v; (w = strsep (&t, " \t")) != NULL; ) { + if (*w == '\0') + continue; + + str_puts (&new_str, basename (w)); + str_putc (&new_str, ' '); + } + + str_pop (&new_str); + free (v); + v = str_release (&new_str); + } else if (str_get (&old_str)[0] == 'M') { + str_new (&new_str); + + pattern = str_get (&old_str) + 1; + + for (t = v; (w = strsep (&t, " \t")) != NULL; ) { + if (*w == '\0') + continue; + + if (fnmatch (pattern, w, 0) != 0) + continue; + + str_puts (&new_str, w); + str_putc (&new_str, ' '); + } + + str_pop (&new_str); + free (v); + v = str_release (&new_str); + } else if (str_get (&old_str)[0] == 'N') { + str_new (&new_str); + + pattern = str_get (&old_str) + 1; + + for (t = v; (w = strsep (&t, " \t")) != NULL; ) { + if (*w == '\0') + continue; + + if (fnmatch (pattern, w, 0) == 0) + continue; + + str_puts (&new_str, w); + str_putc (&new_str, ' '); + } + + str_pop (&new_str); + free (v); + v = str_release (&new_str); + } else { + errx (1, "%s: invalid modifier: ':%s' in '${%s'", sc_path_str (sc), str_get (&old_str), orig); + } + } + + str_puts (out, v); + +ret: + if (**s != '}') + goto invalid; + ++*s; + str_free (&old_str); + free (v); + --ctx->depth; + return 0; +invalid: + errx (1, "%s: invalid macro expansion: '${%s', s = '%s'", sc_path_str (sc), orig, *s); +} + +subst (out, sc, prefix, s, ctx) +str_t *out; +struct scope *sc; +struct path *prefix; +char **s; +struct expand_ctx *ctx; +{ + char *t; + int ch; + + ch = **s; + ++*s; + switch (ch) { + case '$': + str_putc (out, '$'); + break; + case '.': + expand_special_into (out, sc, prefix, ".TOPDIR", ctx); + break; + case '@': + expand_special_into (out, sc, prefix, ".TARGET", ctx); + break; + case '<': + expand_special_into (out, sc, prefix, ".IMPSRC", ctx); + break; + case '^': + expand_special_into (out, sc, prefix, ".ALLSRC", ctx); + break; + case '*': + t = ".IMPSRC:T}"; + subst2 (out, sc, prefix, &t, ctx); + break; + case '{': + subst2 (out, sc, prefix, s, ctx); + break; + case '(': + errx (1, "%s: syntax error: $(...) syntax is reserved for future use, please use ${...} instead.", sc_path_str (sc)); + default: + errx (1, "%s: syntax error: invalid escape sequence: $%c%s", sc_path_str (sc), ch, *s); + } + + return 0; +} + +expand_into (out, sc, prefix, s, ctx) +str_t *out; +struct scope *sc; +struct path *prefix; +char *s; +struct expand_ctx *ctx; +{ + while (*s != '\0') { + if (*s != '$') { + str_putc (out, *s++); + continue; + } + ++s; + subst (out, sc, prefix, &s, ctx); + } + return 0; +} + +expand_macro_into (out, sc, prefix, m, name, ctx) +str_t *out; +struct scope *sc; +struct path *prefix; +struct macro *m; +char *name; +struct expand_ctx *ctx; +{ + if (m == NULL) + return expand_special_into (out, sc, prefix, name, ctx); + + if (m->prepend != NULL) { + expand_macro_into (out, sc, prefix, m->prepend, m->prepend->name, ctx); + str_putc (out, ' '); + } + + if (m->value == NULL) + return 0; + + if (m->lazy) { + expand_into (out, sc, prefix, m->value, ctx); + } else { + str_puts (out, m->value); + } + return 0; +} + +char * +expand_macro (sc, prefix, m, name, ctx) +struct scope *sc; +struct path *prefix; +struct macro *m; +char *name; +struct expand_ctx *ctx; +{ + str_t tmp; + + str_new (&tmp); + expand_macro_into (&tmp, sc, prefix, m, name, ctx); + return str_release (&tmp); +} + +char * +expand (sc, prefix, s, ctx) +struct scope *sc; +struct path *prefix; +char *s; +struct expand_ctx *ctx; +{ + str_t out; + + if (ctx == NULL) + ctx = ectx_null (); + + str_new (&out); + expand_into (&out, sc, prefix, s, ctx); + str_trim (&out); + return str_release (&out); +} + +/* COMMAND EXECUTION */ + +char * +evalcom (sc, dir, cmd) +struct scope *sc; +struct path *dir; +char *cmd; +{ + char *args[4]; + ssize_t i, n; + str_t data; + pid_t pid; + int pipefd[2]; + char buf[64 + 1]; + + args[0] = m_shell.value; + args[1] = "-c"; + args[2] = expand (sc, dir, cmd, NULL); + args[3] = NULL; + + if (pipe (pipefd) != 0) + err (1, "pipe()"); + + pid = fork (); + if (pid == -1) + err (1, "fork()"); + + if (pid == 0) { + close (STDOUT_FILENO); + close (pipefd[0]); + if (dup (pipefd[1]) != STDOUT_FILENO) + err (1, "failed to dup"); + close (STDIN_FILENO); + if (open ("/dev/null", O_RDONLY) != STDIN_FILENO) + err (1, "failed to open /dev/null"); + close (pipefd[1]); + + if (chdir (path_to_str (dir)) != 0) + err (1, "failed to chdir"); + + execvp (m_shell.value, args); + err (1, "failed to launch shell"); + } else { + close (pipefd[1]); + + str_new (&data); + + while ((n = read (pipefd[0], buf, sizeof (buf) - 1)) > 0) { + for (i = 0; i < n; ++i) + str_putc (&data, buf[i]); + } + close (pipefd[0]); + wait (NULL); + } + + free (args[2]); + str_chomp (&data); + + return str_release (&data); +} + +runcom (sc, prefix, cmd, ctx, rule) +struct scope *sc; +struct path *prefix; +char *cmd, *rule; +struct expand_ctx *ctx; +{ + char *ecmd, *args[5]; + pid_t pid; + int i = 0, q = 0, ws, ign = 0; + + if (*cmd == '@') { + q = 1; + ++cmd; + } else if (*cmd == '-') { + ign = 1; + ++cmd; + } else if (verbose < 0) { + q = 1; + } + + ecmd = expand (sc, prefix, cmd, ctx); + + if (!q) { + printf ("[%s%s%s] $ %s\n", + path_to_str (prefix), + rule != NULL ? "/" : "", + rule != NULL ? rule : "", + verbose ? ecmd : cmd + ); + } + + pid = fork (); + if (pid < 0) + err (1, "fork()"); + + args[i++] = m_shell.value; +#ifndef HAVE_BROKEN_SHELL + if (!ign) + args[i++] = "-e"; +#endif + args[i++] = "-c"; + args[i++] = ecmd; + args[i] = NULL; + + if (pid == 0) { + close (STDIN_FILENO); + if (open ("/dev/null", O_RDONLY) != STDIN_FILENO) + warn ("%d: open('/dev/null')", STDIN_FILENO); + + if (chdir (path_to_str (prefix)) != 0) + err (126, "chdir()"); + + execvp (m_shell.value, args); + err (127, "exec('%s')", ecmd); + } else { + free (ecmd); + if (wait (&ws) != pid) { + warn ("wait()"); + return 254; + } + + if (!WIFEXITED (ws)) { + warnx ("%d: process didn't exit", (int)pid); + return 255; + } + + return ign ? 0 : WEXITSTATUS (ws); + } +} + +/* EXPRESSION PARSER */ + +is_truthy (s) +char *s; +{ + char *endp; + long x; + + if (*s == '\0') + return 0; + + x = strtol (s, &endp, 0); + + return *endp != '\0' || x != 0; +} + +e_command (s, cmd, arg) +char **s, *cmd; +str_t *arg; +{ + char *orig = *s; + + *s += strlen (cmd); + skip_ws (s); + if (**s != '(') + errx (1, "%s:%d: expected '(' after 'defined': %s", cpath, cline, orig); + + str_new (arg); + for (++*s; **s != ')'; ++*s) + str_putc (arg, **s); + ++*s; + + str_trim (arg); + return 0; +} + +e_atom (sc, prefix, s, val) +struct scope *sc; +struct path *prefix; +char **s; +str_t *val; +{ + str_t arg; + int x; + + skip_ws (s); + + if (**s == '"') { + ++*s; + + while (**s != '"') { + if (**s == '$') { + ++*s; + subst (val, sc, prefix, s, ectx_null ()); + } else { + str_putc (val, **s); + ++*s; + } + } + ++*s; + } else if (starts_with (*s, "defined")) { + e_command (s, "defined", &arg); + x = find_macro (sc, str_get (&arg)) != NULL; + comm: + str_putc (val, x ? '1' : '0'); + str_free (&arg); + } else if (starts_with (*s, "target")) { + e_command (s, "target", &arg); + x = find_file (sc_dir (sc), str_get (&arg)) != NULL; + goto comm; + } else { + errx (1, "%s:%d: invalid expression: '%s'", cpath, cline, *s); + } + + return 0; +} + +e_unary (sc, prefix, s, val) +struct scope *sc; +struct path *prefix; +char **s; +str_t *val; +{ + skip_ws (s); + if (**s != '!') + return e_atom (sc, prefix, s, val); + ++*s; + + e_unary (sc, prefix, s, val); + + if (is_truthy (str_get (val))) { + str_reset (val); + str_putc (val, '0'); + } else { + str_reset (val); + str_putc (val, '1'); + } + + return 0; +} + +enum { + COMP_EQ, + COMP_NE, + COMP_LT, + COMP_LE, + COMP_GT, + COMP_GE, +}; + +e_comp (sc, prefix, s) +struct scope *sc; +struct path *prefix; +char **s; +{ + str_t left, right; + char *sl, *sr, *el, *er; + long il, ir; + int cmp, x, icmp; + + str_new (&left); + e_unary (sc, prefix, s, &left); + + skip_ws (s); + + if (starts_with (*s, "==")) { + cmp = COMP_EQ; + *s += 2; + } else if (starts_with (*s, "!=")) { + cmp = COMP_NE; + *s += 2; + } else if (**s == '<') { + ++*s; + if (**s == '=') { + cmp = COMP_LE; + ++*s; + } else { + cmp = COMP_LT; + ++*s; + } + } else if (**s == '>') { + ++*s; + if (**s == '=') { + cmp = COMP_GE; + ++*s; + } else { + cmp = COMP_GT; + ++*s; + } + } else { + str_trim (&left); + x = is_truthy (str_get (&left)); + str_free (&left); + return x; + } + + skip_ws (s); + str_new (&right); + e_unary (sc, prefix, s, &right); + + str_trim (&left); + str_trim (&right); + + sl = str_get (&left); + sr = str_get (&right); + il = strtol (sl, &el, 0); + ir = strtol (sr, &er, 0); + icmp = *sl != '\0' && *sr != '\0' && *el == '\0' && *er == '\0'; + x = icmp ? il - ir : strcmp (sl, sr); + switch (cmp) { + case COMP_EQ: + x = x == 0; + break; + case COMP_NE: + x = x != 0; + break; + case COMP_LT: + x = x < 0; + break; + case COMP_LE: + x = x <= 0; + break; + case COMP_GT: + x = x > 0; + break; + case COMP_GE: + x = x >= 0; + break; + default: + abort (); + } + + str_free (&left); + str_free (&right); + return x; +} + +e_and (sc, prefix, s) +struct scope *sc; +struct path *prefix; +char **s; +{ + int x; + + x = e_comp (sc, prefix, s); + while (skip_ws (s), starts_with (*s, "&&")) { + *s += 2; + x &= e_comp (sc, prefix, s); + } + + return x; +} + +e_or (sc, prefix, s) +struct scope *sc; +struct path *prefix; +char **s; +{ + int x; + + x = e_and (sc, prefix, s); + while (skip_ws (s), starts_with (*s, "||")) { + *s += 2; + x |= e_and (sc, prefix, s); + } + + return x; +} + +parse_expr (sc, prefix, s) +struct scope *sc; +struct path *prefix; +char *s; +{ + s = trim (s); + return e_or (sc, prefix, &s); +} + +/* PARSER */ + +char * +readline (file, ln) +FILE *file; +int *ln; +{ + str_t line; + int ch, eof = 1; + + str_new (&line); + + while (1) { + ch = fgetc (file); + switch (ch) { + case EOF: + goto ret; + case '\n': + eof = 0; + ++*ln; + goto ret; + case '\\': + ch = fgetc (file); + if (ch == '\n') { + ++*ln; + } else { + str_putc (&line, '\\'); + str_putc (&line, ch); + } + eof = 0; + break; + default: + str_putc (&line, ch); + eof = 0; + break; + } + } + +ret: + return eof ? NULL : str_release (&line); +} + +struct scope * +new_subdir (parent, name) +struct scope *parent; +char *name; +{ + struct directory *pdir; + struct scope *sub; + + assert (name != NULL); + + pdir = sc_dir (parent); + + sub = new (struct scope); + sub->next = pdir->subdirs; + sub->type = SC_DIR; + sub->name = name; + sub->parent = parent; + sub->makefile = NULL; + sub->created = 0; + pdir->subdirs = sub; + + return sub; +} + +struct macro * +new_macro (name, value, help, lazy, prepend) +char *name, *value, *help; +struct macro *prepend; +{ + struct macro *m; + char *s; + + assert (name != NULL); + assert (value != NULL); + + /* check name */ + if (*name == '\0') + errx (1, "new_macro(): macro name is empty"); + for (s = name; *s != '\0'; ++s) { + if (!ismname (*s)) + errx (1, "invalid macro name: '%s'", name); + } + + m = new (struct macro); + m->next = NULL; + m->enext = NULL; + m->prepend = prepend; + m->name = name; + m->value = value; + m->help = help; + m->lazy = lazy; + + return m; +} + +struct file * +new_file (name, rule, time, dhead, dtail, help, inf, obj) +char *name, *help; +struct rule *rule; +struct timespec time; +struct dep *dhead, *dtail; +struct inference *inf; +{ + struct file *f; + + assert (name != NULL); + + f = new (struct file); + f->next = f->prev = NULL; + f->name = name; + f->rule = rule; + f->dhead = dhead; + f->dtail = dtail; + f->mtime = time; + f->help = help; + f->inf = inf; + f->obj = obj; + f->err = 0; + + return f; +} + +struct dep * +new_dep (path) +struct path *path; +{ + struct dep *d; + + assert (path != NULL); + + d = new (struct dep); + d->next = d->prev = NULL; + d->path = path; + + return d; +} + +struct dep * +new_dep_name (name) +char *name; +{ + struct path *p; + + assert (name != NULL); + + p = calloc (2, sizeof (struct path)); + p[0].type = PATH_NAME; + p[0].name = name; + p[1].type = PATH_NULL; + + return new_dep (p); +} + +dir_add_file (dir, f) +struct directory *dir; +struct file *f; +{ + assert (f->next == NULL && f->prev == NULL); + + if (dir->fhead != NULL) { + f->prev = dir->ftail; + dir->ftail->next = f; + } else { + dir->fhead = f; + } + dir->ftail = f; + return 0; +} + +file_add_deps (file, dhead, dtail) +struct file *file; +struct dep *dhead, *dtail; +{ + assert (dhead->prev == NULL); + assert (dtail->next == NULL); + + if (file->dhead != NULL) { + dhead->prev = file->dtail; + file->dtail->next = dhead; + } else { + file->dhead = dhead; + } + file->dtail = dtail; + return 0; +} + +file_add_dep (file, dep) +struct file *file; +struct dep *dep; +{ + return file_add_deps (file, dep, dep); +} + +char * +strip_comment (s) +char *s; +{ + char *t, *o = s; + + while ((t = strchr (s, '#')) != NULL) { + if (t == o || isspace (t[-1])) { + *t = '\0'; + break; + } + s = t + 1; + } + + return o; +} + +/* .SUBDIRS: cc make sys # comment */ +parse_subdirs (sc, dir, s) +struct scope *sc; +struct path *dir; +char *s; +{ + struct scope *sub; + char *subdir, *name, *path; + + strip_comment (s); + + while ((subdir = strsep (&s, " \t")) != NULL) { + if (*subdir == '\0') + continue; + + name = strdup (trim (subdir)); + sub = new_subdir (sc, name); + sub->type = SC_DIR; + sub->makefile = MAKEFILE; + + path = path_cat_str (dir, sub->name); + if (access (path, F_OK) != 0) + errx (1, "%s:%d: directory not found: %s", cpath, cline, sub->name); + } + + return 0; +} + +/* .FOREIGN: libfoo libbar # comment */ +parse_foreign (sc, s) +struct scope *sc; +char *s; +{ + struct custom *cs; + struct scope *sub; + char *subdir, *name; + + strip_comment (s); + + while ((subdir = strsep (&s, " \t")) != NULL) { + if (*subdir == '\0') + continue; + + name = strdup (trim (subdir)); + sub = new_subdir (sc, name); + sub->type = SC_CUSTOM; + sub->makefile = NULL; + cs = new (struct custom); + cs->test = NULL; + cs->exec = NULL; + sub->inner.custom = cs; + } + + return 0; +} + +/* .EXPORTS: CC CFLAGS # comment */ +parse_exports (sc, s) +struct scope *sc; +char *s; +{ + struct macro *m; + char *name; + + strip_comment (s); + + while ((name = strsep (&s, " \t")) != NULL) { + if (*name == '\0') + continue; + + /* check if the macro is already exported */ + for (m = sc_dir (sc)->emacros; m != NULL; m = m->enext) { + if (strcmp (m->name, name) == 0) + goto cont; /* already exported */ + } + + m = find_macro (sc, name); + if (m == NULL) + errx (1, "%s:%d: no such macro: '%s'", cpath, cline, name); + + m->enext = sc_dir (sc)->emacros; + sc_dir (sc)->emacros = m; + + cont:; + } + + return 0; +} + +try_add_custom (sc, f) +struct scope *sc; +struct file *f; +{ + struct scope *sub; + char *name; + size_t len; + int ch; + + len = strlen (f->name); + ch = f->name[len - 1]; + if (ch != '?' && ch != '!') + return 0; + + name = strdup (f->name); + name[len - 1] = '\0'; + sub = find_subdir (sc, name); + if (sub == NULL) + errx (1, "%s: not a subdir: %s", sc_path_str (sc), name); + + if (sub->type != SC_CUSTOM) + errx (1, "%s: not a custom subdir: %s", sc_path_str (sc), name); + + if (ch == '?') { + sub->inner.custom->test = f; + } else { + sub->inner.custom->exec = f; + } + + free (name); + return 1; +} + +/* TODO: impl .SUFFIXES: */ +is_inf (s) +char *s; +{ + char *d; + int x; + + if (*s != '.') + return 0; + ++s; + d = strchr (s, '.'); + + if (d == NULL) { + return strlen (s) <= 3; + } + + *d = '\0'; + x = strlen (s) <= 3 && strlen (d + 1) <= 3; + *d = '.'; + return x; +} + +struct rule * +parse_rule (sc, dir, s, t, help) +struct scope *sc; +struct path *dir; +char *s, *t, *help; +{ + struct inference *inf; + struct filetime ft; + struct rule *r; + struct file *f; + struct dep *dep, *dhead, *dtail; + char *u, *v, *p; + int flag; + + r = new (struct rule); + r->code = NULL; + dhead = dtail = NULL; + + *t = '\0'; + + /* parse deps */ + v = u = expand (sc, dir, t + 1, NULL); + while ((p = strsep (&v, " \t")) != NULL) { + if (*p == '\0') + continue; + + dep = new_dep (parse_path (p)); + if (dhead != NULL) { + dep->prev = dtail; + dtail->next = dep; + } else { + dhead = dep; + } + dtail = dep; + } + free (u); + + /* parse targets */ + u = expand (sc, dir, s, NULL); + flag = 1; + if (is_inf (u)) { + p = strchr (u + 1, '.'); + inf = new (struct inference); + inf->next = sc_dir (sc)->infs; + inf->rule = r; + inf->dhead = dhead; + inf->dtail = dtail; + + if (p != NULL) { + *p = '\0'; + inf->from = strdup (u); + *p = '.'; + inf->to = strdup (p); + } else { + inf->from = strdup (u); + inf->to = ""; + } + + sc_dir (sc)->infs = inf; + } else { + v = u; + while ((p = strsep (&v, " \t")) != NULL) { + if (*p == '\0') + continue; + /* TODO: check name */ + + f = find_file (sc_dir (sc), p); + if (f == NULL) { + get_mtime (&ft, sc, dir, p); + + f = new_file ( + /* name */ strdup (p), + /* rule */ r, + /* time */ ft.t, + /* dhead*/ dhead, + /* dtail*/ dtail, + /* help */ help, + /* inf */ NULL, + /* obj */ ft.obj + ); + /* TODO: maybe first do try_add_custom()? */ + dir_add_file (sc_dir (sc), f); + try_add_custom (sc, f); + continue; + } + + flag = 0; + + if (f->help == NULL) + f->help = help; + + if (dhead != NULL) + file_add_deps (f, dhead, dtail); + } + } + free (u); + return flag ? r : NULL; +} + +parse_assign (sc, dir, s, t, help) +struct scope *sc; +struct path *dir; +char *s, *t, *help; +{ + struct macro *m, *prepend = NULL; + char *value; + int lazy; + + if (t[-1] == '!') { + t[-1] = '\0'; + lazy = 0; + value = evalcom (sc, dir, trim (t + 1)); + } else if (t[-1] == '?') { + /* handle both `?=` and `??=` */ + if (s == (t - 1)) + errx (1, "why are you doing this to me Davids?"); + + if (t[-2] == '?') { + t[-2] = '\0'; + value = getenv (trim (s)); + if (value == NULL) { + m = find_macro (sc, trim (s)); + if (m != NULL) + value = m->value; + } + if (value == NULL) + value = strdup (trim (t + 1)); + } else { + t[-1] = '\0'; + m = find_macro (sc, trim (s)); + value = m != NULL ? m->value : strdup (trim (t + 1)); + } + lazy = 1; + } else if (t[-1] == ':') { + /* handle both `:=` and `::=` */ + if (s == (t - 1)) + errx (1, "why are you doing this to me Davids, again?"); + t[t[-2] == ':' ? -2 : -1] = '\0'; + value = expand (sc, dir, trim (t + 1), NULL); + lazy = 0; + } else if (t[-1] == '+') { + t[-1] = '\0'; + value = strdup (trim (t + 1)); + prepend = find_macro (sc, trim (s)); + lazy = 1; + } else { + value = strdup (trim (t + 1)); + lazy = 1; + } + + m = new_macro ( + /* name */ strdup (trim (s)), + /* value */ value, + /* help */ help, + /* lazy */ lazy, + /*prepend*/ prepend + ); + m->next = sc_dir (sc)->macros; + sc_dir (sc)->macros = m; + return 0; +} + + +#define IF_VAL 0x01 +#define IF_HAS 0x02 +#define MAX_IFSTACK 16 + +walkifstack (s, n) +char *s; +size_t n; +{ + size_t i; + + for (i = 0; i < n; ++i) { + if (!(s[i] & IF_VAL)) + return 0; + } + return 1; +} + +is_directive (out, s, name) +char **out, *s, *name; +{ + size_t len; + + if (*s != '.') + return 0; + ++s; + + skip_ws (&s); + len = strlen (name); + if (strncmp (s, name, len) != 0) + return 0; + + s += len; + + if (*s != '\0' && !isspace (*s)) + return 0; + + if (out != NULL) + *out = trim (s); + return 1; +} + +is_target (out, s, name) +char **out, *s, *name; +{ + size_t len; + + len = strlen (name); + if (strncmp (s, name, len) != 0) + return 0; + + s += len; + skip_ws (&s); + if (*s != ':') + return 0; + ++s; + + if (out != NULL) + *out = trim (s); + + return 1; +} + +do_parse (sc, dir, path, file) +struct scope *sc; +struct path *dir; +char *path; +FILE *file; +{ + extern parse (); + struct template *tm; + struct rule *r = NULL; + size_t len, cap, iflen = 0; + char *s, *t, *u, *help = NULL; + char ifstack[MAX_IFSTACK]; + int x, run, oldcline; + FILE *tfile; + str_t text; + + assert (sc->type == SC_DIR); + cpath = path; + + if (verbose >= 3) { + printf ("Parsing dir '%s' ...\n", path_to_str (dir)); + } + + cline = 1; + + for (; (s = readline (file, &cline)) != NULL; free (s)) { + run = walkifstack (ifstack, iflen); + if (s[0] == '#' && s[1] == '#') { + help = expand (sc, dir, trim (s + 2), NULL); + continue; + } else if (s[0] == '#' || *trim (s) == '\0') { + continue; + } else if (starts_with (s, "include ")) { + if (!run) + goto cont; + + strip_comment (s); + t = expand (sc, dir, trim (s + 8), NULL); + if (*t == '/') { + u = t; + } else { + u = strdup (path_cat_str (dir, t)); + } + oldcline = cline; + parse (sc, dir, u); + cline = oldcline; + if (u != t) + free (u); + free (t); + } else if (starts_with (s, "-include ") || starts_with (s, "sinclude ")) { + if (!run) + goto cont; + + strip_comment (s); + t = expand (sc, dir, trim (s + 9), NULL); + if (*t == '/') { + u = t; + } else { + u = strdup (path_cat_str (dir, t)); + } + + if (access (t, R_OK) == 0) + parse (sc, dir, u); + + if (u != t) + free (u); + free (t); + } else if (is_directive (&t, s, "include")) { + if (!run) + goto cont; + + errx (1, "%s:%d: please use .SUBDIRS: or .FOREIGN: now", path, cline); + } else if (is_directive (&t, s, "if")) { + if (iflen == MAX_IFSTACK) + errx (1, "%s:%d: maximum .if depth of %d reached", path, cline, MAX_IFSTACK); + x = parse_expr (sc, dir, t) & 0x01; + ifstack[iflen++] = x * (IF_VAL | IF_HAS); + } else if (is_directive (NULL, s, "else")) { + if (iflen == 0) + errx (1, "%s:%d: not in .if", path, cline); + t = &ifstack[iflen - 1]; + *t = (!(*t & IF_HAS) * (IF_VAL | IF_HAS)) | (*t & IF_HAS); + } else if (is_directive (&t, s, "elif")) { + if (iflen == 0) + errx (1, "%s:%d: not in .if", path, cline); + x = parse_expr (sc, dir, t); + t = &ifstack[iflen - 1]; + *t = ((!(*t & IF_HAS) && x) * (IF_VAL | IF_HAS)) | (*t & IF_HAS); + } else if (is_directive (NULL, s, "endif")) { + if (iflen == 0) + errx (1, "%s:%d: not in .if", path, cline); + --iflen; + } else if (is_directive (&t, s, "template")) { + str_new (&text); + + tm = new (struct template); + tm->next = sc_dir (sc)->templates; + tm->name = strdup (strip_comment (t)); + + for (free (s); (s = readline (file, &cline)) != NULL; free (s)) { + if (is_directive (NULL, s, "endt") || is_directive (NULL, s, "endtemplate")) + break; + + str_puts (&text, s); + str_putc (&text, '\n'); + } + + if (run) { + tm->text = str_release (&text); + sc_dir (sc)->templates = tm; + } else { + free (tm); + str_free (&text); + } + } else if (is_directive (&t, s, "expand")) { + if (!run) + goto cont; + tm = find_template (sc, strip_comment (t)); + if (tm == NULL) + errx (1, "%s:%d: no such template: %s", cpath, cline, t); + tfile = fmemopen (tm->text, strlen (tm->text), "r"); + do_parse (sc, dir, "template", tfile); + fclose (tfile); + } else if (is_target (&t, s, ".DEFAULT")) { + if (run) + sc_dir (sc)->default_file = strdup (strip_comment (t)); + } else if (is_target (NULL, s, ".POSIX")) { + if (run) + warnx ("%s:%d: this is not a POSIX-compatible make", path, cline); + } else if (is_target (NULL, s, ".SUFFIXES")) { + if (run) + warnx ("%s:%d: this make doesn't require .SUFFIXES", path, cline); + } else if (is_target (&t, s, ".SUBDIRS")) { + if (run) + parse_subdirs (sc, dir, t); + } else if (is_target (&t, s, ".FOREIGN")) { + if (run) + parse_foreign (sc, t); + } else if (is_target (&t, s, ".EXPORTS")) { + if (run) + parse_exports (sc, t); + } else if (s[0] == '\t') { + if (!run) + goto cont; + + if (r == NULL) + errx (1, "%s:%d: syntax error", path, cline); + + if (len == cap) { + cap *= 2; + r->code = reallocarray (r->code, cap + 1, sizeof (char *)); + } + + r->code[len++] = strdup (s + 1); + r->code[len] = NULL; + } else if ((t = strchr (s, '=')) != NULL) { + if (!run) + goto cont; + + if (s == t) + errx (1, "%s:%d: invalid macro name", path, cline); + + *t = '\0'; + parse_assign (sc, dir, s, t, help); + } else if ((t = strchr (s, ':')) != NULL) { + if (!run) + goto cont; + + r = parse_rule (sc, dir, s, t, help); + if (r == NULL) + goto cont; + + len = 0; + cap = 1; + r->code = calloc (cap + 1, sizeof (char *)); + r->code[0] = NULL; + } else { + warnx ("%s:%d: invalid line: %s", path, cline, s); + } + + cont: + help = NULL; + } + + return 0; +} + +parse (sc, dir, path) +struct scope *sc; +struct path *dir; +char *path; +{ + struct directory *dirx; + FILE *file; + + file = fopen (path, "r"); + if (file == NULL) { + /* err (1, "fopen(\"%s\")", path); */ + dirx = new (struct directory); + dirx->subdirs = NULL; + dirx->fhead = NULL; + dirx->ftail = NULL; + dirx->done = 0; + sc->inner.dir = dirx; + return 0; + } + + if (sc->inner.dir == NULL) { + dirx = new (struct directory); + dirx->subdirs = NULL; + dirx->fhead = NULL; + dirx->ftail = NULL; + dirx->done = 0; + sc->inner.dir = dirx; + } else if (sc_dir (sc)->done) { + errx (1, "%s: parsing this file again?", path); + } + + do_parse (sc, dir, path, file); + sc_dir (sc)->done = 1; + + fclose (file); + return 0; +} + +parse_dir (sc, dir) +struct scope *sc; +struct path *dir; +{ + char *path; + + path = strdup (path_cat_str (dir, sc->makefile)); + parse (sc, dir, path); + free (path); + + return 0; +} + +struct scope * +parse_recursive (dir, makefile) +struct path *dir; +char *makefile; +{ + struct path *mfpath, *ppath; + struct scope *sc, *parent; + char *path, *name; + + tmppath.name = makefile; + mfpath = path_cat (dir, &tmppath); + path = path_to_str (mfpath); + if (access (path, F_OK) != 0) { + sc = NULL; + goto ret; + } + + ppath = path_cat (dir, &path_super); + parent = parse_recursive (ppath, makefile); + if (parent == NULL) + parent = parse_recursive (ppath, MAKEFILE); + free (ppath); + + name = path_basename (dir); + + if (parent != NULL) { + if (parent->type != SC_DIR) + errx (1, "%s: invalid parent type", path_to_str (mfpath)); + + for (sc = sc_dir (parent)->subdirs; sc != NULL; sc = sc->next) { + if (strcmp (sc->name, name) == 0) { + if (sc->type != SC_DIR) + errx (1, "%s: invalid type", path_to_str (mfpath)); + goto parse; + } + } + + goto create; + } else { + create: + sc = new (struct scope); + sc->type = SC_DIR; + sc->name = name; + sc->inner.dir = NULL; + } + +parse: + sc->makefile = makefile; + sc->parent = parent; + path = strdup (path_to_str (mfpath)); + parse (sc, dir, path); + free (path); + +ret: + free (mfpath); + return sc; +} + +struct path * +parse_subdir (prefix, sub) +struct path *prefix; +struct scope *sub; +{ + struct path *np; + + tmppath.name = sub->name; + np = path_cat (prefix, &tmppath); + parse_dir (sub, np); + return np; +} + +/* INFERENCE RULES */ + +char * +replace_suffix (name, sufx) +char *name, *sufx; +{ + char *out, *ext; + size_t len_name, len_sufx; + + ext = strrchr (name, '.'); + len_name = ext != NULL ? (size_t)(ext - name) : strlen (name); + len_sufx = strlen (sufx); + + out = malloc (len_name + len_sufx + 1); + memcpy (out, name, len_name); + memcpy (out + len_name, sufx, len_sufx); + out[len_name + len_sufx] = '\0'; + + return out; +} + +/* instantiate a new file from an inference rule */ +struct file * +inst_inf (sc, inf, name) +struct scope *sc; +struct inference *inf; +char *name; +{ + struct file *f; + struct dep *dep; + + dep = new_dep_name (replace_suffix (name, inf->from)); + + f = new_file ( + /* name */ strdup (name), + /* rule */ inf->rule, + /* time */ time_zero, + /* deps */ dep, + /* dtail*/ dep, + /* help */ NULL, + /* inf */ inf, + /* obj */ 0 + ); + dir_add_file (sc_dir (sc), f); + + return f; +} + +/* instantiate an inference rule on an existing file */ +inf_inst_file (f, inf) +struct file *f; +struct inference *inf; +{ + struct dep *dep; + + assert (f->inf == NULL); + /* TODO: this is ugly */ + assert (f->rule == NULL || f->rule->code == NULL || *f->rule->code == NULL); + + dep = new_dep_name (replace_suffix (f->name, inf->from)); + + /* prepend dependency to file */ + if (f->dhead != NULL) { + dep->next = f->dhead; + f->dhead->prev = dep; + } + f->dhead = dep; + f->inf = inf; + f->rule = inf->rule; + return 0; +} + +struct inference * +find_inf (sc, dir, name) +struct scope *sc; +struct path *dir; +char *name; +{ + extern struct file *try_find (); + struct inference *inf; + struct file *sf; + char *sn, *base; + char *ext; + + ext = strrchr (name, '.'); + base = strdup (name); + if (ext == NULL) { + ext = ""; + } else { + base[ext - name] = '\0'; + } + + for (inf = sc_dir (sc)->infs; inf != NULL; inf = inf->next) { + if (strcmp (inf->to, ext) == 0) { + sn = xstrcat (base, inf->from); + sf = find_file (sc_dir (sc), sn); + if (sf == NULL) + sf = try_find (sc, dir, sn); + free (sn); + if (sf != NULL) + break; + } + } + + free (base); + return inf != NULL || sc->parent == NULL ? inf : find_inf (sc->parent, dir, name); +} + +struct file * +try_find (sc, dir, name) +struct scope *sc; +struct path *dir; +char *name; +{ + struct inference *inf; + struct filetime ft; + struct file *f; + + get_mtime (&ft, sc, dir, name); + if (tv_cmp (&ft.t, &time_zero) <= 0) { + inf = find_inf (sc, dir, name); + if (inf == NULL) + return NULL; + return inst_inf (sc, inf, name); + } + + f = new_file ( + /* name */ strdup (name), + /* rule */ NULL, + /* time */ ft.t, + /* deps */ NULL, + /* dtail*/ NULL, + /* help */ NULL, + /* inf */ NULL, + /* obj */ ft.obj + ); + dir_add_file (sc_dir (sc), f); + + return f; +} + +/* BUILDING */ + +struct build { + struct timespec t; + struct file *f; + int obj; +}; + +build_init (out, t, f, obj) +struct build *out; +struct timespec t; +struct file *f; +{ + out->t = t; + out->f = f; + out->obj = obj; + return 0; +} + +build_deps (sc, dhead, prefix, mt, maxt, needs_update) +struct scope *sc; +struct dep *dhead; +struct path *prefix; +struct timespec *mt, *maxt; +int *needs_update; +{ + extern build_dir (); + struct build b; + struct dep *dep; + int ec = 0; + + for (dep = dhead; dep != NULL; dep = dep->next) { + if (build_dir (&b, sc, dep->path, prefix) == 0) { + dep->obj = b.obj; + + if (tv_cmp (&b.t, mt) >= 0) + *needs_update = 1; + if (tv_cmp (&b.t, maxt) > 0) + *maxt = b.t; + } else { + ec = 1; + if (!conterr) + break; + } + } + + return ec; +} + +/* TODO: refactor this function, to be less complicated */ +build_file (out, sc, name, prefix) +struct build *out; +struct scope *sc; +char *name; +struct path *prefix; +{ + extern build_dir (); + int needs_update; + struct scope *sub; + struct path *new_prefix, xpath[2]; + struct file *f; + struct timespec maxt; + struct inference *inf; + struct expand_ctx ctx; + struct filetime ft; + struct dep *dep, xdep; + struct build b; + char **s; + int ec, rc; + + if (verbose >= 2) { + printf ("dir %s", path_to_str (prefix)); + if (name) + printf (" (%s)", name); + printf (" ...\n"); + } + + if (!sc->created) { + sc_mkdir_p (sc); + sc->created = 1; + } + + switch (sc->type) { + case SC_DIR: + /* lazily parse subdirectories */ + if (sc_dir (sc) == NULL) + parse_dir (sc, prefix); + + /* if no rule specified to build, use the default rule */ + if (name == NULL && sc_dir (sc)->default_file != NULL) + name = sc_dir (sc)->default_file; + + if (name != NULL) { + /* try finding an explicitly defined file */ + f = find_file (sc_dir (sc), name); + + if (f == NULL) { + /* try finding and building a subdirectory */ + sub = find_subdir (sc, name); + if (sub != NULL) { + tmppath.name = name; + new_prefix = path_cat (prefix, &tmppath); + ec = build_file (out, sub, NULL, new_prefix); + free (new_prefix); + return ec; + } + + /* try finding an inference rule */ + f = try_find (sc, prefix, name); + if (f == NULL) + errx (1, "%s: no such file: %s", sc_path_str (sc), name); + } else { + get_mtime (&ft, sc, prefix, name); + f->mtime = ft.t; + f->obj = ft.obj; + } + } else { + f = sc_dir (sc)->fhead; + if (f == NULL) + errx (1, "%s: nothing to build", sc_path_str (sc)); + } + + /* if this file has no rule, try to find an inference rule */ + if (f->rule == NULL || *f->rule->code == NULL) { + /* try finding an inference rule */ + inf = name != NULL ? find_inf (sc, prefix, name) : NULL; + + if (inf != NULL) { + /* instantiate inference rule */ + inf_inst_file (f, inf); + } else if (f->rule == NULL) { + if (tv_cmp (&f->mtime, &time_zero) > 0) { + build_init (out, f->mtime, f, f->obj); + return 0; + } else { + errx (1, "%s: no rule to build: %s", sc_path_str (sc), name); + } + } + } + + needs_update = (tv_cmp (&f->mtime, &time_zero) <= 0); + maxt = f->mtime; + + if (f->err) + return 1; + + /* build dependencies and record timestamps */ + if (build_deps (sc, f->dhead, prefix, &f->mtime, &maxt, &needs_update) != 0) { + f->err = 1; + if (!conterr) + return 1; + } + + /* build dependencies from inference rule */ + if (f->inf != NULL) { + if (build_deps (sc, f->inf->dhead, prefix, &f->mtime, &maxt, &needs_update) != 0) { + f->err = 1; + if (!conterr) + return 1; + } + } + + if (!needs_update) { + build_init (out, f->mtime, f, f->obj); + return 0; + } + + if (f->err) + return 1; + + s = f->rule->code; + + /* rule is a "sum" rule, so doesn't need to be built */ + if (s == NULL || *s == NULL) { + build_init (out, maxt, f, f->obj); + return 0; + } + + /* run commands */ + ectx_file (&ctx, sc, f); + for (; *s != NULL; ++s) { + if ((rc = runcom (sc, prefix, *s, &ctx, name)) != 0) { + fprintf (stderr, "%s: command failed with %d: %s\n", sc_path_str (sc), rc, *s); + f->err = 1; + return 1; + } + } + ectx_free (&ctx); + + /* update timestamp */ + get_mtime (&ft, sc, prefix, f->name); + f->mtime = ft.t; + f->obj = ft.obj; + build_init (out, f->mtime, f, f->obj); + return 0; + case SC_CUSTOM: + /* run the "subdir?" rule, to test if the target needs to be updated */ + new_prefix = path_cat (prefix, &path_super); + if (name != NULL) { + xpath[0].type = PATH_NAME; + xpath[0].name = name; + xpath[1].type = PATH_NULL; + } + + xdep.next = xdep.prev = NULL; + xdep.path = xpath; + xdep.obj = 0; + + f = sc_custom (sc)->test; + if (f != NULL) { + assert (f->inf == NULL); + + ec = 0; + for (dep = f->dhead; dep != NULL; dep = dep->next) { + if (build_dir (&b, sc->parent, dep->path, new_prefix) != 0) { + ec = 1; + if (!conterr) + return ec; + } + } + + if (ec != 0) + return ec; + + ectx_init ( + /* ctx */ &ctx, + /* target */ prefix[path_len (prefix) - 1].name, + /* dep0 */ name != NULL ? &xdep : NULL, + /* deps */ f->dhead, + /* infdeps*/ NULL + ); + + needs_update = 0; + for (s = f->rule->code; *s != NULL; ++s) { + if (runcom (sc->parent, new_prefix, *s, &ctx, name) != 0) { + needs_update = 1; + break; + } + } + } else { + needs_update = 1; + } + + if (!needs_update) { + if (name != NULL && get_mtime (&ft, sc, prefix, name) == 0) { + build_init (out, ft.t, NULL, ft.obj); + } else { + build_init (out, time_zero, NULL, 0); + } + return 0; + } + + /* run the "subdir!" rule */ + f = sc_custom (sc)->exec; + if (f == NULL) + errx (1, "%s: missing '%s!' rule", sc_path_str (sc->parent), sc->name); + assert (f->inf == NULL); + + ectx_init ( + /* ctx */ &ctx, + /* target */ prefix[path_len (prefix) - 1].name, + /* dep0 */ name != NULL ? &xdep : NULL, + /* deps */ f->dhead, + /* infdeps*/ NULL + ); + + ec = 0; + for (dep = f->dhead; dep != NULL; dep = dep->next) { + if (build_dir (&b, sc->parent, dep->path, new_prefix) != 0) { + ec = 1; + if (!conterr) + return ec; + } + } + if (ec != 0) + return ec; + + for (s = f->rule->code; *s != NULL; ++s) { + if ((rc = runcom (sc->parent, new_prefix, *s, &ctx, name)) != 0) { + fprintf (stderr, "%s: command failed with %d: %s\n", sc_path_str (sc->parent), rc, *s); + return 1; + } + } + + free (new_prefix); + if (name != NULL && get_mtime (&ft, sc, prefix, name) == 0) { + build_init (out, ft.t, NULL, ft.obj); + } else { + build_init (out, now (), NULL, 0); + } + return 0; + } + + abort (); +} + +build_dir (out, sc, path, prefix) +struct build *out; +struct scope *sc; +struct path *path, *prefix; +{ + struct path *new_prefix; + struct scope *sub; + int ec; + + switch (path[0].type) { + case PATH_SUPER: + new_prefix = path_cat (prefix, &path[0]); + ec = build_dir (out, sc->parent, path + 1, new_prefix); + free (new_prefix); + return ec; + case PATH_NULL: + return build_file (out, sc, NULL, prefix); + case PATH_NAME: + if (path[1].type == PATH_NULL) + return build_file (out, sc, path[0].name, prefix); + + if (sc->type != SC_DIR) + errx (1, "%s: invalid path", sc_path_str (sc)); + + if (sc_dir (sc) == NULL) + parse_dir (sc, prefix); + + new_prefix = path_cat (prefix, &path[0]); + + sub = find_subdir (sc, path[0].name); + if (sub == NULL) + errx (1, "%s: invalid subdir: %s", sc_path_str (sc), path[0].name); + + ec = build_dir (out, sub, path + 1, new_prefix); + free (new_prefix); + return ec; + } + + abort (); +} + +build (out, sc, path) +struct build *out; +struct scope *sc; +struct path *path; +{ + return build_dir (out, sc, path, &path_null); +} + +/* HELP */ + +help_macros (sc) +struct scope *sc; +{ + struct macro *m; + + assert (sc_dir (sc) != NULL); + + for (m = sc_dir (sc)->macros; m != NULL; m = m->next) { + if (m->help == NULL) + continue; + + printf ("%-30s- %s\n", m->name, m->help); + } + + return 0; +} + +help_files (prefix, sc) +struct path *prefix; +struct scope *sc; +{ + struct path *new_prefix; + struct scope *sub; + struct file *f; + char *p; + int n; + + p = path_to_str (prefix); + if (strcmp (p, ".") == 0) { + p = NULL; + } else { + p += 2; /* skip ./ */ + } + + for (f = sc_dir (sc)->fhead; f != NULL; f = f->next) { + if (f->help == NULL) + continue; + + n = 0; + if (p != NULL) + n += printf ("%s/", p); + n += printf ("%s", f->name); + printf ("%-*s- %s\n", n < 30 ? 30 - n : 0, "", f->help); + } + + if (!verbose) + return 0; + + for (sub = sc_dir (sc)->subdirs; sub != NULL; sub = sub->next) { + if (sub->type != SC_DIR) + continue; + + new_prefix = parse_subdir (prefix, sub); + help_files (new_prefix, sub); + free (new_prefix); + } + + return 0; +} + +help (prefix, sc) +struct path *prefix; +struct scope *sc; +{ + extern usage (); + usage (1); + + fputs ("\nOPTIONS:\n", stderr); + fputs ("-C dir - chdir(dir)\n", stderr); + fprintf (stderr, "-f file - read `file` instead of \"%s\"\n", MAKEFILE); + fputs ("-o objdir - put build artifacts into objdir\n", stderr); + fputs ("-V var - print expanded version of var\n", stderr); + fputs ("-h - print help page\n", stderr); + fputs ("-hv - print help page, recursively\n", stderr); + fputs ("-p - dump tree\n", stderr); + fputs ("-pv - dump tree, recursively\n", stderr); + fputs ("-s - do not echo commands\n", stderr); + fputs ("-k - continue processing after errors are encountered\n", stderr); + fputs ("-S - stop processing when errors are encountered (default)\n", stderr); + fputs ("-v - verbose output\n", stderr); + + fputs ("\nMACROS:\n", stderr); + help_macros (sc); + + fputs ("\nTARGETS:\n", stderr); + help_files (prefix, sc); + + return 1; +} + +/* DUMP */ + +print_sc (prefix, sc) +struct path *prefix; +struct scope *sc; +{ + struct path *new_prefix; + struct inference *inf; + struct scope *sub; + struct macro *m; + struct dep *dep; + struct file *f; + struct rule *r; + char **s; + + if (verbose) + printf ("=== %s\n", sc_path_str (sc)); + + if (sc->type != SC_DIR || sc_dir (sc) == NULL) + errx (1, "%s: print_sc(): must be of type SC_DIR", sc_path_str (sc)); + + if (sc_dir (sc)->default_file != NULL) + printf (".DEFAULT: %s\n", sc_dir (sc)->default_file); + + for (m = sc_dir (sc)->macros; m != NULL; m = m->next) { + if (m->help != NULL) + printf ("\n## %s\n", m->help); + printf ("%s %s= %s\n", m->name, m->prepend != NULL ? "+" : "", m->value); + } + + printf ("\n"); + + for (f = sc_dir (sc)->fhead; f != NULL; f = f->next) { + if (f->help != NULL) + printf ("## %s\n", f->help); + printf ("%s:", f->name); + + for (dep = f->dhead; dep != NULL; dep = dep->next) + printf (" %s", path_to_str (dep->path)); + if (f->inf != NULL) { + for (dep = f->inf->dhead; dep != NULL; dep = dep->next) + printf (" %s", path_to_str (dep->path)); + } + printf ("\n"); + + r = f->rule; + if (r != NULL && r->code != NULL) { + for (s = r->code; *s != NULL; ++s) + printf ("\t%s\n", *s); + } + printf ("\n"); + } + + for (inf = sc_dir (sc)->infs; inf != NULL; inf = inf->next) { + printf ("%s%s:", inf->from, inf->to); + for (dep = inf->dhead; dep != NULL; dep = dep->next) + printf (" %s", path_to_str (dep->path)); + printf ("\n"); + if (inf->rule->code != NULL) { + for (s = inf->rule->code; *s != NULL; ++s) + printf ("\t%s\n", *s); + printf ("\n"); + } + } + + for (sub = sc_dir (sc)->subdirs; sub != NULL; sub = sub->next) { + switch (sub->type) { + case SC_DIR: + printf (".include %s, DIR", sub->name); + if (sc->makefile != NULL) + printf (", %s", sc->makefile); + printf ("\n"); + break; + case SC_CUSTOM: + printf (".include %s, CUSTOM\n", sub->name); + break; + } + } + + if (verbose) { + for (sub = sc_dir (sc)->subdirs; sub != NULL; sub = sub->next) { + if (sub->type != SC_DIR) + continue; + + printf ("\n"); + + new_prefix = parse_subdir (prefix, sub); + print_sc (new_prefix, sub); + free (new_prefix); + } + } + + return 0; +} + +/* MAIN */ + +usage (uc) +{ + fprintf (stderr, "%s: %s [-hkpsSv] [-C dir] [-f makefile] [-o objdir] [-V var] [target...]\n", uc ? "USAGE" : "usage", m_make.value); + return 1; +} + +do_V (sc, V) +struct scope *sc; +char *V; +{ + size_t len; + char *s; + + if (strchr (V, '$') != 0) { + s = V; + } else { + len = strlen (V); + s = malloc (len + 4); + s[0] = '$'; + s[1] = '{'; + memcpy (s + 2, V, len); + s[len + 2] = '}'; + s[len + 3] = '\0'; + } + + puts (expand (sc, &path_null, s, NULL)); + return 0; +} + +main (argc, argv) +char **argv; +{ + extern char *optarg; + extern optind; + str_t cmdline; + struct scope *sc; + struct path *path; + struct macro *m; + struct build b; + char *s, *cd = NULL, *makefile = MAKEFILE, *V = NULL, *odir = NULL; + int i, option, pr = 0, n = 0, dohelp = 0; + + m_dmake.value = m_make.value = argv[0]; + + str_new (&cmdline); + while ((option = getopt (argc, argv, "hpsvkSC:f:V:o:")) != -1) { + switch (option) { + case 'h': + dohelp = 1; + break; + case 'p': + str_puts (&cmdline, " -p"); + pr = 1; + break; + case 's': + str_puts (&cmdline, " -s"); + verbose = -1; + break; + case 'v': + str_puts (&cmdline, " -v"); + ++verbose; + break; + case 'C': + cd = optarg; + break; + case 'f': + makefile = optarg; + break; + case 'V': + V = optarg; + break; + case 'o': + odir = optarg; + break; + case 'k': + conterr = 1; + break; + case 'S': + conterr = 0; + break; + case '?': + return usage (0); + default: + errx (1, "unexpected option: -%c", option); + } + } + + if (odir != NULL) { + mkdir_p (odir); + objdir = realpath (odir, malloc (PATH_MAX)); + if (objdir == NULL) + err (1, "realpath()"); + + if (verbose >= 3) + printf ("objdir = '%s'\n", objdir); + } + + if (cd != NULL && chdir (cd) != 0) + err (1, "chdir()"); + + argv += optind; + argc -= optind; + + for (i = 0; i < argc; ++i) { + s = strchr (argv[i], '='); + if (s == NULL) + continue; + + str_putc (&cmdline, ' '); + str_puts (&cmdline, argv[i]); + + *s = '\0'; + + m = new_macro ( + /* name */ trim (argv[i]), + /* value */ trim (s + 1), + /* help */ NULL, + /* lazy */ 0, + /*prepend*/ NULL + ); + m->next = globals; + globals = m; + + argv[i] = NULL; + } + + str_trim (&cmdline); + m_dmakeflags.value = m_makeflags.value = str_release (&cmdline); + + path = parse_path ("."); + sc = parse_recursive (path, makefile); + if (sc == NULL) + errx (1, "failed to find or parse makefile"); + + if (dohelp) + return help (path, sc); + + if (pr) { + print_sc (path, sc); + return 0; + } + + if (V != NULL) + return do_V (sc, V); + + free (path); + + for (i = 0; i < argc; ++i) { + if (argv[i] == NULL) + continue; + + path = parse_path (argv[i]); + if (build (&b, sc, path) != 0) + return 1; + free (path); + ++n; + } + + return n == 0 ? build (&b, sc, &path_null) : 0; +} + blob - /dev/null blob + c10f1b2118b78d99453995c111774e9e811189d7 (mode 644) --- /dev/null +++ make/mk.h @@ -0,0 +1,88 @@ +#ifndef FILE_MAKE_H +#define FILE_MAKE_H + +enum path_type { + PATH_NULL, + PATH_SUPER, + PATH_NAME, +}; +struct path { + enum path_type type; + char *name; +}; + +struct template { + struct template *next; + char *name; + char *text; +}; + +enum scope_type { + SC_DIR, + SC_CUSTOM, +}; +struct scope { + struct scope *next; + enum scope_type type; + char *name; /* optional */ + struct scope *parent; /* optional */ + char *makefile; /* required */ + int created; + union { + struct directory *dir; /* optional */ + struct custom *custom; /* required */ + } inner; +}; + +struct directory { + struct scope *subdirs; + struct file *fhead, *ftail; + struct macro *macros; + struct macro *emacros; /* exported macros */ + struct inference *infs; + struct template *templates; + char *default_file; + int done; +}; + +struct custom { + struct file *test, *exec; +}; + +struct dep { + struct dep *next, *prev; + struct path *path; + int obj; +}; + +struct file { + struct file *next, *prev; + char *name; + struct rule *rule; /* optional */ + struct dep *dhead, *dtail; + struct inference *inf; /* optional */ + struct timespec mtime; + char *help; /* optional */ + int obj, err; +}; + +struct inference { + struct inference *next; + char *from, *to; + struct rule *rule; + struct dep *dhead, *dtail; +}; + +struct rule { + char **code; /* optional */ +}; + +struct macro { + struct macro *next, *enext, *prepend; + char *name; /* required */ + char *value; /* required */ + char *help; /* optional */ + int lazy; +}; + +#endif /* FILE_MAKE_H */ blob - /dev/null blob + e4d36670a0647ed418f774336e26e1a0d9e140b8 (mode 644) --- /dev/null +++ make/test/Mkfile @@ -0,0 +1,25 @@ +.SUBDIRS: libfoo +.FOREIGN: libbar +.EXPORTS: CC + +all: libs.txt + +clean: libfoo/clean libbar/clean + rm -f libs.txt + +distclean: clean libbar/distclean + rm -f libbar-configure + +libs.txt: libfoo/libfoo.txt libbar/libbar.txt + cat $^ >$@ + +libbar-configure: + (cd libbar && ./configure) + touch $@ + +libbar?: + test -e libbar/Makefile + gmake -q -C $@ $< + +libbar!: libbar-configure + gmake -C $@ ${.EXPORTS} $< blob - /dev/null blob + bd758e9206aa9142682a558e65db36a976cbcb0b (mode 644) --- /dev/null +++ make/test/libfoo/Mkfile @@ -0,0 +1,8 @@ + +all: libfoo.txt + +clean: + rm -f libfoo.txt + +libfoo.txt: + echo libfoo >$@ blob - b4f935b0573d788e185bfa9c621b737305c0379b (mode 644) blob + /dev/null --- sys/MyMakefile +++ /dev/null @@ -1,39 +0,0 @@ -LDFLAGS = -s --no-pie - -OBJ = loader.o kernel.o floppy.o -IMG = sys.img - -all: ${IMG} - -install: - @echo "'install' rule not suppported for sys" - -xxd: ${IMG} - xxd $< | less - -clean: - rm -f ${.OBJDIR}/*.o ${.OBJDIR}/*.elf ${.OBJDIR}/*.bin ${.OBJDIR}/*.img - -run: ${IMG} - qemu-system-i386 -M pc -m 1M -fda $< - -floppy1440.img: sys.bin - cat $< /dev/zero | dd of=$@ bs=512 count=2880 - -sys.img: sys.bin - cat $< /dev/zero | dd of=$@ bs=512 count=128 - -sys.bin: sys.elf - objcopy -O binary $< $@ - -sys.elf: linker.ld ${OBJ} - ld -o $@ -T linker.ld ${OBJ:F} ${LDFLAGS} - -user.bin: user.asm - nasm -fbin -o $@ $< - -.asm.o: - nasm -felf32 -o $@ $< -I${.OBJDIR} - -kernel.o: user.bin - blob - /dev/null blob + b4f935b0573d788e185bfa9c621b737305c0379b (mode 644) --- /dev/null +++ sys/Mkfile @@ -0,0 +1,39 @@ +LDFLAGS = -s --no-pie + +OBJ = loader.o kernel.o floppy.o +IMG = sys.img + +all: ${IMG} + +install: + @echo "'install' rule not suppported for sys" + +xxd: ${IMG} + xxd $< | less + +clean: + rm -f ${.OBJDIR}/*.o ${.OBJDIR}/*.elf ${.OBJDIR}/*.bin ${.OBJDIR}/*.img + +run: ${IMG} + qemu-system-i386 -M pc -m 1M -fda $< + +floppy1440.img: sys.bin + cat $< /dev/zero | dd of=$@ bs=512 count=2880 + +sys.img: sys.bin + cat $< /dev/zero | dd of=$@ bs=512 count=128 + +sys.bin: sys.elf + objcopy -O binary $< $@ + +sys.elf: linker.ld ${OBJ} + ld -o $@ -T linker.ld ${OBJ:F} ${LDFLAGS} + +user.bin: user.asm + nasm -fbin -o $@ $< + +.asm.o: + nasm -felf32 -o $@ $< -I${.OBJDIR} + +kernel.o: user.bin +