# Copyright © Tavian Barnes <tavianator@tavianator.com>
# SPDX-License-Identifier: 0BSD

# Common makefile utilities.  Compatible with both GNU make and most BSD makes.

# BSD make will chdir into ${.OBJDIR} by default, unless we tell it not to
.OBJDIR: .

# We don't use any suffix rules
.SUFFIXES:

# GNU make has $^ for the full list of targets, while BSD make has $> and the
# long-form ${.ALLSRC}.  We could write $^ $> to get them both, but that would
# break if one of them implemented support for the other.  So instead, bring
# BSD's ${.ALLSRC} to GNU.
.ALLSRC ?= $^

# For out-of-tree builds, e.g.
#
#     $ make config BUILDDIR=/path/to/build/dir
#     $ make BUILDDIR=/path/to/build/dir
BUILDDIR ?= .

# Shorthand for build subdirectories
BIN := ${BUILDDIR}/bin
GEN := ${BUILDDIR}/gen
OBJ := ${BUILDDIR}/obj

# GNU make strips a leading ./ from target names, so do the same for BSD make
BIN := ${BIN:./%=%}
GEN := ${GEN:./%=%}
OBJ := ${OBJ:./%=%}

# The configuration file generated by `make config`
CONFIG := ${GEN}/config.mk

# Installation paths
DESTDIR ?=
PREFIX ?= /usr
MANDIR ?= ${PREFIX}/share/man

# GNU make supports `export VAR`, but BSD make requires `export VAR=value`.
# Sadly, GNU make gives a recursion error on `export VAR=${VAR}`.
_BUILDDIR := ${BUILDDIR}
export BUILDDIR=${_BUILDDIR}

# Configurable executables; can be overridden with
#
#     $ make config CC=clang
CC ?= cc
INSTALL ?= install
MKDIR ?= mkdir -p
PKG_CONFIG ?= pkg-config
RM ?= rm -f

# GNU and BSD make have incompatible syntax for conditionals, but we can do a
# lot with just nested variable expansion.  We use "y" as the canonical
# truthy value, and "" (the empty string) as the canonical falsey value.
#
# To normalize a boolean, use ${TRUTHY,${VAR}}, which expands like this:
#
#     VAR=y      ${TRUTHY,${VAR}} => ${TRUTHY,y}     => y
#     VAR=1      ${TRUTHY,${VAR}} => ${TRUTHY,1}     => y
#     VAR=n      ${TRUTHY,${VAR}} => ${TRUTHY,n}     =>   [empty]
#     VAR=other  ${TRUTHY,${VAR}} => ${TRUTHY,other} =>   [empty]
#     VAR=       ${TRUTHY,${VAR}} => ${TRUTHY,}      =>   [emtpy]
#
# Inspired by https://github.com/wahern/autoguess
TRUTHY,y := y
TRUTHY,1 := y

# Boolean operators are also implemented with nested expansion
NOT,y :=
NOT,  := y

# Support up to 5 arguments
AND,y         := y
AND,y,y       := y
AND,y,y,y     := y
AND,y,y,y,y   := y
AND,y,y,y,y,y := y

# NOR can be defined without combinatorial explosion.
# OR is just ${NOT,${NOR,...}}
NOR,     := y
NOR,,    := y
NOR,,,   := y
NOR,,,,  := y
NOR,,,,, := y

# Normalize ${V} to either "y" or ""
IS_V := ${TRUTHY,${V}}

# Suppress output unless V=1
Q, := @
Q  := ${Q,${IS_V}}

# The current target, with ${BUILDDIR} stripped for shorter messages
TGT = ${@:${BUILDDIR}/%=%}

# Show full commands with `make V=1`, otherwise short summaries
MSG = @msg() { \
          MSG="$$1"; \
          shift; \
          test "${IS_V}" || printf '%s\n' "$$MSG"; \
          test "$${1:-}" || return 0; \
          test "${IS_V}" && printf '%s\n' "$$*"; \
          "$$@"; \
      }; \
      msg

# Maximum width of a short message, to align the [X]
MSG_WIDTH := 24

# cat a file if V=1
VCAT,y := @cat
VCAT,  := @:
VCAT   := ${VCAT,${IS_V}}

# List all object files here, as they're needed by both `make config` and `make`

# All object files except the entry point
LIBBFS := \
    ${OBJ}/src/alloc.o \
    ${OBJ}/src/bar.o \
    ${OBJ}/src/bfstd.o \
    ${OBJ}/src/bftw.o \
    ${OBJ}/src/color.o \
    ${OBJ}/src/ctx.o \
    ${OBJ}/src/diag.o \
    ${OBJ}/src/dir.o \
    ${OBJ}/src/dstring.o \
    ${OBJ}/src/eval.o \
    ${OBJ}/src/exec.o \
    ${OBJ}/src/expr.o \
    ${OBJ}/src/fsade.o \
    ${OBJ}/src/ioq.o \
    ${OBJ}/src/mtab.o \
    ${OBJ}/src/opt.o \
    ${OBJ}/src/parse.o \
    ${OBJ}/src/printf.o \
    ${OBJ}/src/pwcache.o \
    ${OBJ}/src/stat.o \
    ${OBJ}/src/thread.o \
    ${OBJ}/src/trie.o \
    ${OBJ}/src/typo.o \
    ${OBJ}/src/xregex.o \
    ${OBJ}/src/xspawn.o \
    ${OBJ}/src/xtime.o \
    ${OBJ}/gen/version.o

# Unit test objects
UNIT_OBJS := \
    ${OBJ}/tests/alloc.o \
    ${OBJ}/tests/bfstd.o \
    ${OBJ}/tests/bit.o \
    ${OBJ}/tests/ioq.o \
    ${OBJ}/tests/main.o \
    ${OBJ}/tests/trie.o \
    ${OBJ}/tests/xspawn.o \
    ${OBJ}/tests/xtime.o

# All object files
OBJS := \
    ${OBJ}/src/main.o \
    ${OBJ}/tests/mksock.o \
    ${OBJ}/tests/xspawnee.o \
    ${OBJ}/tests/xtouch.o \
    ${LIBBFS} \
    ${UNIT_OBJS}