BIN := recon
RELEASE_BIN := target/release/$(BIN)
DEBUG_BIN := target/debug/$(BIN)
MANUAL_MD := docs/MANUAL.md
MANUAL_PDF := docs/MANUAL.pdf
CARGO ?= cargo
IMPERSONATE := --features impersonate
FEATURES ?=
.DEFAULT_GOAL := help
.PHONY: help build release all check test test-quiet fmt fmt-check clippy lint \
doc run install uninstall trim clean clean-all distclean size pdf \
flags examples bump-check ci \
build-impersonate release-impersonate all-impersonate \
check-impersonate test-impersonate run-impersonate \
install-impersonate ci-impersonate \
linux-deps linux tarball deb dist dist-clean-deb
help:
@awk 'BEGIN {FS = ":.*##"; printf "Usage: make \033[36m<target>\033[0m\n\nTargets:\n"} \
/^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-14s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
build:
$(CARGO) build $(FEATURES)
release:
$(CARGO) build --release $(FEATURES)
@$(MAKE) --no-print-directory trim
all: build release
build-impersonate:
$(CARGO) build $(IMPERSONATE)
release-impersonate:
$(CARGO) build --release $(IMPERSONATE)
@$(MAKE) --no-print-directory trim
all-impersonate: build-impersonate release-impersonate
check:
$(CARGO) check --all-targets $(FEATURES)
check-impersonate:
$(CARGO) check --all-targets $(IMPERSONATE)
test:
$(CARGO) test $(FEATURES)
test-impersonate:
$(CARGO) test $(IMPERSONATE)
test-quiet:
$(CARGO) test --quiet $(FEATURES)
fmt:
$(CARGO) fmt --all
fmt-check:
$(CARGO) fmt --all -- --check
clippy:
$(CARGO) clippy --all-targets -- -D warnings
lint: fmt-check clippy
run: ## Run the debug binary (use ARGS="..." to pass arguments)
$(CARGO) run $(FEATURES) -- $(ARGS)
run-impersonate: ## Run the impersonate-feature debug binary (use ARGS="..." to pass arguments)
$(CARGO) run $(IMPERSONATE) -- $(ARGS)
install: release
$(CARGO) install --path . --force $(FEATURES)
install-impersonate: release-impersonate
$(CARGO) install --path . --force $(IMPERSONATE)
uninstall:
$(CARGO) uninstall $(BIN) || true
doc:
$(CARGO) doc --no-deps
pdf: release
$(RELEASE_BIN) --md-to-pdf $(MANUAL_MD) \
--toc --toc-depth 3 --gfm \
--unsafe-html --page-break-on-h1 \
--doc-title 'recon User Manual' \
-o $(MANUAL_PDF)
flags: release
$(RELEASE_BIN) --flags
examples: release
$(RELEASE_BIN) --examples
KEEP ?= 2
trim: ## Prune stale build artifacts (keep newest KEEP=2 recon binaries; drop old-toolchain cruft) — incremental-safe
@$(CARGO) sweep --installed >/dev/null 2>&1 || true
@for prof in release debug; do \
dir="target/$$prof/deps"; \
[ -d "$$dir" ] || continue; \
ls -t "$$dir"/$(BIN)-* 2>/dev/null | grep -v '\.d$$' \
| tail -n +$$(($(KEEP)+1)) \
| while read -r f; do rm -f "$$f" "$$f.d"; done; \
done
@echo "trim: kept newest $(KEEP) $(BIN) binary copies per profile; swept old-toolchain artifacts"
clean:
$(CARGO) clean
clean-all: clean
rm -rf target/doc dump.rdb $(DIST)
distclean: clean-all
rm -f Cargo.lock
DIST := dist
LINUX_AMD64 := x86_64-unknown-linux-gnu
LINUX_ARM64 := aarch64-unknown-linux-gnu
LINUX_AMD64_MUSL := x86_64-unknown-linux-musl
LINUX_ARM64_MUSL := aarch64-unknown-linux-musl
GLIBC := 2.28
VERSION := $(shell grep -m1 '^version' Cargo.toml | sed -E 's/.*"([^"]+)".*/\1/')
linux-deps:
@command -v zig >/dev/null 2>&1 || { echo "missing: zig — brew install zig"; exit 1; }
@command -v cargo-zigbuild >/dev/null 2>&1 || { echo "missing: cargo-zigbuild — cargo install cargo-zigbuild"; exit 1; }
@command -v cargo-deb >/dev/null 2>&1 || { echo "missing: cargo-deb — cargo install cargo-deb"; exit 1; }
@rustup target list --installed | grep -q '^$(LINUX_AMD64)$$' || { echo "missing rustup target: $(LINUX_AMD64) — rustup target add $(LINUX_AMD64)"; exit 1; }
@rustup target list --installed | grep -q '^$(LINUX_ARM64)$$' || { echo "missing rustup target: $(LINUX_ARM64) — rustup target add $(LINUX_ARM64)"; exit 1; }
@rustup target list --installed | grep -q '^$(LINUX_AMD64_MUSL)$$' || { echo "missing rustup target: $(LINUX_AMD64_MUSL) — rustup target add $(LINUX_AMD64_MUSL)"; exit 1; }
@rustup target list --installed | grep -q '^$(LINUX_ARM64_MUSL)$$' || { echo "missing rustup target: $(LINUX_ARM64_MUSL) — rustup target add $(LINUX_ARM64_MUSL)"; exit 1; }
@echo "linux cross-build prerequisites OK"
linux: linux-deps
$(CARGO) zigbuild --release --features bundled-sqlite --target $(LINUX_AMD64).$(GLIBC)
$(CARGO) zigbuild --release --features bundled-sqlite --target $(LINUX_ARM64).$(GLIBC)
$(CARGO) zigbuild --release --features bundled-sqlite --target $(LINUX_AMD64_MUSL)
$(CARGO) zigbuild --release --features bundled-sqlite --target $(LINUX_ARM64_MUSL)
tarball: linux
mkdir -p $(DIST)
tar -czf $(DIST)/recon-$(VERSION)-x86_64-linux.tar.gz -C target/$(LINUX_AMD64)/release recon -C $(CURDIR) LICENSE README.md
tar -czf $(DIST)/recon-$(VERSION)-aarch64-linux.tar.gz -C target/$(LINUX_ARM64)/release recon -C $(CURDIR) LICENSE README.md
tar -czf $(DIST)/recon-$(VERSION)-x86_64-linux-musl.tar.gz -C target/$(LINUX_AMD64_MUSL)/release recon -C $(CURDIR) LICENSE README.md
tar -czf $(DIST)/recon-$(VERSION)-aarch64-linux-musl.tar.gz -C target/$(LINUX_ARM64_MUSL)/release recon -C $(CURDIR) LICENSE README.md
deb: linux
mkdir -p $(DIST)
$(CARGO) deb --no-build --no-strip --target $(LINUX_AMD64) --output $(DIST)/
$(CARGO) deb --no-build --no-strip --target $(LINUX_ARM64) --output $(DIST)/
$(CARGO) deb --no-build --no-strip --variant musl --target $(LINUX_AMD64_MUSL) --output $(DIST)/
$(CARGO) deb --no-build --no-strip --variant musl --target $(LINUX_ARM64_MUSL) --output $(DIST)/
dist: linux tarball deb
@echo "── dist/ ──"; ls -1 $(DIST)
dist-clean-deb:
rm -rf $(DIST)
size:
@if [ -d target ]; then \
du -sh target target/debug target/release target/doc 2>/dev/null | sort -hr; \
else \
echo "target/ does not exist"; \
fi
bump-check:
@grep -E '^version' Cargo.toml | head -1
@grep -E 'RELEASE_DATE' src/version.rs | head -1
ci: fmt-check clippy test
ci-impersonate: fmt-check clippy test test-impersonate