# SPDX-License-Identifier: MIT


CXX ?= c++
CC ?= cc
CPP ?= cpp
MANDOC ?= mandoc
AWK ?= awk
CARGO ?= cargo
LTO ?= y
FEBUG_VERSION ?= "$(patsubst v%,%,$(shell git describe))"
FEBUG_DATE ?= $(shell date $(shell date --version > /dev/null 2>&1 && echo "--date @" || echo "-r ")$(shell git log -1 --no-show-signature --format=%at) +"%B %e, %Y")
EXTRACCAR ?=
EXTRALDAR ?=

AS_NEEDED := -Wl,--as-needed
SONAME := -soname
SYSTEM := $(shell uname -s)
ifeq "$(SYSTEM)" "NetBSD"
	FUSE_ARGS ?= -lrefuse
else ifeq "$(SYSTEM)" "OpenBSD"
	FUSE_ARGS ?= -lfuse
else ifeq "$(SYSTEM)" "Darwin"
	FUSE_ARGS ?= -lfuse
	AS_NEEDED :=
	SONAME := -install_name
else
	FUSE_ARGS ?= $(shell pkg-config --libs --cflags fuse3)
endif

CXXVER := $(shell $(CXX) --version | head -1)
ifneq "$(findstring clang,$(CXXVER))" ""
	# GCC doesn't have this granularity
	CXXSPECIFICCC := -Wpedantic -Wno-c++20-designator -Wno-c99-extensions -Wno-unknown-warning-option
ifneq "$(findstring Apple,$(CXXVER))" ""
	# CMSG_SPACE() is a macro, and it's decidedly constexpr. i don't care if appleclang can't figure that out
	CXXSPECIFICCC += -Wno-vla-extension
endif
	ifeq "$(LTO)" "y"
		CXXSPECIFICLD := -flto=full  # Clang produces .o files with LLVM bitcode, which cannot be linked to if put in .as
	else
		CXXSPECIFICLD :=
	endif
else
	CXXSPECIFICCC := -Wno-missing-field-initializers -fno-common
	ifeq "$(LTO)" "y"
		CXXSPECIFICCC += -flto
	endif
	CXXSPECIFICLD :=
endif


OUTDIR ?= out/
OBJDIR ?= $(OUTDIR)obj/
CCAR := -g -O3 -pipe -Wall -Wextra -isysteminclude/ $(CXXSPECIFICCC) $(EXTRACCAR)
LDAR := $(AS_NEEDED) -L$(OUTDIR) $(CXXSPECIFICLD) $(EXTRALDAR)
HEADERS := $(wildcard include/*.h include/*.hpp)
EXAMPLES := $(wildcard examples/*.c examples/*.cpp)
EXAMPLES_RS := $(wildcard examples/*.rs)
MANPAGES := $(wildcard man/*.[0123456789])
FEBUG_VERSION := $(FEBUG_VERSION)
FEBUG_DATE := $(FEBUG_DATE)
FUSE_ARGS := $(FUSE_ARGS)


.PHONY : main libfebug++ libfebug examples manpages htmlpages rust-build rust-doc

all : main libfebug++ libfebug examples manpages htmlpages how.png
clean:
	rm -rf $(OUTDIR) $(OBJDIR) how.png

main : $(OUTDIR)febug
libfebug++ : $(OUTDIR)libfebug++.a
libfebug : $(OUTDIR)libfebug.a $(OUTDIR)libfebug.so
examples : $(foreach l,$(patsubst %.c,$(OUTDIR)%,$(patsubst %pp,%,$(EXAMPLES))),$(l) $(l)-dont)
manpages : $(patsubst %,$(OUTDIR)%,$(MANPAGES)) $(OBJDIR)man/aliases
htmlpages : $(patsubst %,$(OUTDIR)%.html,$(MANPAGES)) $(OUTDIR)man/style.css
rust-build : $(OUTDIR)febug.rs/target/libfebug.rlib
rust-doc : $(OUTDIR)febug.rs/doc/febug/index.html


$(OUTDIR)febug : main.cpp $(HEADERS)
	@mkdir -p $(dir $@)
	$(CXX) $< -o$@ -DFEBUG_VERSION='$(FEBUG_VERSION)' -std=c++17 -fno-exceptions -fPIE $(CCAR) $(LDAR) $(FUSE_ARGS)


$(OBJDIR)febug++.o : febug.cpp $(HEADERS)
	@mkdir -p $(dir $@)
	$(CXX) -c $< -o$@ -std=c++17 -fPIC $(CCAR)

$(OUTDIR)libfebug++.a : $(OBJDIR)febug++.o
	@mkdir -p $(dir $@)
	ar crs $@ $^


$(OBJDIR)febug.o : febug.c $(HEADERS)
	@mkdir -p $(dir $@)
	$(CC) -c $< -o$@ -std=c11 -fPIC $(CCAR)

$(OUTDIR)libfebug.a : $(OBJDIR)febug.o
	@mkdir -p $(dir $@)
	ar crs $@ $^

$(OUTDIR)libfebug.so : $(OBJDIR)febug.o
	@mkdir -p $(dir $@)
	$(CC) -shared $< -o$(OUTDIR)libfebug.so.0 -std=c11 -fPIC $(CCAR) $(LDAR) -Wl,$(SONAME),libfebug.so.0
	ln -fs libfebug.so.0 $@


$(OUTDIR)examples/% : examples/%.cpp $(OUTDIR)libfebug++.a $(HEADERS)
	@mkdir -p $(dir $@)
	$(CXX) $< -o$@              -std=c++17 -fPIE $(CCAR) $(LDAR) -lfebug++

$(OUTDIR)examples/%-dont : examples/%.cpp $(OUTDIR)libfebug++.a $(HEADERS)
	@mkdir -p $(dir $@)
	$(CXX) $< -o$@ -DFEBUG_DONT -std=c++17 -fPIE $(CCAR) $(LDAR) -lfebug++

$(OUTDIR)examples/% : examples/%.c $(OUTDIR)libfebug.a $(HEADERS)
	@mkdir -p $(dir $@)
	$(CC) $< -o$@              -std=c11 -fPIE $(CCAR) $(LDAR) -lfebug

$(OUTDIR)examples/%-dont : examples/%.c $(OUTDIR)libfebug.a $(HEADERS)
	@mkdir -p $(dir $@)
	$(CC) $< -o$@ -DFEBUG_DONT -std=c11 -fPIE $(CCAR) $(LDAR) -lfebug


$(OBJDIR)man/aliases : man/aliases
	@mkdir -p $(dir $@)
	@touch $@
	$(AWK) '!/^$$/ {print "ln -fs " $$1 " $(OUTDIR)man/" $$2}' man/aliases | sh -x

$(OUTDIR)man/% : man/% $(wildcard man/*.h)
	@mkdir -p $(dir $@)
	(cd $(dir $<); $(CPP) -nostdinc -Wno-trigraphs -Wno-invalid-pp-token -CC -P -DFEBUG_DATE="$(FEBUG_DATE)" -DFEBUG_VERSION='$(FEBUG_VERSION)' - < $(notdir $<) | \
		$(AWK) '/^$$/ {next}  $$1 == ".Dt" { print ".ds doc-volume-operating-system" }  \
			$$1 == "$$include" {gsub(/"/, "", $$2); while((getline line < $$2) > 0) {gsub(/\\/, "\\e", line); print line}; next}  \
			{print}') > $@
	! $(MANDOC) -Tlint $@ 2>&1 | grep -vE -e 'mandoc: outdated mandoc.db' -e 'STYLE: referenced manual not found' -e 'WARNING: unknown manual section: Dt .* 0$$' -e 'STYLE: operating system explicitly specified: Os febug' -e 'STYLE: input text line longer than 80 bytes' -e 'STYLE: .*OpenBSD' -e 'STYLE: RCS id missing'

$(OUTDIR)man/%.html : $(OUTDIR)man/%
	(cd man/; $(MANDOC) -Thtml -Oincludes="https://git.sr.ht/~nabijaczleweli/febug/tree/trunk/item/include/%I",man="%N.%S.html;https://manpages.debian.org/bullseye/%N.%S",style="style.css" ../$^ | \
		sed -Ee 's/ title=".."//g') > $@

$(OUTDIR)man/style.css : man/style.css
	@mkdir -p $(dir $@)
	ln -f $^ $@ 2>/dev/null || cp $^ $@

$(OUTDIR)febug.rs/README.md : README.md
	@mkdir -p $(dir $@)
	ln -f $^ $@ 2>/dev/null || cp $^ $@

$(OUTDIR)febug.rs/%.rs : febug.rs/%.rs
	@mkdir -p $(dir $@)
	ln -f $^ $@ 2>/dev/null || cp $^ $@

$(OUTDIR)febug.rs/examples/%.rs : examples/%.rs
	@mkdir -p $(dir $@)
	ln -f $^ $@ 2>/dev/null || cp $^ $@

$(OUTDIR)febug.rs/target/libfebug.rlib : $(OUTDIR)febug.rs/Cargo.toml $(OUTDIR)febug.rs/lib.rs $(OUTDIR)febug.rs/build.rs $(OUTDIR)febug.rs/README.md $(patsubst %,$(OUTDIR)febug.rs/%,$(EXAMPLES_RS))
	@mkdir -p $(OUTDIR)examples/
	cd $(dir $<); FEBUG_DONT=1 $(CARGO) build --release --examples
	$(foreach l,$(patsubst %.rs,%,$(EXAMPLES_RS)),mv $(OUTDIR)febug.rs/target/release/$(l) $(OUTDIR)$(l)-dont; )
	cd $(dir $<); $(CARGO) build --release --examples
	$(foreach l,$(patsubst %.rs,%,$(EXAMPLES_RS)),mv $(OUTDIR)febug.rs/target/release/$(l) $(OUTDIR)$(l); )

$(OUTDIR)febug.rs/doc/febug/index.html : $(OUTDIR)febug.rs/target/libfebug.rlib
	cd $(dir $<); $(CARGO) doc --release

$(OUTDIR)febug.rs/Cargo.toml : febug.rs/Cargo.toml
	@mkdir -p $(dir $@)
	sed 's;FEBUG_VERSION;$(FEBUG_VERSION);' $< > $@


how.png : how.dot
	dot -Tpng $^ -o $@
