axhash 1.0.0

Simple Rust entrypoint for the AxHash engine.
Documentation
# Axhash development Makefile.
#
# Run `make help` to list everything.
# Override variables on the command line, e.g. `make smhasher SMHASHER3_DIR=/path/to/smhasher3`.

CARGO ?= cargo
SMHASHER3_DIR ?= $(HOME)/Development/smhasher3
PKG_FFI := axhash-ffi
PKG_CORE := axhash-core
PKG_RUST := axhash

.DEFAULT_GOAL := help

##@ General

.PHONY: help
help: ## Print this help
	@awk 'BEGIN {FS = ":.*##"; printf "Usage: make \033[36m<target>\033[0m\n"} \
		/^[a-zA-Z][a-zA-Z0-9_-]*:.*?##/ { printf "  \033[36m%-22s\033[0m %s\n", $$1, $$2 } \
		/^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) }' $(MAKEFILE_LIST)

.PHONY: version
version: ## Print workspace version
	@grep -E '^version =' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/Workspace version: \1/'

##@ Build

.PHONY: build
build: ## Release build of every workspace crate
	$(CARGO) build --workspace --release

.PHONY: check
check: ## Fast cargo check across the workspace
	$(CARGO) check --workspace --all-targets

.PHONY: ffi
ffi: ## Build axhash-ffi static + dynamic libs in release mode
	$(CARGO) build --release -p $(PKG_FFI)
	@echo ""
	@echo "Output:"
	@ls -lh target/release/libaxhash_ffi.* 2>/dev/null || true

.PHONY: no-std
no-std: ## Verify axhash-core builds with no_std (no default features)
	$(CARGO) build -p $(PKG_CORE) --no-default-features

##@ Test

.PHONY: test
test: ## Run all workspace tests (debug)
	$(CARGO) test --workspace

.PHONY: test-release
test-release: ## Run all workspace tests in release mode
	$(CARGO) test --workspace --release

.PHONY: parity
parity: ## Run only the scalar-vs-SIMD bit-identity parity tests
	$(CARGO) test -p $(PKG_CORE) --lib backend_parity -- --nocapture

##@ Quality

.PHONY: fmt
fmt: ## Format all code with rustfmt
	$(CARGO) fmt --all

.PHONY: fmt-check
fmt-check: ## Check formatting without modifying files
	$(CARGO) fmt --all -- --check

.PHONY: lint
lint: ## Run clippy on libs (denies warnings)
	$(CARGO) clippy --workspace --lib --all-features -- -D warnings

.PHONY: lint-all
lint-all: ## Run clippy on everything including benches & tests
	$(CARGO) clippy --workspace --all-targets --all-features -- -D warnings

##@ Benchmarks

.PHONY: bench
bench: clean-target ## Clean release artifacts, then run ALL benchmarks (~minutes)
	bash scripts/bench.sh

.PHONY: bench-quick
bench-quick: ## Run all benchmarks without cleaning first
	bash scripts/bench.sh

.PHONY: bench-throughput
bench-throughput: ## Run throughput benchmark only
	bash scripts/bench.sh throughput

.PHONY: bench-latency
bench-latency: ## Run latency benchmark only
	bash scripts/bench.sh latency

.PHONY: bench-streaming
bench-streaming: ## Run streaming benchmark only
	bash scripts/bench.sh streaming

.PHONY: bench-hashmap
bench-hashmap: ## Run HashMap benchmark only
	bash scripts/bench.sh hashmap

.PHONY: bench-concurrent
bench-concurrent: ## Run concurrent benchmark only
	bash scripts/bench.sh concurrent

.PHONY: bench-report
bench-report: ## Open last bench HTML report (criterion)
	@if [ -f target/criterion/report/index.html ]; then \
		(command -v open >/dev/null && open target/criterion/report/index.html) || \
		(command -v xdg-open >/dev/null && xdg-open target/criterion/report/index.html) || \
		echo "Open manually: target/criterion/report/index.html"; \
	else \
		echo "No criterion report found. Run 'make bench' first."; \
	fi

##@ SMHasher3 quality validation

.PHONY: smhasher
smhasher: ffi ## Rebuild axhash-ffi + run FULL SMHasher3 battery (~5 min)
	@if [ ! -d "$(SMHASHER3_DIR)/build" ]; then \
		echo "SMHasher3 build dir not found at $(SMHASHER3_DIR)/build"; \
		echo "Either set SMHASHER3_DIR=/path/to/smhasher3, or build it first:"; \
		echo "  cd $(SMHASHER3_DIR) && mkdir -p build && cd build && cmake .. && make -j8"; \
		exit 1; \
	fi
	cd $(SMHASHER3_DIR)/build && make SMHasher3 -j8
	$(SMHASHER3_DIR)/build/SMHasher3 AxHash_64 | tail -25

.PHONY: smhasher-verify
smhasher-verify: ffi ## Quick SMHasher3 verification-code check only (~5 sec)
	cd $(SMHASHER3_DIR)/build && make SMHasher3 -j8
	$(SMHASHER3_DIR)/build/SMHasher3 --test=VerifyAll 2>&1 | grep -i axhash

##@ Documentation

.PHONY: doc
doc: ## Build rustdoc and open in browser
	$(CARGO) doc --workspace --no-deps --open

.PHONY: doc-build
doc-build: ## Build rustdoc without opening
	$(CARGO) doc --workspace --no-deps

##@ CI simulation

.PHONY: ci
ci: fmt-check lint test no-std ## Simulate CI locally (fmt + lint + test + no_std)
	@echo ""
	@echo "CI checks passed locally."

.PHONY: pre-release
pre-release: ci parity smhasher-verify ## Comprehensive pre-release validation
	@echo ""
	@echo "Pre-release validation complete:"
	@echo "  - fmt-check OK"
	@echo "  - clippy OK"
	@echo "  - tests pass"
	@echo "  - no_std OK"
	@echo "  - backend parity OK"
	@echo "  - SMHasher3 verification OK"
	@echo ""
	@echo "Next steps:"
	@echo "  make dry-publish   # Verify crates.io publish would work"
	@echo "  git tag v\$$(grep -E '^version =' Cargo.toml | head -1 | sed 's/version = \"\\(.*\\)\"/\\1/')"
	@echo "  git push --tags    # Triggers release.yml"

##@ Release

.PHONY: dry-publish
dry-publish: ## cargo publish --dry-run for all crates in dependency order
	$(CARGO) publish -p $(PKG_CORE) --dry-run
	$(CARGO) publish -p $(PKG_RUST) --dry-run
	$(CARGO) publish -p $(PKG_FFI) --dry-run

.PHONY: package
package: ffi ## Build a local release archive (same script CI uses)
	@TRIPLE=$$(rustc -vV | sed -n 's/host: //p'); \
	echo "Packaging for $$TRIPLE"; \
	python scripts/package-release.py "$$TRIPLE" release "$$PWD/dist"
	@echo ""
	@echo "Local release archive in: dist/"
	@ls -lh dist/

##@ Maintenance

.PHONY: clean
clean: ## Remove entire target directory
	$(CARGO) clean

.PHONY: clean-target
clean-target: ## Lighter clean: release + criterion artifacts only
	rm -rf target/release target/criterion
	@find target -maxdepth 2 -name release -type d -exec rm -rf {} + 2>/dev/null || true

.PHONY: clean-dist
clean-dist: ## Remove local release archives
	rm -rf dist/

.PHONY: update
update: ## Update Cargo.lock to latest compatible versions
	$(CARGO) update

.PHONY: tree
tree: ## Show workspace dependency tree
	$(CARGO) tree --workspace --depth 1