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

# User variables
# Target, e.g: --target=aarch64-unknown-linux-musl
TARGET ?= $(shell rustc --print=host-tuple)

ARCH=$(shell uname -m)
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)

# Default to static linking.
RUSTFLAGS := -g -Ctarget-feature=+crt-static
export RUSTFLAGS

# Common tools
RM= rm
RM_RF= rm -rf --one-file-system
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
PANDOC= pandoc
PKG_CONFIG= pkg-config

# Default flags for make(1), avoid noise.
MAKEFLAGS += --no-print-directory

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

# LaTeX
PDFLATEX   := lualatex --interaction=nonstopmode --halt-on-error --shell-restricted --utc
PDFVIEWER  := evince -s
SYDTEX     := ./doc/manual/syd-tex.py

# superdiff(1)
SUPERDIFF ?= superdiff

#
# codespell(1)
#
CODESPELL ?= codespell

# Words file (optional). If present, each line is a word to ignore.
CODESPELL_IGNORES ?= .codespellignore
CODESPELL_IGNORE_FLAG := $(if $(wildcard $(CODESPELL_IGNORES)),-I $(CODESPELL_IGNORES),)

# Builtin dictionaries: include code/names to reduce Rust false-positives.
CODESPELL_BUILTINS ?= clear,rare,code,names

# Skip common non-source stuff.
CODESPELL_SKIP ?= ,.git,./.git/*,Makefile,Makefile.in,shoebox,target,*.1,*.2,*.5,*.7,*~,*.orig,*.txt,*.lock,*.png,*.jpg,*.jpeg,*.gif,*.svg,*.pdf,*.zip,*.tar,*.gz,*.tgz,*.xz,*.wasm,*.map,*.min.js,*.woff,*.woff2

# Paths to scan.
SPELL_PATHS ?= src man tests benches examples build.rs Cargo.toml ChangeLog.md README.md

# Common args:
# -q 3 : suppress noisy encoding/binary warnings
# -s   : print a summary
# -f   : check filenames too
# -H   : include hidden dirs (we still skip .git explicitly)
CODESPELL_ARGS := --builtin $(CODESPELL_BUILTINS) -S "$(CODESPELL_SKIP)" $(CODESPELL_IGNORE_FLAG) -q 3 -s -f -H

# 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

# Pick number of jobs based on CPU count
JOBS = $(shell nproc)

# Parallel tests with prove(1)
PROVE := prove \
	 -mosvv \
	 -j$(JOBS) \
	 --normalize \
	 --timer --trap \
	 --exec '/bin/sh -c' -
STAMP := $(shell date -u +"%Y%m%dT%H%M%SZ")
TLOGS := ./syd_test_logs-$(STAMP)

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

# PGO builds
PROFDATA := llvm-profdata

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-exec \
	  syd-fd \
	  syd-fork \
	  syd-fs \
	  syd-hex \
	  syd-info \
	  syd-key \
	  syd-ldd \
	  syd-lock \
	  syd-ls \
	  syd-mdwe \
	  syd-net \
	  syd-mem \
	  syd-oci \
	  syd-ofd \
	  syd-path \
	  syd-pause \
	  syd-pds \
	  syd-poc \
	  syd-pty \
	  syd-read \
	  syd-rnd \
	  syd-run \
	  syd-sec \
	  syd-sh \
	  syd-size \
	  syd-stat \
	  syd-sum \
	  syd-sys \
	  syd-test \
	  syd-test-do \
	  syd-tck \
	  syd-tor \
	  syd-tsc \
	  syd-tty \
	  syd-utc \
	  syd-uts \
	  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-exec.1 \
       man/syd-fd.1 \
       man/syd-fork.1 \
       man/syd-fs.1 \
       man/syd-hex.1 \
       man/syd-info.1 \
       man/syd-key.1 \
       man/syd-ldd.1 \
       man/syd-lock.1 \
       man/syd-ls.1 \
       man/syd-mdwe.1 \
       man/syd-net.1 \
       man/syd-mem.1 \
       man/syd-oci.1 \
       man/syd-ofd.1 \
       man/syd-path.1 \
       man/syd-pause.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-sec.1 \
       man/syd-sh.1 \
       man/syd-size.1 \
       man/syd-stat.1 \
       man/syd-sum.1 \
       man/syd-sys.1 \
       man/syd-test.1 \
       man/syd-tck.1 \
       man/syd-tor.1 \
       man/syd-tsc.1 \
       man/syd-tty.1 \
       man/syd-utc.1 \
       man/syd-uts.1 \
       man/syd-x.1
MANS2= \
       man/syd.2
MANS5= \
       man/syd.5
MANS7= \
       man/sydtutorial.7 \
       man/syd.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))

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

# 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)

html: $(HTMLS)

md: $(MARKDOWNS)

pdf: doc/manual/syd-manual.pdf

tex: doc/manual/syd-manual.tex

build:
	@echo Using libseccomp library from $(LIBSECCOMP_LIB_PATH)
	$(CARGO) acl -n
	$(CARGO) deny check
	$(CARGO) clippy $(CARGOFLAGS)
	$(CARGO) build \
		--locked \
		--target=$(TARGET) \
		--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 \
		$(CARGO) build \
			--locked \
			--target=i686-unknown-linux-musl \
			--features $(CARGOFEATS) $(CARGOFLAGS)
clean:
	$(CARGO) clean
	$(RM_RF) android.tar.xz
	$(RM_RF) syd_test_*
	$(RM_RF) syd-gnulib.*
	$(RM_RF) syd-ltp.*
	$(RM_RF) target/man
	$(RM_RF) target/html
	$(RM_RF) target/syd.html
	$(RM_RF) target/syd-manual.log
	$(RM_RF) doc/manual/syd-manual.tex.tmp
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/$(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 \
		--target=$(TARGET) \
		--features $(CARGOFEATS_DEBUG) $(CARGOFLAGS)
	$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(BINDIR)/
	for program in $(PROGRAMS); do \
		$(INSTALL) -pm 0755 target/$(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 \
		--target=$(TARGET) \
		--features $(CARGOFEATS) $(CARGOFLAGS)
nopie:
	env RUSTFLAGS='$(RUSTFLAGS) -Ctarget-cpu=native -Clink-arg=-no-pie' $(MAKE) release
pgo:
	$(RM_RF) $(CURDIR)/syd-pgo
	env RUSTFLAGS='$(RUSTFLAGS) -Cprofile-generate=$(CURDIR)/syd-pgo' $(MAKE) release
	env SYD_SKIP_SCMP=YesPlease PATH='$(CURDIR)/target/$(TARGET)/release:$(PATH)' $(MAKE) prove || true
	$(RM_RF) $(CURDIR)/syd_test*
	$(PROFDATA) merge -o $(CURDIR)/syd-pgo/syd.profdata $(CURDIR)/syd-pgo
	env RUSTFLAGS='$(RUSTFLAGS) -Cprofile-use=$(CURDIR)/syd-pgo/syd.profdata' $(MAKE) release
prof:
	@echo Using libseccomp library from $(LIBSECCOMP_LIB_PATH)
	env RUSTFLAGS='-g -Ctarget-cpu=native -Ctarget-feature=-crt-static' \
		$(CARGO) build \
			--release --locked \
			--target=$(TARGET) \
			--features prof
	$(INSTALL) -d $(DESTDIR)$(PREFIX)/$(BINDIR)/
	for program in $(PROGRAMS); do \
		$(INSTALL) -pm 0755 target/$(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)
pandoracheck:
	PANDORA_QUIET=1 $(MAKE) pandoracheckverbose
pandoracheckverbose:
	$(CARGO) test $(CARGOFLAGS) -p pandora_box
distcheck:
	env CARGO="$(CARGO)" PATH="$(PWD)/target/$(TARGET)/release:$(PATH)" \
		./dev/cargo-test.sh \
			--release --locked \
			--target=$(TARGET) \
			--features $(CARGOFEATS) $(CARGOFLAGS)
distdebug:
	env CARGO="$(CARGO)" PATH="$(PWD)/target/$(TARGET)/debug:$(PATH)" \
		./dev/cargo-test.sh \
			--target=$(TARGET) \
			--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
	$(MAKE) -C pandora fmt
	black ./doc/manual/syd-tex.py || true
lint:
	$(EMACS_BATCH) --no-site-file --directory . -f batch-byte-compile src/syd.el
sec:
	@for bin in target/$(TARGET)/{debug,release}/syd; do \
		test -e $$bin || continue; \
		./dev/checksec.sh $$bin; \
	done
dist:
	./dev/release.sh
publish:
	$(CARGO) publish
upload: eldoc html
	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

# KCOV
kcov:
	cargo rustc --bin syd --release --locked --features kcov,trusted -j$(JOBS) --quiet -- \
		-g \
		-C passes=sancov-module \
		-C llvm-args=-sanitizer-coverage-level=3 \
		-C llvm-args=-sanitizer-coverage-trace-pc \
		-C llvm-args=-sanitizer-coverage-trace-compares \
		-C llvm-args=-sanitizer-coverage-prune-blocks=0

#
# Set up fuzz.exherbo.org Syzkaller instance.
#
KCOV_IMG ?= $(HOME)/fuzz/img/syd_fuzz.qcow2
KCOV_SSH_PORT ?= 10022
KCOV_SSH = ssh -p$(KCOV_SSH_PORT) root@127.0.0.1
KCOV_SCP = scp -P$(KCOV_SSH_PORT)

kcovup: kcovinstall
	systemctl --user start syz-manager
kcovdown:
	systemctl --user stop syz-vm || true
	systemctl --user stop syz-manager || true
kcovmaint: kcovdown
	systemctl --user start syz-vm
	fuzz/kcov/bin/ssh-wait.sh
kcovinfo:
	file $(KCOV_IMG)
	qemu-img info $(KCOV_IMG)
	qemu-img check $(KCOV_IMG)
	qemu-img snapshot --list $(KCOV_IMG)
kcovinstall: kcov kcovdown
	$(RM_RF) $(HOME)/syd/syzkaller/bin/syz-manager
	$(RM_RF) $(HOME)/syd/syzkaller/bin/linux_amd64/syz-executor
	sh -c "cd ./syzkaller; exec make -j"
	cp fuzz/kcov/systemd/syz-vm.service $(HOME)/.config/systemd/user/syz-vm.service
	cp fuzz/kcov/systemd/syz-manager.service $(HOME)/.config/systemd/user/syz-manager.service
	sed -e "s|@VERSION@|$$(git describe)|" < fuzz/kcov/syd-fuzz.json > $(HOME)/.config/syd-fuzz.json
	systemctl --user daemon-reload
	systemctl --user start syz-vm
	fuzz/kcov/bin/ssh-wait.sh
	$(KCOV_SCP) fuzz/kcov/bin/syd-executor root@127.0.0.1:/syz-executor
	$(KCOV_SCP) target/release/syd root@127.0.0.1:/srv/syd
	$(KCOV_SCP) $(HOME)/syd/syzkaller/bin/linux_amd64/syz-executor root@127.0.0.1:/syz-executor.orig
	$(KCOV_SSH) sync
	$(KCOV_SSH) rm -rf /syzcgroup
	systemctl --user stop syz-vm

# 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 --target=$(TARGET)
sanitize_leak:
	env RUSTFLAGS="-Zsanitizer=leak -Ctarget-feature=-crt-static" $(CARGO) build --release --no-default-features $(CARGOFLAGS) -Zbuild-std=std --target=$(TARGET)
sanitize_memory:
	env RUSTFLAGS="-Zsanitizer=memory -Ctarget-feature=-crt-static" $(CARGO) build --release --no-default-features $(CARGOFLAGS) -Zbuild-std=std --target=$(TARGET)
sanitize_thread:
	env RUSTFLAGS="-Zsanitizer=thread -Ctarget-feature=-crt-static" $(CARGO) build --release --no-default-features $(CARGOFLAGS) -Zbuild-std=std --target=$(TARGET)

bench:
	$(CARGO) bench $(CARGOFLAGS)
bloat:
	$(CARGO) bloat --crates -n 25 --bin syd --profile release
cov:
	env SYD_SKIP_SCMP=1 $(CARGO) llvm-cov --release --html 2>&1 | tee -a syd-cov.log
	@echo "open target/llvm-cov/html/index.html"
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

cross: mips64el

mips64el-img:
	if ! podman image exists local/cross-mips64el:musl 2>/dev/null; then \
		TMPDIR=$(HOME)/tmp podman build --build-arg CROSS_BASE_IMAGE=ghcr.io/cross-rs/mips64el-unknown-linux-muslabi64:edge -f dev/Dockerfile.mips64el.musl -t local/cross-mips64el:musl .; \
	fi
mips64el: mips64el-img
	env \
		CROSS_CONTAINER_ENGINE=podman \
		LIBSECCOMP_LINK_TYPE=static \
		PKG_CONFIG_ALL_STATIC=1 \
		RUSTFLAGS='-Ctarget-feature=+crt-static -Clink-arg=-L/usr/local/lib/gcc/mips64el-linux-musl/9.2.0 -Clink-arg=-L/usr/local/mips64el-linux-musl/lib -Clink-arg=-Wl,--start-group -Clink-arg=-Wl,-Bstatic -Clink-arg=-lgcc_eh -Clink-arg=-lgcc -Clink-arg=-Wl,--end-group' \
		TMPDIR=$(HOME)/tmp \
		cross build --release --locked --features $(CARGOFEATS) $(CARGOFLAGS) \
			--target mips64el-unknown-linux-muslabi64 \
			-Z build-std=core,std,alloc,proc_macro

mipsel-img:
	if ! podman image exists local/cross-mipsel:musl 2>/dev/null; then \
		TMPDIR=$(HOME)/tmp podman build --build-arg CROSS_BASE_IMAGE=ghcr.io/cross-rs/mipsel-unknown-linux-musl:edge -f dev/Dockerfile.mipsel.musl -t local/cross-mipsel:musl .; \
	fi
mipsel: mipsel-img
	env \
		CROSS_CONTAINER_ENGINE=podman \
		LIBSECCOMP_LINK_TYPE=static \
		PKG_CONFIG_ALL_STATIC=1 \
		RUSTFLAGS='-Ctarget-feature=+crt-static -Clink-arg=-L/usr/local/lib/gcc/mipsel-linux-muslsf/9.2.0 -Clink-arg=-L/usr/local/mipsel-linux-muslsf/lib -Clink-arg=-Wl,--start-group -Clink-arg=-Wl,-Bstatic -Clink-arg=-lgcc_eh -Clink-arg=-lgcc -Clink-arg=-Wl,--end-group' \
		TMPDIR=$(HOME)/tmp \
		cross build --release --locked --features $(CARGOFEATS) $(CARGOFLAGS) \
			--target mipsel-unknown-linux-musl \
			-Z build-std=core,std,alloc,proc_macro

android:
	env \
		CROSS_CONTAINER_ENGINE=podman \
		LIBSECCOMP_LINK_TYPE=static \
		LIBSECCOMP_LIB_PATH=/usr/local/aarch64-linux-android/lib \
		PKG_CONFIG_ALL_STATIC=1 \
		PKG_CONFIG_PATH=/usr/local/aarch64-linux-android/lib/pkgconfig \
		RUSTFLAGS='$(RUSTFLAGS) -L/usr/local/aarch64-linux-android/lib -Clink-arg=-lunwind' \
		TMPDIR=$(HOME)/tmp \
		cross build --release --locked --features $(CARGOFEATS) $(CARGOFLAGS) \
			--target aarch64-linux-android \
			-Z build-std=core,std,alloc,proc_macro
android32:
	env \
		CROSS_CONTAINER_ENGINE=podman \
		LIBSECCOMP_LINK_TYPE=static \
		PKG_CONFIG_ALL_STATIC=1 \
		TMPDIR=$(HOME)/tmp \
		cross build --release --locked --features $(CARGOFEATS) $(CARGOFLAGS) \
			--target armv7-linux-androideabi \
			-Z build-std=core,std,alloc,proc_macro
android.tar.xz: android
	tar -cpJf $@ \
		-C target/aarch64-linux-android/release \
		$$(cd target/aarch64-linux-android/release && for p in $(PROGRAMS); do [ -e $$p ] && echo $$p; done)

ctf: arm64
	scp data/ctf.syd-3 root@syd.chesswob.org:/etc/user.syd-3
	$(RSYNC) -avze ssh target/aarch64-unknown-linux-musl/release/syd* root@syd.chesswob.org:/usr/bin/
arm64:
	env \
		CROSS_CONTAINER_ENGINE=podman \
		LIBSECCOMP_LINK_TYPE=static \
		PKG_CONFIG_ALL_STATIC=1 \
		TMPDIR=$(HOME)/tmp \
		cross build --release --locked --features $(CARGOFEATS) $(CARGOFLAGS) \
			--target aarch64-unknown-linux-musl \
			-Z build-std=core,std,alloc,proc_macro
arm-img:
	if ! podman image exists local/cross-arm:musl 2>/dev/null; then \
		TMPDIR=$(HOME)/tmp podman build --build-arg CROSS_BASE_IMAGE=ghcr.io/cross-rs/armv7-unknown-linux-musleabihf:edge -f dev/Dockerfile.arm.musl -t local/cross-arm:musl .; \
	fi
arm: arm-img
	env \
		CROSS_CONTAINER_ENGINE=podman \
		LIBSECCOMP_LINK_TYPE=static \
		PKG_CONFIG_ALL_STATIC=1 \
		RUSTFLAGS='-Ctarget-feature=+crt-static -Clink-arg=-L/usr/local/lib/gcc/arm-linux-musleabihf/9.2.0 -Clink-arg=-L/usr/local/arm-linux-musleabihf/lib -Clink-arg=-Wl,--start-group -Clink-arg=-Wl,-Bstatic -Clink-arg=-lgcc_eh -Clink-arg=-lgcc -Clink-arg=-Wl,--end-group' \
		TMPDIR=$(HOME)/tmp \
		cross build --release --locked --features $(CARGOFEATS) $(CARGOFLAGS) \
			--target armv7-unknown-linux-musleabihf \
			-Z build-std=core,std,alloc,proc_macro

musl:
	env \
		CROSS_CONTAINER_ENGINE=podman \
		LIBSECCOMP_LINK_TYPE=static \
		PKG_CONFIG_ALL_STATIC=1 \
		RUSTFLAGS='$(RUSTFLAGS) -Ctarget-cpu=x86-64-v2' \
		TMPDIR=$(HOME)/tmp \
		cross build --release --locked --features $(CARGOFEATS) $(CARGOFLAGS) \
			--target x86_64-unknown-linux-musl \
			-Z build-std=core,std,alloc,proc_macro
musl32:
	env \
		CROSS_CONTAINER_ENGINE=podman \
		LIBSECCOMP_LINK_TYPE=static \
		PKG_CONFIG_ALL_STATIC=1 \
		RUSTFLAGS='$(RUSTFLAGS) -Ctarget-cpu=pentium4' \
		TMPDIR=$(HOME)/tmp \
		cross build --release --locked --features $(CARGOFEATS) $(CARGOFLAGS) \
			--target i686-unknown-linux-musl \
			-Z build-std=core,std,alloc,proc_macro
test32: musl32
	$(RSYNC) -avze ssh --partial target/i686-unknown-linux-musl/release/syd* cfarm27.cfarm.net:.local/bin/
m68k:
	env \
		CROSS_CONTAINER_ENGINE=podman \
		LIBSECCOMP_LINK_TYPE=static \
		PKG_CONFIG_ALL_STATIC=1 \
		CFLAGS="--sysroot=${SYSROOT} -O0 -pipe -mstrict-align -fno-builtin -fno-lto" \
		LDFLAGS="--sysroot=${SYSROOT} -static" \
		RUSTFLAGS='$(RUSTFLAGS) -Ctarget-cpu=M68020 -Ccode-model=large -Crelocation-model=static -Clink-arg=--sysroot=/opt/m68k-generic -Clink-arg=-Wl,--start-group -Clink-arg=-Wl,-Bstatic -Clink-arg=-lseccomp -Clink-arg=-ldl -Clink-arg=-lc -Clink-arg=-Wl,--end-group' \
		TMPDIR=$(HOME)/tmp \
		cross +nightly build --release --locked --features $(CARGOFEATS) $(CARGOFLAGS) \
			--no-default-features \
			--target m68k-unknown-linux-gnu \
			-Z build-std=core,std,alloc,proc_macro
ppc-img:
	if ! podman image exists local/cross-ppc:trixie 2>/dev/null; then \
		TMPDIR=$(HOME)/tmp podman build -f dev/Dockerfile.ppc.musl -t local/cross-ppc:trixie .; \
	fi
ppc: ppc-img
	env \
		CROSS_CONTAINER_ENGINE=podman \
		CC=powerpc-linux-musl-gcc \
		LIBSECCOMP_LINK_TYPE=static \
		LIBSECCOMP_LIB_PATH=/opt/powerpc-linux-musl-cross/lib \
		PKG_CONFIG_ALL_STATIC=1 \
		PKG_CONFIG_ALLOW_CROSS=1 \
		PKG_CONFIG_PATH=/project/libs/lib/pkgconfig:/opt/powerpc-linux-musl-cross/powerpc-linux-musl/lib/pkgconfig \
		RUSTFLAGS='-g -Ctarget-feature=+crt-static -Clink-self-contained=no -Clink-arg=-B/opt/powerpc-linux-musl-cross/powerpc-linux-musl/lib -Clink-arg=-B/opt/powerpc-linux-musl-cross/lib/gcc/powerpc-linux-musl/11.2.1 -Clink-arg=-L. -Clink-arg=-Wl,--start-group -Clink-arg=-Wl,-Bstatic -Clink-arg=-l:libgcc.a -Clink-arg=-l:libgcc_eh.a -Clink-arg=-l:libc.a -Clink-arg=-Wl,--end-group -Clink-arg=-L/project/libs/lib -Clink-arg=-L/opt/powerpc-linux-musl-cross/powerpc-linux-musl/lib -Clink-arg=-L/opt/powerpc-linux-musl-cross/lib' \
		TMPDIR=$(HOME)/tmp \
		cross +nightly build --release --locked --features $(CARGOFEATS) $(CARGOFLAGS) \
			--target powerpc-unknown-linux-musl \
			-Z build-std=core,std,alloc,proc_macro
riscv64:
	env \
		CROSS_CONTAINER_ENGINE=podman \
		LIBSECCOMP_LINK_TYPE=static \
		PKG_CONFIG_ALL_STATIC=1 \
		TMPDIR=$(HOME)/tmp \
		cross build --release --locked --features $(CARGOFEATS) $(CARGOFLAGS) \
			--target riscv64gc-unknown-linux-gnu \
			-Z build-std=core,std,alloc,proc_macro

ltp:
	set -x &&\
	SYD_TEST_LTP="$$(mktemp -d ltp.XXXXXXXXXX)" && \
	cd "$$SYD_TEST_LTP" && \
	$(GIT) clone --depth 1 --recursive https://github.com/linux-test-project/ltp.git ltp.git && \
	cd ltp.git && \
	$(MAKE) autotools >ltp-auto.log 2>&1 && \
	./configure >ltp-conf.log 2>&1 && \
	$(MAKE) -j$(JOBS) all >ltp-make.log 2>&1 && \
	$(MAKE) -j$(JOBS) install >ltp-inst.log 2>&1 && \
	cd /opt/ltp && \
	syd -pltp -- ./kirk -vv --run-suite syscalls

prove:
	@if [ -z "$$SYD_TEST_EXPENSIVE" ]; then \
		$(MAKE) TLOGS="$(TLOGS)" prove-def; \
	else \
		$(MAKE) TLOGS="$(TLOGS)" prove-exp; \
	fi
prove-def:
	@echo "Test log directory is $(TLOGS)."
	mkdir -m700 -p "$(TLOGS)"
	syd-test -l | awk '/^[[:space:]]*[0-9]+[[:space:]]*:/ { \
		id=$$1; sub(":", "", id); \
		name=$$0; sub("^[[:space:]]*[0-9]+[[:space:]]*:[[:space:]]*", "", name); \
		if (name !~ /^exp_/) \
			printf "syd-test %s 2>&1 | tee \"%s/%s_%s.tap\"\n", id, "$(TLOGS)", id, name; \
	}' | $(PROVE)
prove-exp:
	@echo "Test log directory is $(TLOGS)."
	mkdir -m700 -p "$(TLOGS)"
	SYD_TEST_EXPENSIVE=1 syd-test -l | awk '/^[[:space:]]*[0-9]+[[:space:]]*:/ { \
		id=$$1; sub(":", "", id); \
		name=$$0; sub("^[[:space:]]*[0-9]+[[:space:]]*:[[:space:]]*", "", name); \
		if (name ~ /^exp_/) \
			printf "SYD_TEST_EXPENSIVE=1 syd-test %s 2>&1 | tee \"%s/%s_%s.tap\"\n", id, "$(TLOGS)", id, name; \
	}' | $(PROVE)

spell:
	$(CODESPELL) $(CODESPELL_ARGS) --count $(SPELL_PATHS)
spell-fix:
	$(CODESPELL) $(CODESPELL_ARGS) -w -i 3 $(SPELL_PATHS)
diff:
	find src \
		-not -wholename '*/ioctl/*' \
		-not -wholename '*/t/*' \
		-not -wholename '*/utils/*' \
		-type f -name '*.rs' |\
		$(SUPERDIFF) -v | tee superdiff.log

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 \
		--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) < $< > $@

target/man/%.html: man/%
	mkdir -m700 -p target/man
	$(MANDOC) -Thtml $< > $@

target/man/%.md: man/%
	mkdir -m700 -p target/man
	$(PANDOC) -tmarkdown $< -o $@
	sed -i 's/^#/##/' $@
	sed -i '1s/^/# $(basename $*)($(patsubst .%,%,$(suffix $*)))\n/' $@

doc/manual/syd-manual.tex: $(MARKDOWNS)
	pandoc $(MARKDOWNS) -t latex -o $@.tmp \
		--standalone --citeproc \
		--toc --toc-depth=6 \
		--wrap=none \
		--variable documentclass=scrbook \
		--variable papersize=a4 \
		--variable fontsize=12pt \
		--variable geometry=margin=0.25in \
		--variable mainfont=texgyretermes \
		--variable mainfontoptions=Ligatures=TeX \
		--variable mainfontoptions=Extension=.otf \
		--variable mainfontoptions=BoldFont=*-bold \
		--variable mainfontoptions=UprightFont=*-regular \
		--variable mainfontoptions=ItalicFont=*-italic \
		--variable mainfontoptions=BoldItalicFont=*-bolditalic \
		--variable mainfontoptions='SmallCapsFeatures={Letters=SmallCaps}' \
		--variable mathfont=texgyretermes-math.otf \
		--variable mathfontoptions=Ligatures=TeX \
		--variable sansfont=texgyreheros \
		--variable sansfontoptions=Ligatures=TeX \
		--variable sansfontoptions=Extension=.otf \
		--variable sansfontoptions=BoldFont=*-bold \
		--variable sansfontoptions=UprightFont=*-regular \
		--variable sansfontoptions=ItalicFont=*-italic \
		--variable sansfontoptions=BoldItalicFont=*-bolditalic \
		--variable sansfontoptions='SmallCapsFeatures={Letters=SmallCaps}' \
		--variable header-includes="\input{./doc/manual/header.tex}" \
		--include-before-body=./doc/manual/body-before.tex \
		--include-after-body=./doc/manual/body-after.tex
	$(SYDTEX) < $@.tmp > $@
	$(RM_RF) $@.tmp
doc/manual/vc.tex: doc/manual/syd-manual.tex doc/manual/vc-git.awk
	/bin/sh -cex "cd doc/manual && ./vc"

doc/manual/syd-manual.pdf: doc/manual/syd-manual.tex doc/manual/vc.tex
	$(PDFLATEX) --output-directory=doc/manual doc/manual/syd-manual >>target/syd-manual.log
	$(PDFLATEX) --output-directory=doc/manual doc/manual/syd-manual >>target/syd-manual.log

.PHONY: cave dist clean debug doc fmt sec man install-man publish upload watch who
.PHONY: check doccheck distcheck pandoracheck pandoracheckverbose
.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
.PHONY: cross mips64el mips64el-img mipsel mipsel-img musl musl32 arm arm-img arm64 android android32 ppc ppc-img
.PHONY: ctf ltp prove prove-def prove-exp pgo prof
.PHONY: diff spell spell-fix
.PHONY: html md pdf tex
.PHONY: kcov kcovinfo kcovinstall kcovup kcovdown kcovmaint
