# This justfile contains jobs for a mixed Rust/Python project, but can be used for sole Rust
# projects as well, as forwarding commands check whether HAS_PYTHON is set to a value to determine
# whether the Python build, test, lint, etc. jobs must be run in conjunction with their Rust
# counterparts, which are always run.
# Version 2025-05-12
set dotenv-load
set export
# Whether this project has Python bindings.
HAS_PYTHON := env("HAS_PYTHON", "")
# Shorthand for the justfile directory.
HERE := env("HERE", justfile_directory())
# Container image to use when running commands in a dev container.
IMAGE := env("IMAGE", "registry.gitlab.com/ratio-case-os/docker/rust-ci")
# The tag. Switch to "cross" for cross compilation.
TAG := env("TAG", if HAS_PYTHON == "" { "latest" } else { "cross" })
# Compilation targets if required.
# TARGETS := env("TARGETS", "x86_64-unknown-linux-gnu x86_64-pc-windows-msvc aarch64-unknown-linux-gnu aarch64-pc-windows-msvc")
TARGETS := env("TARGETS", "x86_64-unknown-linux-gnu x86_64-apple-darwin x86_64-pc-windows-msvc aarch64-unknown-linux-gnu aarch64-apple-darwin aarch64-pc-windows-msvc")
# Optional Rust crate to target within a workspace.
CRATE := env("CRATE", "")
# String of flame scripts (space separated) to run and calculate flamegraphs for.
FLAMES := env("FLAMES", "")
# Flamegraph output directory.
FLAMES_DIR := env("FLAMES_DIR", HERE + "/target/flames")
# The Python crate name.
PYTHON_CRATE := env("PYTHON_CRATE", CRATE + "-py")
# The directory containing the Python bindings crate.
PYTHON_DIR := env("PYTHON_DIR", HERE / PYTHON_CRATE)
# The Python wheels output directory.
WHEELS_DIR := env("WHEELS_DIR", HERE + "/target/wheels")
# Show the recipe list.
default:
@just --list --justfile {{justfile()}}
# Clean generated project files.
clean:
cargo clean
rm ./ratio-graph-py/python/**/*.so
# Pull the dev container image from the registry.
pull-container:
podman pull {{IMAGE}}:{{TAG}}
# Run a dev container with the project directory mounted.
run-container args="" cmd="bash":
podman run --userns=keep-id --rm -it -v {{HERE}}:/home/ratio/work:z {{args}} {{IMAGE}}:{{TAG}} {{cmd}}
# Run a recipe in a dev container.
in-container recipe="":
podman run --userns=keep-id --rm -it -v {{HERE}}:/home/ratio/work:z {{IMAGE}}:{{TAG}} just {{recipe}}
@install:
if [[ -n "{{HAS_PYTHON}}" ]]; then just install-py; fi
# Install the Python bindings environment.
install-py *args:
cd {{PYTHON_DIR}} && \
uv lock && \
uv sync --all-extras --all-groups && \
uvx maturin develop --uv && \
uv run python -m maturin_import_hook site install
# Build package in release mode.
@build: build-rs
if [[ -n "{{HAS_PYTHON}}" ]]; then just build-py; fi
# Build Rust crate(s) for all available targets.
build-rs:
cargo build --release --all-targets --color always {{ if CRATE != "" { "-p " + CRATE } else { "" } }}
# Build Python bindings for all set targets.
build-py *args:
rm -rf {{WHEELS_DIR}}
cd {{PYTHON_DIR}} && for i in {{TARGETS}}; do uvx maturin[zig] build --zig --release --target "$i" {{args}}; done
# Create flamegraph of certain scripts for performance measurement.
flamegraph *args:
#!/bin/bash
mkdir -p {{FLAMES_DIR}}
for i in {{FLAMES}}; \
do CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph -o "{{FLAMES_DIR}}/$i.svg" --no-inline --unit-test -- "$i" --color always {{args}} || true; \
done
rm -f perf.data
rm -f perf.data.old
alias flame := flamegraph
# Lint both the Rust code, license usage, and Python bindings.
@lint: lint-rs lint-deny
if [[ -n "{{HAS_PYTHON}}" ]]; then just lint-py; fi
# Lint the Rust code.
lint-rs *args:
cargo clippy --all-targets --color always {{ if CRATE != "" { "-p " + CRATE } else { "" } }} {{args}}
cargo +nightly fmt --check
# Lint the Python bindings if any.
lint-py *args:
uvx ruff check {{PYTHON_DIR}} {{args}}
# Lint project dependencies and licenses
lint-deny:
cargo deny check
# Fix the Rust code and Python bindings if any.
@fix: fix-rs
if [[ -n "{{HAS_PYTHON}}" ]]; then just fix-py; fi
# Fix the Rust code.
fix-rs *args:
cargo clippy --all-targets --fix --allow-dirty --allow-staged --color always {{args}}
cargo +nightly fmt --all
# Fix the Python bindings if any.
fix-py *args:
uvx ruff format {{PYTHON_DIR}} {{args}}
# Run a single test cycle for the project.
@test: test-rs-coverage
if [[ -n "{{HAS_PYTHON}}" ]]; then just test-py; fi
# Run Rust code tests.
test-rs *args:
cargo test {{ if CRATE != "" { "-p " + CRATE } else { "" } }} {{args}}
# Run the Python bindings tests.
test-py *args:
cd {{PYTHON_DIR}} && uv run pytest {{args}}
# Run Rust tests with coverage.
test-rs-coverage *args:
cargo tarpaulin --tests --doc --color always --locked --out Xml --output-dir target/tarpaulin {{ if CRATE != "" { "-p " + CRATE } else { "" } }} {{args}}
# Run Rust tests in watch mode, or the Python suite if the bindings are enabled.
@watch *args:
if [[ -n "{{HAS_PYTHON}}" ]]; then just watch-py; else just watch-rs; fi
# Run Rust tests continuously.
watch-rs *args:
bacon -j test {{ if CRATE != "" { "-p " + CRATE } else { "" } }} {{args}}
# Run Python bindings test continuously.
watch-py *args:
bacon -j pytest {{args}}
# Build and publish both the Rust crate(s) as well as the Python bindings.
@publish: publish-rs
if [[ -n "{{HAS_PYTHON}}" ]]; then just publish-py; fi
# Build and publish the Rust crate.
publish-rs *args: build-rs
cargo publish --color always {{ if CRATE != "" { "-p " + CRATE } else { "" } }} {{args}}
# Build and publish the Python bindings.
publish-py *args:
uv publish {{args}} {{WHEELS_DIR}}/*
# Check for outdated packages.
outdated: outdated-rs
# Check for outdated packages for the Rust code.
outdated-rs *args:
cargo outdated --color always {{ if CRATE != "" { "-p " + CRATE } else { "" } }} {{args}}
# Upgrade all dependencies.
@upgrade: upgrade-rs
if [[ -n "{{HAS_PYTHON}}" ]]; then just update-py; fi
# Upgrade Rust crate dependencies.
upgrade-rs *args:
cargo upgrade {{ if CRATE != "" { "-p " + CRATE } else { "" } }} {{args}}
# Update Python lockfile.
update-py *args:
cd {{PYTHON_DIR}} && uv lock
# Update this justfile to the latest version.
update-justfile:
wget https://gitlab.com/ratio-case-os/justfiles/-/raw/main/justfiles/rust-ci.justfile -O "{{justfile()}}"