PROJ_REPO := github.com/habedi/spart
BINARY_NAME := $(or $(PROJ_BINARY), $(notdir $(PROJ_REPO)))
BINARY = :target/release/$(BINARY_NAME)
PATH := /snap/bin:$(PATH)
RUST_BACKTRACE := 0
RUST_LOG := info
WHEEL_DIR := dist
PYSPART_DIR := pyspart
PY_DEP_MNGR := uv
WASM_TARGETS := wasm32-unknown-unknown wasm32-wasip1
PYSPART_VERSION := $(shell grep -m1 '^version = ' Cargo.toml | sed 's/.*"\(.*\)".*/\1/')
WHEEL_FILES := $(shell ls $(PYSPART_DIR)/$(WHEEL_DIR)/pyspart-$(PYSPART_VERSION)-*.whl 2>/dev/null)
SDIST_FILE := $(shell ls $(PYSPART_DIR)/$(WHEEL_DIR)/pyspart-$(PYSPART_VERSION).tar.gz 2>/dev/null)
TARPAULIN_VERSION=0.32.0
NEXTEST_VERSION=0.9.97-b.2
AUDIT_VERSION=0.22.1
CAREFUL_VERSION=0.4.8
.DEFAULT_GOAL := help
.PHONY: help
help:
@echo "Usage: make <target>"
@echo ""
@echo "Targets:"
@grep -E '^[a-zA-Z_-]+:.*## .*$$' Makefile | \
awk 'BEGIN {FS = ":.*## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'
.PHONY: format
format:
@echo "Formatting Rust files..."
@cargo fmt
.PHONY: test
test: format
@echo "Running tests..."
@RUST_BACKTRACE=$(RUST_BACKTRACE) cargo test -- --nocapture
.PHONY: coverage
coverage: format
@echo "Generating test coverage report..."
@cargo tarpaulin --out Xml --out Html
.PHONY: build
build: format
@echo "Building the project..."
@cargo build --release
.PHONY: wasm
wasm:
@echo "Building the library for WebAssembly..."
@for target in $(WASM_TARGETS); do \
echo " $$target"; \
rustup target add $$target >/dev/null 2>&1 || true; \
cargo build --lib --all-features --target $$target || exit 1; \
done
@echo "The library builds for: $(WASM_TARGETS)"
@echo "Note: --lib is deliberate. Tests, examples, and benches pull in dev-dependencies"
@echo " (proptest -> rusty-fork -> wait-timeout) that do not compile for WebAssembly,"
@echo " so only the library itself is checked here."
.PHONY: run
run: build
@echo "Running the $(BINARY) binary..."
@./$(BINARY)
.PHONY: run-examples
run-examples: build
@echo "Running Rust examples..."
@cargo run --example quadtree
@cargo run --example octree
@cargo run --example kdtree
@cargo run --example rtree
@cargo run --example rstar_tree
.PHONY: run-py-examples
run-py-examples: develop-py
@echo "Running Python examples..."
@bash -c "source .venv/bin/activate && python pyspart/examples/quadtree.py"
@bash -c "source .venv/bin/activate && python pyspart/examples/octree.py"
@bash -c "source .venv/bin/activate && python pyspart/examples/kdtree.py"
@bash -c "source .venv/bin/activate && python pyspart/examples/rtree.py"
@bash -c "source .venv/bin/activate && python pyspart/examples/rstar_tree.py"
.PHONY: clean
clean:
@echo "Cleaning up..."
@cargo clean
@rm -rf $(WHEEL_DIR) dist/ $(PYSPART_DIR)/$(WHEEL_DIR) $(PYSPART_DIR)/*.so $(PYSPART_DIR)/target
.PHONY: install-snap
install-snap:
@echo "Installing the snap package..."
@sudo apt-get update
@sudo apt-get install -y snapd
@sudo snap refresh
@sudo snap install rustup --classic
.PHONY: install-deps
install-deps: install-snap
@echo "Installing dependencies..."
@rustup component add rustfmt clippy
@cargo install --locked cargo-tarpaulin --version ${TARPAULIN_VERSION}
@cargo install --locked cargo-nextest --version ${NEXTEST_VERSION}
@cargo install --locked cargo-audit --version ${AUDIT_VERSION}
@cargo install --locked cargo-careful --version ${CAREFUL_VERSION}
@sudo apt-get install -y python3-pip
@pip install $(PY_DEP_MNGR)
.PHONY: lint
lint: format
@echo "Linting Rust files..."
@cargo clippy -- -D warnings -D clippy::unwrap_used -D clippy::expect_used
.PHONY: publish
publish:
@echo "Publishing the package to Cargo registry..."
@cargo publish --token $(CARGO_REGISTRY_TOKEN)
.PHONY: bench
bench:
@echo "Running benchmarks..."
@cargo bench
.PHONY: audit
audit:
@echo "Running security audit..."
@cargo audit
.PHONY: careful
careful:
@echo "Running security checks..."
@RUST_BACKTRACE=$(RUST_BACKTRACE) cargo careful run
.PHONY: nextest
nextest:
@echo "Running tests using nextest..."
@RUST_BACKTRACE=$(RUST_BACKTRACE) cargo nextest run
.PHONY: docs
docs: format
@echo "Generating documentation..."
@cargo doc --no-deps --document-private-items
.PHONY: fix-lint
fix-lint:
@echo "Fixing linter warnings..."
@cargo clippy --fix --allow-dirty --allow-staged --all-targets --workspace --all-features -- -D warnings -D clippy::unwrap_used -D clippy::expect_used
.PHONY: develop-py
develop-py:
@echo "Building and installing PySpart..."
@bash -c "source .venv/bin/activate && cd $(PYSPART_DIR) && unset CONDA_PREFIX && maturin develop"
.PHONY: wheel
wheel:
@echo "Building the PySpart wheel..."
@(cd $(PYSPART_DIR) && maturin build --release --out $(WHEEL_DIR) --auditwheel check)
.PHONY: sdist
sdist:
@echo "Building the PySpart source distribution..."
@(cd $(PYSPART_DIR) && maturin sdist --out $(WHEEL_DIR))
.PHONY: wheel-manylinux
wheel-manylinux:
@echo "Building the `manylinux` PySpart wheel..."
@(cd $(PYSPART_DIR) && maturin build --release --out $(WHEEL_DIR) --auditwheel check --zig)
.PHONY: test-py
test-py: develop-py
@echo "Running Python tests..."
@$(PY_DEP_MNGR) run --extra dev pytest
.PHONY: publish-py
publish-py: wheel-manylinux sdist
@echo "Publishing PySpart $(PYSPART_VERSION) to PyPI..."
@echo "Note: this builds for the host platform only. The publish_py.yml workflow builds all eight"
@echo " platform wheels plus the sdist, and is the intended release path."
@if [ -z "$(WHEEL_FILES)" ]; then \
echo "Error: no wheel for version $(PYSPART_VERSION) in $(PYSPART_DIR)/$(WHEEL_DIR)."; \
exit 1; \
fi
@if [ -z "$(SDIST_FILE)" ]; then \
echo "Error: no sdist for version $(PYSPART_VERSION) in $(PYSPART_DIR)/$(WHEEL_DIR)."; \
exit 1; \
fi
@if [ -z "$$PYPI_TOKEN" ]; then \
echo "Error: PYPI_TOKEN is not set in the environment."; \
exit 1; \
fi
@echo "Uploading: $(WHEEL_FILES) $(SDIST_FILE)"
@UV_PUBLISH_TOKEN="$$PYPI_TOKEN" $(PY_DEP_MNGR) publish --check-url https://pypi.org/simple/ $(WHEEL_FILES) $(SDIST_FILE)
.PHONY: setup-hooks
setup-hooks:
@echo "Installing Git hooks..."
@pre-commit install --hook-type pre-commit
@pre-commit install --hook-type pre-push
@pre-commit install-hooks
.PHONY: test-hooks
test-hooks:
@echo "Testing Git hooks..."
@pre-commit run --all-files