# syd's Makefile
# Copyright (c) 2023, 2024, 2025 Ali Polatel <alip@chesswob.org>
# SPDX-License-Identifier: GPL-3.0

# User variables
# Target, e.g: --target=aarch64-unknown-linux-musl
TARGET=

ARCH=$(shell uname -m)
LIBC=$(shell readlink /lib/ld* | grep -q musl && echo musl || echo gnu)
ALPINE_MMV=3.19
ALPINE_VER=$(ALPINE_MMV).1

# Use cargo-auditable for auditable binaries if available.
# Respect user's choice of CARGO=
CARGO ?= $(shell \
	if which cargo-auditable >/dev/null 2>&1; then \
		printf 'cargo auditable'; \
	else \
		printf 'cargo'; \
	fi)

# Common tools
RM= rm
FIND= find
RSYNC = rsync
GIT = git
INSTALL= install
PREFIX= $(HOME)/.local
BINDIR= bin
MANDIR= share/man
DOCDIR= $(HOME)/src/sydbox.exherbo.org
VIMDIR= share/vim/vimfiles
SCDOC= scdoc
MANDOC= mandoc
PKG_CONFIG= pkg-config

# Emacs
EMACS?= emacs
EMACS_BATCH = $(EMACS) -Q --batch

# Environment variables necessary to link libseccomp statically.
export LIBSECCOMP_LINK_TYPE= static
export LIBSECCOMP_LIB_PATH= $(shell $(PKG_CONFIG) --variable=libdir libseccomp || echo /usr/lib)

export RUST_BACKTRACE=1

# Cargo features and flags
CARGOFLAGS= -j$(shell nproc) --quiet
CARGOFEATS= uring
# CARGONODEF=YesPlease -> CARGOFLAGS+=--no-default-features
ifneq ($(CARGONODEF),)
    CARGOFLAGS+= --no-default-features
endif
CARGOFEATS_DEBUG= $(CARGOFEATS)

PROGRAMS= \
	  syd \
	  syd-aes \
	  syd-asm \
	  syd-aux \
	  syd-bit \
	  syd-cap \
	  syd-cat \
	  syd-cpu \
	  syd-dns \
	  syd-elf \
	  syd-emacs \
	  syd-env \
	  syd-err \
	  syd-exec \
	  syd-fd \
	  syd-fork \
	  syd-hex \
	  syd-info \
	  syd-key \
	  syd-ldd \
	  syd-load \
	  syd-lock \
	  syd-log \
	  syd-ls \
	  syd-mdwe \
	  syd-net \
	  syd-mem \
	  syd-oci \
	  syd-open \
	  syd-path \
	  syd-pds \
	  syd-poc \
	  syd-pty \
	  syd-read \
	  syd-rnd \
	  syd-run \
	  syd-sh \
	  syd-sha \
	  syd-size \
	  syd-stat \
	  syd-sys \
	  syd-test \
	  syd-test-do \
	  syd-tck \
	  syd-tor \
	  syd-tty \
	  syd-x

# Manual pages
MANS1= \
       man/syd.1 \
       man/syd-aes.1 \
       man/syd-asm.1 \
       man/syd-aux.1 \
       man/syd-bit.1 \
       man/syd-cap.1 \
       man/syd-cat.1 \
       man/syd-cpu.1 \
       man/syd-dns.1 \
       man/syd-elf.1 \
       man/syd-emacs.1 \
       man/syd-env.1 \
       man/syd-err.1 \
       man/syd-exec.1 \
       man/syd-fd.1 \
       man/syd-fork.1 \
       man/syd-hex.1 \
       man/syd-info.1 \
       man/syd-key.1 \
       man/syd-ldd.1 \
       man/syd-load.1 \
       man/syd-lock.1 \
       man/syd-log.1 \
       man/syd-ls.1 \
       man/syd-mdwe.1 \
       man/syd-net.1 \
       man/syd-mem.1 \
       man/syd-oci.1 \
       man/syd-open.1 \
       man/syd-path.1 \
       man/syd-pds.1 \
       man/syd-poc.1 \
       man/syd-pty.1 \
       man/syd-read.1 \
       man/syd-rnd.1 \
       man/syd-run.1 \
       man/syd-sh.1 \
       man/syd-sha.1 \
       man/syd-size.1 \
       man/syd-stat.1 \
       man/syd-sys.1 \
       man/syd-test.1 \
       man/syd-tck.1 \
       man/syd-tor.1 \
       man/syd-tty.1 \
       man/syd-x.1
MANS2= \
       man/syd.2
MANS5= \
       man/syd.5
MANS7= \
       man/syd.7 \
       man/sydtutorial.7

MANS= $(MANS1) $(MANS2) $(MANS5) $(MANS7)

HTMLS= $(patsubst man/%.1,target/man/%.1.html,$(MANS1)) \
       $(patsubst man/%.2,target/man/%.2.html,$(MANS2)) \
       $(patsubst man/%.5,target/man/%.5.html,$(MANS5)) \
       $(patsubst man/%.7,target/man/%.7.html,$(MANS7))

# Vim syntax files
VIMS_FTD= \
	  vim/ftdetect/syd.vim
VIMS_SYN= \
	  vim/syntax/syd-3.vim

VIMS= $(VIMS_FTD) $(VIMS_SYN)

PUTFLAGS= -c $(HOME)/.s3cfg.site
PUTFLAGS_HTML= $(PUTFLAGS) \
	       --no-guess-mime-type \
	       --default-mime-type=text/html

all: release

man: $(MANS)

build:
	@echo Using libseccomp library from $(LIBSECCOMP_LIB_PATH)
	#FIXME: Fails to build with:
	#error: could not compile `proc-macro2` (build script)
	#$(CARGO) acl -n
	$(CARGO) deny check
	$(CARGO) clippy $(CARGOFLAGS)
	$(CARGO) build --locked --features $(CARGOFEATS_DEBUG) $(CARGOFLAGS)
	$(CARGO) test
build32:
	env \
		LIBSECCOMP_LINK_TYPE=static \
		LIBSECCOMP_LIB_PATH=/usr/i686-linux-musl/lib \
		LD_LIBRARY_PATH=/usr/i686-linux-musl/lib \
		RUSTFLAGS="-Ctarget-feature=+crt-static" \
		$(CARGO) build \
			--target=i686-unknown-linux-musl \
			--features $(CARGOFEATS) $(CARGOFLAGS)
clean:
	$(CARGO) clean
install32: build32
	$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(BINDIR)/
	for program in $(PROGRAMS); do \
		$(INSTALL) -pm 0755 target/i686-unknown-linux-musl/release/$$program $(DESTDIR)$(PREFIX)/$(BINDIR)/$$program"32"; \
	done
install: release
	$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(BINDIR)/
	for program in $(PROGRAMS); do \
		$(INSTALL) -pm 0755 target/release/$$program $(DESTDIR)$(PREFIX)/$(BINDIR)/; \
	done
	$(MAKE) install-man
	$(MAKE) install-vim
cave:
	if test -e /etc/exherbo-release; then \
		$(INSTALL) -pm 0755 dev/cave-force.rb /usr/libexec/cave/commands/force; \
		$(INSTALL) -pm 0755 dev/cave-hide.rb /usr/libexec/cave/commands/hide; \
	fi
debug:
	$(CARGO) build --locked --features $(CARGOFEATS_DEBUG) $(CARGOFLAGS)
	$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(BINDIR)/
	for program in $(PROGRAMS); do \
		$(INSTALL) -pm 0755 target/debug/$$program $(DESTDIR)$(PREFIX)/$(BINDIR)/; \
	done
	$(MAKE) install-man
	$(MAKE) install-vim
install-man: $(MANS)
	$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(MANDIR)/
	$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(MANDIR)/man1/
	$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(MANDIR)/man2/
	$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(MANDIR)/man5/
	$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(MANDIR)/man7/
	for man in $(MANS1); do \
		$(INSTALL) -pm 0644 $$man $(DESTDIR)$(PREFIX)/$(MANDIR)/man1/; \
	done
	for man in $(MANS2); do \
		$(INSTALL) -pm 0644 $$man $(DESTDIR)$(PREFIX)/$(MANDIR)/man2/; \
	done
	for man in $(MANS5); do \
		$(INSTALL) -pm 0644 $$man $(DESTDIR)$(PREFIX)/$(MANDIR)/man5/; \
	done
	for man in $(MANS7); do \
		$(INSTALL) -pm 0644 $$man $(DESTDIR)$(PREFIX)/$(MANDIR)/man7/; \
	done
install-vim: $(VIMS)
	$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(VIMDIR)/ftdetect
	for f in $(VIMS_FTD); do \
		$(INSTALL) -pm 0644 $$f $(DESTDIR)$(PREFIX)/$(VIMDIR)/ftdetect/; \
	done
	$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(VIMDIR)/syntax
	for f in $(VIMS_SYN); do \
		$(INSTALL) -pm 0644 $$f $(DESTDIR)$(PREFIX)/$(VIMDIR)/syntax/; \
	done
uninstall:
	for program in $(PROGRAMS); do \
		$(RM) -f $(DESTDIR)$(PREFIX)/$(BINDIR)/$$program; \
	done
	for man in $(MANS1); do \
		$(RM) -f $(DESTDIR)$(PREFIX)/$(MANDIR)/man1/$$man; \
	done
	for man in $(MANS2); do \
		$(RM) -f $(DESTDIR)$(PREFIX)/$(MANDIR)/man2/$$man; \
	done
	for man in $(MANS5); do \
		$(RM) -f $(DESTDIR)$(PREFIX)/$(MANDIR)/man5/$$man; \
	done
	for man in $(MANS7); do \
		$(RM) -f $(DESTDIR)$(PREFIX)/$(MANDIR)/man7/$$man; \
	done
	for f in $(VIMS_FTD); do \
		$(RM) -f $(DESTDIR)$(PREFIX)/$(VIMDIR)/ftdetect/$$f; \
	done
	for f in $(VIMS_SYN); do \
		$(RM) -f $(DESTDIR)$(PREFIX)/$(VIMDIR)/syntax/$$f; \
	done
release:
	@echo Using libseccomp library from $(LIBSECCOMP_LIB_PATH)
	$(CARGO) build --release --locked --features $(CARGOFEATS) $(CARGOFLAGS) $(TARGET)
prof:
	@echo Using libseccomp library from $(LIBSECCOMP_LIB_PATH)
	env RUSTFLAGS='-g -Ctarget-cpu=native -Ctarget-feature=-crt-static' \
		$(CARGO) build --release --locked --features prof $(TARGET)
	$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(BINDIR)/
	for program in $(PROGRAMS); do \
		$(INSTALL) -pm 0755 target/release/$$program $(DESTDIR)$(PREFIX)/$(BINDIR)/; \
	done
audit:
	$(CARGO) audit
bpfclean:
	find bpf -name '*.bpf' -type f -delete
bpfgen:
	/bin/sh -cex 'mkdir -m700 -p bpf; cd bpf; syd -mtrace/allow_unsafe_nocookie:1 -Ebpf'
bpf: bpfgen
	@for bpf in ./bpf/*.bpf; do \
		./dev/scmp-info.sh $$bpf | tee ./bpf/$$(basename $$bpf .bpf).txt; \
	done
check:
	$(CARGO) test $(CARGOFLAGS)
doccheck:
	$(CARGO) test --doc $(CARGOFLAGS)
distcheck:
	env CARGO="$(CARGO)" PATH="$(PWD)/target/release:$(PATH)" \
		./dev/cargo-test.sh --release --features $(CARGOFEATS) $(CARGOFLAGS)
distdebug:
	env CARGO="$(CARGO)" PATH="$(PWD)/target/debug:$(PATH)" \
		./dev/cargo-test.sh --features $(CARGOFEATS) $(CARGOFLAGS)
doc:
	$(CARGO) doc --open
eldoc: dev/lsp2html.def src/syd.el
	lsp2html -d dev/lsp2html.def src/syd.el
	sed -i \
		-e 's|charset=ISO-8859-1|charset=UTF-8|' \
		-e 's|<h1>syd.el|<h1><a href="https://gitlab.exherbo.org/sydbox/sydbox/-/raw/main/lib/src/syd.el">syd.el</a>|' \
		src/syd.el.html
	mkdir -m700 -p doc/auto
	mv src/syd.el.html doc/auto
fmt:
	$(CARGO) fmt
	$(EMACS_BATCH) src/syd.el -l $(CURDIR)/dev/emacs-format-file.el -f emacs-format-function
	$(MAKE) -C lib fmt
lint:
	$(EMACS_BATCH) --no-site-file --directory . -f batch-byte-compile src/syd.el
sec:
	@for bin in target/{debug,release}/syd; do \
		test -e $$bin || continue; \
		./dev/checksec.sh $$bin; \
	done
dist:
	./dev/release.sh
publish:
	$(CARGO) publish
upload: eldoc
	rm -rf target/man
	mkdir -m700 -p target/man
	$(MAKE) -j $(HTMLS)
	echo "<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>Man Pages Index</title></head><body><h1>man.exherbo.org</h1>" > target/man/index.html
	@for man in target/man/*.*.html; do \
		filename=$${man##*/} ;\
		basename=$${filename%.html}; \
		echo "<a href=\"$$filename\">$$basename</a><br>" ;\
	done | sort >> target/man/index.html
	echo "</body></html>" >> target/man/index.html
	s3cmd put $(PUTFLAGS_HTML) -P target/man/*.html s3://man.exherbo.org/
	s3cmd put $(PUTFLAGS_HTML) -P doc/auto/syd.el.html s3://sydel.exherbo.org/index.html

# Library
lib:
	$(MAKE) -C lib all
checklib:
	$(MAKE) -C lib check

# Fuzzing
fuzz:
	$(MAKE) -C fuzz all
fuzz_conf:
	$(MAKE) -C fuzz $@
fuzz_path:
	$(MAKE) -C fuzz $@

# Use LLVM sanitizers
sanitize_address:
	env RUSTFLAGS="-Zsanitizer=address -Ctarget-feature=-crt-static" $(CARGO) build --release --no-default-features $(CARGOFLAGS) -Zbuild-std=std,panic_abort --target $(ARCH)-unknown-linux-$(LIBC)
sanitize_leak:
	env RUSTFLAGS="-Zsanitizer=leak -Ctarget-feature=-crt-static" $(CARGO) build --release --no-default-features $(CARGOFLAGS) -Zbuild-std=std,panic_abort --target $(ARCH)-unknown-linux-$(LIBC)
sanitize_memory:
	env RUSTFLAGS="-Zsanitizer=memory -Ctarget-feature=-crt-static" $(CARGO) build --release --no-default-features $(CARGOFLAGS) -Zbuild-std=std,panic_abort --target $(ARCH)-unknown-linux-$(LIBC)
sanitize_thread:
	env RUSTFLAGS="-Zsanitizer=thread -Ctarget-feature=-crt-static" $(CARGO) build --release --no-default-features $(CARGOFLAGS) -Zbuild-std=std,panic_abort --target $(ARCH)-unknown-linux-$(LIBC)

bench:
	$(CARGO) bench $(CARGOFLAGS)
bloat:
	$(CARGO) bloat --crates -n 25 --bin syd --profile release
cov:
	$(CARGO) llvm-cov --open
deny:
	$(CARGO) deny --features log,oci,utils,uring check
msrv:
	$(CARGO) msrv --bisect
watch:
	$(CARGO) watch
who:
	@git log --all --format='%cN <%cE>' | sort -u

root: alpine-rootfs.tar.gz
	mkdir -p -m700 $@
	doas tar -C $@ -xpf alpine-rootfs.tar.gz
	doas cp -L /etc/resolv.conf root/etc
	$(PREFIX)/bin/syd --sh | doas tee root/etc/esyd.sh >/dev/null
	doas chmod 644 root/etc/esyd.sh
enter: root
	doas $(PREFIX)/bin/syd -plib -mroot:root

alpine-rootfs.tar.gz:
	wget -cO$@ https://dl-cdn.alpinelinux.org/alpine/v$(ALPINE_MMV)/releases/$(ARCH)/alpine-minirootfs-$(ALPINE_VER)-$(ARCH).tar.gz

aes: dev/aes-ctr

dev/aes-ctr: dev/aes-ctr.c
	astyle $<
	$(CC) -Wall -Wextra $< -o $@ -lssl -lcrypto

ape:
	env -u RUSTFLAGS \
	$(CARGO) +nightly build --release $(CARGOFLAGS) \
		-Zbuild-std=panic_abort,std -Zbuild-std-features="panic_immediate_abort" \
		--target=./dev/$(ARCH)-unknown-linux-cosmo.json \
		--locked --features $(CARGOFEATS),oci

# XXX: This won't work, instead
# 1. Pull cosmopolitan.git
# 2. Build with: build/bootstrap/make -j4 m=optlinux o/optlinux/libc
# 3. Create libc.a with: cd o/optlinux/libc && ar rcs libc.a $(find . -type f -name "*.o")
# 4. Place libc.a under libc/$ARCH-linux-cosmo/lib
libcosmo:
	/bin/sh -exc " \
		rm -rf libcosmo; \
		mkdir -m700 libcosmo; \
		cd libcosmo; \
		wget https://justine.lol/cosmopolitan/cosmopolitan.zip; \
		unzip cosmopolitan.zip; \
		rm -f cosmopolitan.zip; \
		wget https://cosmo.zip/pub/cosmocc/cosmocc.zip; \
		unzip cosmocc.zip; \
		rm -f cosmocc.zip; \
	"

%.1: %.1.scd
	$(SCDOC) < $< > $@
%.2: %.2.scd
	$(SCDOC) < $< > $@
%.5: %.5.scd
	$(SCDOC) < $< > $@
%.7: %.7.scd
	$(SCDOC) < $< > $@

# Pattern rule for man page to HTML conversion
target/man/%.html: man/%
	$(MANDOC) -Thtml $< > $@

.PHONY: cave check dist distcheck clean debug doc fmt sec man install-man publish upload watch who
.PHONY: all bench bloat bpf bpfclean bpfgen build build32 cov deny msrv native release install install32 uninstall
.PHONY: sanitize_address sanitize_leak sanitize_memory sanitize_thread
.PHONY: chroot fuzz
.PHONY: lib checklib libcosmo
