commit - 10729a4d604b072251ebfcd23aab8b33b1bf13d1
commit + 3a30aa6d16018ca853dadc3e60cab173c5a8b1c8
blob - 83eb3c7528fb2e0be5130dbd12d64d54a7855f93 (mode 644)
blob + /dev/null
--- MyMakefile
+++ /dev/null
-.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
+.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
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
-.SUBDIRS: cc1 cpp irc yacc
-
-.expand dir
blob - /dev/null
blob + 984271dae43c9165d091b91773cb0bce0bda7525 (mode 644)
--- /dev/null
+++ cc/Mkfile
+.SUBDIRS: cc1 cpp irc yacc
+
+.expand dir
blob - a22d12933faf8a16999616fdbcf2587d88a479c9 (mode 644)
blob + /dev/null
--- cc/cc1/MyMakefile
+++ /dev/null
-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
+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
+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
-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
+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
-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
+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
-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
+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
-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
+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
-#if __linux__
-# define _GNU_SOURCE
-# define _XOPEN_SOURCE 700
-#endif
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <fnmatch.h>
-#include <assert.h>
-#include <unistd.h>
-#include <limits.h>
-#include <libgen.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <ctype.h>
-#include <fcntl.h>
-#include <time.h>
-#include <err.h>
-#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
+#include "config.h"
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#if HAVE_LIMITS_H
+# include <limits.h>
+#endif
+#include <stdio.h>
+#include <errno.h>
+
+#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
-#ifndef FILE_MAKE_H
-#define FILE_MAKE_H
-#include <sys/time.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 */
- };
-};
-
-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
+#ifndef __dead
+# define __dead
+#endif
+
+#ifndef HAVE_REALLOCARRAY
+extern void *reallocarray ();
+#endif /* HAVE_REALLOCARRAY */
+
+#ifdef HAVE_ERR_H
+# include <err.h>
+#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 <fnmatch.h>
+#else
+extern fnmatch ();
+#endif
+
+#ifdef HAVE_LIBGEN_H
+# include <libgen.h>
+#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
+/* 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 <err.h> 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 <fnmatch.h> 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 <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <libgen.h> header file. */
+#define HAVE_LIBGEN_H 1
+
+/* Define to 1 if you have the <limits.h> 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 <memory.h> 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 <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> 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 <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> 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 <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/wait.h> 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 <unistd.h> 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 <vfork.h> 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
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#if HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#if HAVE_FTIME
+# include <sys/timeb.h>
+#endif
+#include <assert.h>
+#include <unistd.h>
+#if HAVE_LIMITS_H
+# include <limits.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <time.h>
+#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
+#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
+.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
+
+all: libfoo.txt
+
+clean:
+ rm -f libfoo.txt
+
+libfoo.txt:
+ echo libfoo >$@
blob - b4f935b0573d788e185bfa9c621b737305c0379b (mode 644)
blob + /dev/null
--- sys/MyMakefile
+++ /dev/null
-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
+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
+