# SPDX-License-Identifier: MIT


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


OUTDIR ?= out/
OBJDIR ?= $(OUTDIR)obj/
BOTHFLAGS := -MD -g -O3 -pipe -Wall -Wextra -Iinclude/
LDFLAGS += -Wl,--as-needed -L$(OUTDIR)
EXAMPLES := $(wildcard examples/*.c examples/*.cpp)
EXAMPLES_RS := $(wildcard examples/*.rs)
MANPAGES := $(wildcard man/*.[0123456789])
FEBUG_VERSION := $(FEBUG_VERSION)
FEBUG_DATE := $(FEBUG_DATE)


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
	LDFLAGS := $(filter-out -Wl,--as-needed,$(LDFLAGS))
	SONAME := -install_name
else
	FUSE_ARGS ?= $(shell $(PKGCONF) --libs --cflags fuse3)
endif


CXXVER := $(shell $(CXX) --version | { read -r l; echo "$$l"; })
ifneq "$(findstring clang,$(CXXVER))" ""
	BOTHFLAGS += -Wpedantic
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
	BOTHFLAGS += -Wno-vla-extension
endif
	ifeq "$(LTO)" "y"
		LDFLAGS += -flto=full  # Clang produces .o files with LLVM bitcode, which cannot be linked to if put in .as
	endif
else
	ifeq "$(LTO)" "y"
		BOTHFLAGS += -flto
	endif
endif

CXXFLAGS += $(BOTHFLAGS) -std=c++20
CFLAGS   += $(BOTHFLAGS) -std=c11


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

all : main libfebug++ libfebug libfebug.py 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
libfebug.py : $(OUTDIR)febug.py/febug.py
python-build : $(OUTDIR)febug.py/dist/.built
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
	@mkdir -p $(@D)
	$(CXX) $(CPPFLAGS) -DFEBUG_VERSION='$(FEBUG_VERSION)' $(CXXFLAGS) -fno-exceptions -fPIE $< -o$@ $(FUSE_ARGS)


$(OBJDIR)febug++.o : febug.cpp
	@mkdir -p $(@D)
	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -fPIC -c $< -o$@

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


$(OBJDIR)febug.o : febug.c
	@mkdir -p $(@D)
	$(CC) $(CPPFLAGS) $(CFLAGS) -fPIC -c $< -o$@

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

$(OUTDIR)libfebug.so : $(OBJDIR)febug.o
	@mkdir -p $(@D)
	$(CC) $(CFLAGS) $(LDFLAGS) -shared -fPIC -Wl,$(SONAME),libfebug.so.0 $< -o$(OUTDIR)libfebug.so.0
	ln -fs libfebug.so.0 $@


$(OUTDIR)febug.py/febug.py : febug.py febug.pyproject README.md
	@mkdir -p $(@D)
	sed -e 's;FEBUG_VERSION;$(FEBUG_VERSION);' -e '/version =/s/-/.dev/' -e '/version =/s/-\(.*\)"/"/' febug.pyproject > $(OUTDIR)febug.py/pyproject.toml
	cp febug.py README.md $(@D)

$(OUTDIR)febug.py/dist/.built : $(OUTDIR)febug.py/febug.py
	cd $(OUTDIR)febug.py && $(PYTHON3) -m build
	@>> $@


$(OUTDIR)examples/% : examples/%.cpp $(OUTDIR)libfebug++.a
	@mkdir -p $(@D)
	$(CXX) $(CPPFLAGS)              $(CXXFLAGS) $(LDFLAGS) -fPIE $< -o$@ -lfebug++

$(OUTDIR)examples/%-dont : examples/%.cpp $(OUTDIR)libfebug++.a
	@mkdir -p $(@D)
	$(CXX) $(CPPFLAGS) -DFEBUG_DONT $(CXXFLAGS) $(LDFLAGS) -fPIE $< -o$@ -lfebug++

$(OUTDIR)examples/% : examples/%.c $(OUTDIR)libfebug.a
	@mkdir -p $(@D)
	$(CC) $(CPPFLAGS)               $(CFLAGS)   $(LDFLAGS) -fPIE $< -o$@ -lfebug

$(OUTDIR)examples/%-dont : examples/%.c $(OUTDIR)libfebug.a
	@mkdir -p $(@D)
	$(CC) $(CPPFLAGS) -DFEBUG_DONT  $(CFLAGS)   $(LDFLAGS) -fPIE $< -o$@ -lfebug


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

$(OUTDIR)man/% : man/% man/*.h
	@mkdir -p $(@D)
	(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); gsub(/\t/, "  ", line); if(line !~ /^##/) print line}; next}  \
			{sub(/^~/, "\\(ti"); gsub(/([^\\])~/, "&_BUT-TI_"); gsub(/~_BUT-TI_/, "\\(ti"); 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/bookworm/%N.%S",style="style.css" ../$^ | \
		sed -Ee 's/ title=".."//g') > $@

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

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

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

$(OUTDIR)febug.rs/examples/%.rs : examples/%.rs
	@mkdir -p $(@D)
	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 $(@D)
	sed 's;FEBUG_VERSION;$(FEBUG_VERSION);' $< > $@


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


include $(wildcard $(OUTDIR)*.d $(OUTDIR)*/*.d)
