hilaus-arm's picture
Sync from arm/Optimized-Runtimes via hub-sync
565a0d7 verified
Raw History Blame Contribute Delete
21.5 kB
SHELL := /bin/bash
.DEFAULT_GOAL := help
# Shared commands and repository paths
ROOT := $(CURDIR)
DEV_VENV ?= $(ROOT)/.venv
PYTHON ?= python3
CLANG_FORMAT ?= $(DEV_VENV)/bin/clang-format
LOCK_COMPILER ?= pip-compile
TEST_PYTHONPATH := $(ROOT)/src:$(ROOT)/src/runtime/api/python:$(ROOT)
VERSION := $(strip $(file <$(ROOT)/VERSION))
# Checkpoint conversion
DEFAULT_CHECKPOINT := $(ROOT)/downloads/checkpoints/yolo26n.pt
CHECKPOINT ?= $(DEFAULT_CHECKPOINT)
CHECKPOINT_URL := https://github.com/ultralytics/assets/releases/download/v8.4.0/yolo26n.pt
CHECKPOINT_SHA256 := 9b09cc8bf347f0fc8a5f7657480587f25db09b34bf33b0652110fb03a8ad4fef
RESOLUTION ?= 640
DEFAULT_MODEL_ROOT := $(ROOT)/dist/models
MODEL ?= $(DEFAULT_MODEL_ROOT)/yolo26n-$(RESOLUTION)
MODEL_VALIDATION ?= $(ROOT)/dist/models/validation/yolo26n-$(RESOLUTION).json
# Native runtime
RUNTIME_BUILD ?= $(ROOT)/build/runtime
RUNTIME_DIST ?= $(ROOT)/dist/runtime
DEFAULT_RUNTIME := $(RUNTIME_DIST)/lib/libarm-yolo26n.so.0
RUNTIME ?= $(DEFAULT_RUNTIME)
PACKAGE_DIST ?= $(ROOT)/dist/packages
PYTHON_API_PACKAGE := $(PACKAGE_DIST)/arm_yolo26n-$(VERSION)-py3-none-any.whl
CONVERTER_PACKAGE := $(PACKAGE_DIST)/arm_yolo26n_converter-$(VERSION)-py3-none-any.whl
RELEASE_PACKAGE := $(PACKAGE_DIST)/arm-yolo26n-$(VERSION)-rpi5-trixie-arm64.tar.gz
RELEASE_DEVELOPMENT_PACKAGE := \
$(PACKAGE_DIST)/arm-yolo26n-$(VERSION)-rpi5-trixie-arm64-dev.tar.gz
RUNTIME_RELEASE ?= yolo26-raspberry-pi-5-v$(VERSION)
RUNTIME_ARCHIVE := $(notdir $(RELEASE_PACKAGE))
RUNTIME_REPOSITORY := arm/Optimized-Runtimes
RUNTIME_URL ?= https://github.com/$(RUNTIME_REPOSITORY)/releases/download/$(RUNTIME_RELEASE)/$(RUNTIME_ARCHIVE)
RUNTIME_DOWNLOAD := $(ROOT)/downloads/runtime/$(RUNTIME_RELEASE)/$(RUNTIME_ARCHIVE)
RUNTIME_CHECKSUM := $(RUNTIME_DOWNLOAD).sha256
# Examples
EXAMPLES_VENV ?= $(ROOT)/examples/.venv
EXAMPLES_PYTHON := $(EXAMPLES_VENV)/bin/python
DEFAULT_VIDEO := $(ROOT)/downloads/media/test-video.mp4
VIDEO ?= $(DEFAULT_VIDEO)
VIDEO_URL := https://raw.githubusercontent.com/google-ai-edge/mediapipe/0ad5a71bcdff3d756dc5b07f93765aaeb4152538/mediapipe/examples/desktop/object_detection/test_video.mp4
VIDEO_SHA256 := 710831289c00251c86eafb0b0d11cf6190fda02f95bcfaa5037511eabb5c9de8
VIDEO_OUTPUT ?= $(ROOT)/examples/video/output/detections.json
VIDEO_RENDERED ?= $(ROOT)/examples/video/output/rendered.mp4
CAMERA ?= 0
CAMERA_PREVIEW ?=
CAMERA_ARGS ?=
# The camera target stays display-independent unless the caller selects one of
# the preview backends supported by examples.picamera2.live.
ifeq ($(strip $(CAMERA_PREVIEW)),)
CAMERA_EXAMPLE := examples.picamera2.detect
else
CAMERA_EXAMPLE := examples.picamera2.live
endif
# Ultralytics benchmark
ULTRALYTICS_VENV ?= $(ROOT)/benchmarks/ultralytics/.venv
DATASET_REVISION := 97d6ea181e9dd0784db880b705e426ad23d424e1
DATASET_DIR := $(ROOT)/downloads/datasets/coco-128-by-sa-$(DATASET_REVISION)
DATASET_CONFIG := $(DATASET_DIR)/benchmark.yaml
DATA ?= $(DATASET_CONFIG)
ifeq ($(origin DATA), file)
DATA_REQUIREMENT := $(DATASET_CONFIG)
endif
SOURCE_REVISION ?= $(shell git rev-parse HEAD)
# Evidence is immutable, so each default invocation needs a fresh directory.
BENCHMARK_RUN_ID := $(shell date -u +%Y%m%dT%H%M%S%NZ)-$(shell printf '%.12s' "$(SOURCE_REVISION)")
BENCHMARK_OUTPUT ?= $(ROOT)/benchmarks/reporting/output/$(BENCHMARK_RUN_ID)
# Development checks
MODELS ?= $(ROOT)/dist/models/yolo26n-320:$(ROOT)/dist/models/yolo26n-480:$(ROOT)/dist/models/yolo26n-640:$(ROOT)/dist/models/yolo26n-800:$(ROOT)/dist/models/yolo26n-960:$(ROOT)/dist/models/yolo26n-1280
MODEL_DIRECTORIES := $(subst :, ,$(MODELS))
MODEL_ARTIFACTS := $(foreach model,$(MODEL_DIRECTORIES),\
$(model)/manifest.json $(model)/model.bin)
# The C API test needs one valid model to create a session. The Python
# end-to-end tests exercise every model in MODELS.
NATIVE_API_TEST_MODEL = $(firstword $(MODEL_DIRECTORIES))/model.bin
COVERAGE_ROOT ?= $(ROOT)/build/coverage
COVERAGE_RUNTIME_BUILD ?= $(COVERAGE_ROOT)/runtime
COVERAGE_RUNTIME ?= $(COVERAGE_RUNTIME_BUILD)/libarm-yolo26n.so
PYTHON_COVERAGE_REPORT := $(COVERAGE_ROOT)/python/coverage.xml
NATIVE_COVERAGE_REPORT := $(COVERAGE_ROOT)/native/coverage.xml
.PHONY: help
help:
@printf '%s\n' \
'YOLO26n Raspberry Pi 5 workflows:' \
'' \
'Build and conversion:' \
' make model [CHECKPOINT=/path/to/yolo26n.pt] RESOLUTION=640' \
' Convert a checkpoint under dist/models/. Without CHECKPOINT, download' \
' and verify the pinned official YOLO26n checkpoint.' \
'' \
' make model-check [CHECKPOINT=/path/to/yolo26n.pt] RESOLUTION=640' \
' Validate conversion and write a receipt. Without CHECKPOINT, download' \
' and verify the pinned official YOLO26n checkpoint.' \
'' \
' make runtime' \
' Configure, compile, test, and write the runtime to dist/runtime/.' \
' make runtime-configure' \
' Configure the runtime build and fetch its pinned dependencies.' \
'' \
' make release' \
' Build the runtime, converter, and Python API release packages.' \
'' \
' make clean' \
' Remove reproducible artifacts under build/ and dist/.' \
'' \
' make requirements-lock' \
' Regenerate the hash-locked Python dependency closures.' \
'' \
'Examples and benchmarks:' \
' make examples' \
' Install dependencies in examples/.venv and prepare a runtime and model.' \
'' \
' make example-video' \
' Write detections and a rendered MP4 under examples/video/output/.' \
'' \
' make example-camera [CAMERA=0] [CAMERA_PREVIEW=qtgl|drm|auto]' \
' Run Picamera2 detection; select CAMERA_PREVIEW for the live UI.' \
'' \
' make ultralytics-benchmark RUNTIME=... MODEL=... DATA=...' \
' Prepare a runtime, model, and Ultralytics, then run validation.' \
'' \
'Development checks:' \
' The hash-locked development environment is prepared automatically.' \
' make lint' \
' Prepare the development environment and run repository checks.' \
'' \
' make test-unit' \
' Run unit and command-interface tests without the runtime.' \
'' \
' make test-converter CHECKPOINT=/path/to/yolo26n.pt' \
' Run checkpoint-backed converter behavior tests.' \
'' \
' make test-end-to-end CHECKPOINT=... RUNTIME=... MODELS=/model/320:/model/480:...' \
' Run converter, runtime, and example behavior tests.' \
'' \
' make coverage CHECKPOINT=... MODELS=/model/320:/model/480:...' \
' Generate diagnostic Python and native coverage reports.'
# Python dependencies
.PHONY: requirements-lock
requirements-lock:
PIP_CONFIG_FILE=/dev/null CUSTOM_COMPILE_COMMAND='make requirements-lock' "$(LOCK_COMPILER)" \
--generate-hashes --allow-unsafe --strip-extras \
--output-file requirements.txt requirements.in
PIP_CONFIG_FILE=/dev/null CUSTOM_COMPILE_COMMAND='make requirements-lock' "$(LOCK_COMPILER)" \
--generate-hashes --allow-unsafe --strip-extras \
--output-file requirements-build.txt requirements-build.in
PIP_CONFIG_FILE=/dev/null CUSTOM_COMPILE_COMMAND='make requirements-lock' "$(LOCK_COMPILER)" \
--generate-hashes --allow-unsafe --strip-extras \
--output-file requirements-dev.txt requirements-dev.in
PIP_CONFIG_FILE=/dev/null CUSTOM_COMPILE_COMMAND='make requirements-lock' "$(LOCK_COMPILER)" \
--generate-hashes --allow-unsafe --strip-extras \
--output-file requirements-lock.txt requirements-lock.in
PIP_CONFIG_FILE=/dev/null CUSTOM_COMPILE_COMMAND='make requirements-lock' "$(LOCK_COMPILER)" \
--generate-hashes --allow-unsafe --strip-extras \
--output-file benchmarks/ultralytics/requirements.txt \
benchmarks/ultralytics/requirements.in
# Cleanup
.PHONY: clean
clean:
@printf 'Removing reproducible artifacts:\n %s\n %s\n' "$(ROOT)/build" "$(ROOT)/dist"
@cmake -E remove_directory "$(ROOT)/build"
@cmake -E remove_directory "$(ROOT)/dist"
# Checkpoint conversion
.PHONY: model model-check
model: $(MODEL)/manifest.json $(MODEL)/model.bin
@printf 'Model ready: %s\n' "$(MODEL)"
model-check: $(CHECKPOINT)
PYTHONPATH="$(ROOT)/src" "$(PYTHON)" -m converter.validate \
--checkpoint "$(CHECKPOINT)" \
--resolution "$(RESOLUTION)" \
--output "$(MODEL_VALIDATION)"
# Runtime
.PHONY: runtime-configure runtime
runtime-configure:
cmake -S src/runtime -B "$(RUNTIME_BUILD)" \
-DCMAKE_BUILD_TYPE=Release
runtime: runtime-configure
cmake --build "$(RUNTIME_BUILD)" \
--target arm_yolo26n yolo26n_runtime_api_test --parallel
@if test -f "$(MODEL)/model.bin"; then \
YOLO26N_RUNTIME_API_TEST_MODEL="$(MODEL)/model.bin" \
ctest --test-dir "$(RUNTIME_BUILD)" --output-on-failure; \
else \
ctest --test-dir "$(RUNTIME_BUILD)" --output-on-failure; \
fi
cmake --install "$(RUNTIME_BUILD)" --prefix "$(RUNTIME_DIST)" --component runtime
# Release packaging
.PHONY: release
release: runtime _release-converter _release-api
@# The runtime archive combines CMake's installed runtime with both wheels.
@# The development archive contains CMake's header and linker name. A staging
@# directory keeps those layouts separate and makes publication into
@# dist/packages atomic.
@staging="$$(mktemp -d --tmpdir="$(PACKAGE_DIST)" .release.XXXXXX)"; \
trap 'rm -rf -- "$$staging"' EXIT; \
mkdir "$$staging/runtime" "$$staging/development"; \
cmake --install "$(RUNTIME_BUILD)" --prefix "$$staging/runtime" \
--component runtime; \
cmake --install "$(RUNTIME_BUILD)" --prefix "$$staging/development" \
--component development; \
install --directory "$$staging/runtime/python"; \
install --mode 0644 "$(PYTHON_API_PACKAGE)" "$(CONVERTER_PACKAGE)" \
"$$staging/runtime/python/"; \
tar --create --gzip --file "$$staging/$(notdir $(RELEASE_PACKAGE))" \
--sort=name --mtime=@0 --owner=0 --group=0 --numeric-owner \
--directory "$$staging/runtime" lib python share; \
tar --create --gzip \
--file "$$staging/$(notdir $(RELEASE_DEVELOPMENT_PACKAGE))" \
--sort=name --mtime=@0 --owner=0 --group=0 --numeric-owner \
--directory "$$staging/development" include lib; \
cd "$$staging"; \
sha256sum "$(notdir $(RELEASE_PACKAGE))" \
> "$(notdir $(RELEASE_PACKAGE)).sha256"; \
sha256sum "$(notdir $(RELEASE_DEVELOPMENT_PACKAGE))" \
> "$(notdir $(RELEASE_DEVELOPMENT_PACKAGE)).sha256"; \
mv "$(notdir $(RELEASE_PACKAGE))" "$(notdir $(RELEASE_PACKAGE)).sha256" \
"$(notdir $(RELEASE_DEVELOPMENT_PACKAGE))" \
"$(notdir $(RELEASE_DEVELOPMENT_PACKAGE)).sha256" "$(PACKAGE_DIST)/"
# These targets build only repository source. They neither install nor resolve
# dependencies; callers provide the hash-locked build environment defined by
# requirements-build.txt. CI installs that environment with --require-hashes.
.PHONY: _release-converter
_release-converter: _dev
mkdir -p "$(PACKAGE_DIST)"
@# Give setuptools a self-contained project without writing build state into
@# the converter source directory or duplicating the repository version.
@staging="$$(mktemp -d)"; \
trap 'rm -rf -- "$$staging"' EXIT; \
cp src/converter/*.py src/converter/pyproject.toml \
src/converter/VERSION "$$staging/"; \
install --mode 0644 "$(ROOT)/../LICENSE" "$$staging/LICENSE"; \
PIP_CONFIG_FILE=/dev/null "$(DEV_VENV)/bin/python" -m pip wheel \
--no-deps --no-build-isolation --wheel-dir "$(PACKAGE_DIST)" "$$staging"
.PHONY: _release-api
_release-api: _dev
mkdir -p "$(PACKAGE_DIST)"
PIP_CONFIG_FILE=/dev/null "$(DEV_VENV)/bin/python" -m pip wheel \
--no-deps --no-build-isolation --wheel-dir "$(PACKAGE_DIST)" .
# Examples
.PHONY: examples example-video example-camera
examples: $(RUNTIME) $(MODEL)/manifest.json $(MODEL)/model.bin \
$(EXAMPLES_VENV)/pyvenv.cfg
@printf 'Examples ready:\n runtime: %s\n model: %s\n environment: %s\n' \
"$(RUNTIME)" "$(MODEL)" "$(EXAMPLES_VENV)"
$(EXAMPLES_VENV)/pyvenv.cfg: requirements-build.txt requirements.txt pyproject.toml \
$(wildcard src/runtime/api/python/arm_yolo26n/*.py)
@# Picamera2 is installed by Raspberry Pi OS, so camera examples must see system packages.
"$(PYTHON)" -m venv --system-site-packages "$(EXAMPLES_VENV)"
PIP_CONFIG_FILE=/dev/null "$(EXAMPLES_PYTHON)" -m pip install --require-hashes \
--requirement requirements-build.txt --requirement requirements.txt
PIP_CONFIG_FILE=/dev/null "$(EXAMPLES_PYTHON)" -m pip install \
--no-deps --no-build-isolation "$(ROOT)"
$(DEFAULT_CHECKPOINT):
@printf 'No checkpoint was supplied; downloading the pinned checkpoint to:\n %s\n' "$@"
mkdir -p "$(@D)"
curl --fail --location --show-error "$(CHECKPOINT_URL)" --output "$@.tmp"
echo "$(CHECKPOINT_SHA256) $@.tmp" | sha256sum --check
mv "$@.tmp" "$@"
# A source build and a release download establish the same runtime path. Attach
# the download rules only when no usable source-built distribution is present.
ifneq ($(shell test -f "$(DEFAULT_RUNTIME)" && printf present),present)
# Public releases use curl without requiring GitHub tooling. The authenticated
# gh fallback keeps maintainer testing usable while the repository is internal.
$(RUNTIME_DOWNLOAD):
mkdir -p "$(@D)"
@printf 'Downloading runtime release asset: %s\n' "$(notdir $@)"
@curl --fail --location --silent --show-error "$(RUNTIME_URL)" --output "$@.tmp" || { \
printf 'Anonymous release download unavailable; trying authenticated gh.\n'; \
gh release download "$(RUNTIME_RELEASE)" --repo "$(RUNTIME_REPOSITORY)" \
--pattern "$(notdir $@)" --output "$@.tmp" --clobber; \
}
mv "$@.tmp" "$@"
$(RUNTIME_CHECKSUM):
mkdir -p "$(@D)"
@printf 'Downloading runtime release asset: %s\n' "$(notdir $@)"
@curl --fail --location --silent --show-error "$(RUNTIME_URL).sha256" --output "$@.tmp" || { \
printf 'Anonymous release download unavailable; trying authenticated gh.\n'; \
gh release download "$(RUNTIME_RELEASE)" --repo "$(RUNTIME_REPOSITORY)" \
--pattern "$(notdir $@)" --output "$@.tmp" --clobber; \
}
mv "$@.tmp" "$@"
$(DEFAULT_RUNTIME): $(RUNTIME_DOWNLOAD) $(RUNTIME_CHECKSUM)
@test ! -e "$(RUNTIME_DIST)" || { \
printf 'runtime distribution is incomplete: %s\n' "$(RUNTIME_DIST)" >&2; exit 2; \
}
@cd "$(dir $(RUNTIME_DOWNLOAD))" && sha256sum --check "$(notdir $(RUNTIME_CHECKSUM))"
@mkdir -p "$(dir $(RUNTIME_DIST))"
@staging="$$(mktemp -d --tmpdir="$(dir $(RUNTIME_DIST))" .runtime.XXXXXX)"; \
trap 'rm -rf -- "$$staging"' EXIT; \
tar --extract --gzip --file "$(RUNTIME_DOWNLOAD)" --directory "$$staging"; \
test -f "$$staging/lib/libarm-yolo26n.so.0"; \
mv "$$staging" "$(RUNTIME_DIST)"
endif
# One converter invocation publishes these two model artifacts together.
$(MODEL)/manifest.json $(MODEL)/model.bin &: $(CHECKPOINT)
PYTHONPATH="$(ROOT)/src" "$(PYTHON)" -m converter.export_yolo26n \
--checkpoint "$(CHECKPOINT)" --resolution "$(RESOLUTION)" --output-dir "$(MODEL)"
# Build missing models whose default directory names identify their resolution.
$(DEFAULT_MODEL_ROOT)/yolo26n-%/manifest.json $(DEFAULT_MODEL_ROOT)/yolo26n-%/model.bin &: $(CHECKPOINT)
PYTHONPATH="$(ROOT)/src" "$(PYTHON)" -m converter.export_yolo26n \
--checkpoint "$(CHECKPOINT)" --resolution "$*" --output-dir "$(@D)"
$(DEFAULT_VIDEO):
mkdir -p "$(@D)"
curl --fail --location --show-error "$(VIDEO_URL)" --output "$@.tmp"
echo "$(VIDEO_SHA256) $@.tmp" | sha256sum --check
mv "$@.tmp" "$@"
example-video: examples $(VIDEO)
"$(EXAMPLES_PYTHON)" -m examples.video.detect \
--runtime "$(RUNTIME)" --model "$(MODEL)" --video "$(VIDEO)" \
--output "$(VIDEO_OUTPUT)" --frames 200
"$(EXAMPLES_PYTHON)" -m examples.video.render \
--video "$(VIDEO)" --detections "$(VIDEO_OUTPUT)" --output "$(VIDEO_RENDERED)"
@printf 'Video example complete:\n detections: %s\n rendered video: %s\n' \
"$(VIDEO_OUTPUT)" "$(VIDEO_RENDERED)"
example-camera: examples
"$(EXAMPLES_PYTHON)" -m $(CAMERA_EXAMPLE) --runtime "$(RUNTIME)" \
--model "$(MODEL)" --camera "$(CAMERA)"$(if $(CAMERA_PREVIEW), --preview "$(CAMERA_PREVIEW)") $(CAMERA_ARGS)
# Ultralytics benchmark
# Recreate the benchmark environment only when one of its installed inputs
# changes. pyvenv.cfg is the completion artifact created by Python itself.
$(ULTRALYTICS_VENV)/pyvenv.cfg: benchmarks/ultralytics/patch.sh \
benchmarks/ultralytics/requirements.txt requirements-build.txt \
pyproject.toml VERSION \
benchmarks/ultralytics/arm_yolo26n_pi5_backend.py \
benchmarks/ultralytics/patches/ultralytics-8.4.108-arm-yolo26-pi5.patch \
$(wildcard src/runtime/api/python/arm_yolo26n/*.py)
./benchmarks/ultralytics/patch.sh "$(ULTRALYTICS_VENV)"
$(DATASET_CONFIG): benchmarks/ultralytics/download_dataset.sh
@printf 'Downloading coco-128-by-sa dataset revision %s\n' "$(DATASET_REVISION)"
./benchmarks/ultralytics/download_dataset.sh \
"$(DATASET_REVISION)" "$(DATASET_DIR)"
.PHONY: ultralytics-benchmark
ultralytics-benchmark: $(RUNTIME) $(MODEL)/manifest.json $(MODEL)/model.bin \
$(ULTRALYTICS_VENV)/pyvenv.cfg $(DATA_REQUIREMENT)
"$(PYTHON)" -m benchmarks.ultralytics.benchmark \
--runtime "$(RUNTIME)" \
--model "$(MODEL)" \
--data "$(DATA)" \
--venv "$(ULTRALYTICS_VENV)" \
--output-dir "$(BENCHMARK_OUTPUT)" \
--revision "$(SOURCE_REVISION)" \
-- $(YOLO_ARGS)
# Development checks
.PHONY: _dev lint test-unit test-converter test-end-to-end coverage
_dev: $(DEV_VENV)/pyvenv.cfg
# pyvenv.cfg is created by venv itself, so it can mark a completed environment
# without adding a repository-specific stamp file. A newer lock rebuilds it.
$(DEV_VENV)/pyvenv.cfg: requirements-dev.txt
@cmake -E remove_directory "$(DEV_VENV)"
python3 -m venv "$(DEV_VENV)"
PIP_CONFIG_FILE=/dev/null "$(DEV_VENV)/bin/python" -m pip install --require-hashes \
--requirement requirements-dev.txt
# Development targets use the locked repository environment by default. A
# command-line PYTHON still takes precedence for CI or another managed setup.
lint test-unit test-converter test-end-to-end coverage: PYTHON := $(DEV_VENV)/bin/python
lint: _dev
@# clang-format verifies the C and C++ source formatting.
@find src/runtime -type f \
\( -name '*.c' -o -name '*.cpp' -o -name '*.h' -o -name '*.inc' \) \
-exec "$(CLANG_FORMAT)" --dry-run --Werror {} +
@# Ruff's formatter verifies the Python source formatting.
"$(PYTHON)" -m ruff format --check src/converter src/runtime/api/python/arm_yolo26n \
benchmarks examples tests
@# Ruff's linter detects Python correctness, style, and import problems.
"$(PYTHON)" -m ruff check src/converter src/runtime/api/python/arm_yolo26n \
benchmarks examples tests
@# Import Linter enforces the converter's declared dependency direction.
PYTHONPATH="$(ROOT)/src" "$(PYTHON)" -c \
'from importlinter.cli import lint_imports; raise SystemExit(lint_imports(no_cache=True))'
@# Mypy checks consistency between the declared Python types and their uses.
"$(PYTHON)" -m mypy
@# Vulture reports Python functions, classes, and variables that are never used.
"$(PYTHON)" -m vulture
@# Bash parses every benchmark shell script without executing it.
@find benchmarks/ultralytics -maxdepth 1 -type f -name '*.sh' -exec bash -n {} +
@# Python's JSON parser verifies the checked-in JSON documents.
@shopt -s nullglob; for document in src/converter/*.json benchmarks/reporting/*.json; do \
"$(PYTHON)" -m json.tool "$$document" >/dev/null; \
done
@# Git detects whitespace errors introduced by the current change.
git diff --check
test-unit: _dev
PYTHONPATH="$(TEST_PYTHONPATH)" "$(PYTHON)" -m pytest tests/unit
test-converter: _dev $(CHECKPOINT)
PYTHONPATH="$(TEST_PYTHONPATH)" YOLO26N_CHECKPOINT="$(CHECKPOINT)" \
"$(PYTHON)" -m pytest tests/converter
test-end-to-end: _dev $(MODEL_ARTIFACTS) test-converter runtime
YOLO26N_RUNTIME_API_TEST_MODEL="$(NATIVE_API_TEST_MODEL)" \
ctest --test-dir "$(RUNTIME_BUILD)" --output-on-failure \
--tests-regex '^yolo26n_runtime_api_behavior$$'
PYTHONPATH="$(TEST_PYTHONPATH)" \
YOLO26N_E2E_RUNTIME="$(RUNTIME)" YOLO26N_E2E_MODELS="$(MODELS)" \
"$(PYTHON)" -m pytest tests/end_to_end
coverage: _dev $(CHECKPOINT) $(MODEL_ARTIFACTS)
@# Build the runtime with coverage instrumentation and discard stale counters.
cmake -S src/runtime -B "$(COVERAGE_RUNTIME_BUILD)" \
-DCMAKE_BUILD_TYPE=Debug -DYOLO26N_ENABLE_COVERAGE=ON
cmake --build "$(COVERAGE_RUNTIME_BUILD)" \
--target arm_yolo26n yolo26n_runtime_api_test --parallel
find "$(COVERAGE_RUNTIME_BUILD)/CMakeFiles/arm_yolo26n.dir" -name '*.gcda' -delete
YOLO26N_RUNTIME_API_TEST_MODEL="$(NATIVE_API_TEST_MODEL)" \
ctest --test-dir "$(COVERAGE_RUNTIME_BUILD)" --output-on-failure
@# Run the public Python behavior suites against the instrumented runtime.
mkdir -p "$(dir $(PYTHON_COVERAGE_REPORT))" "$(dir $(NATIVE_COVERAGE_REPORT))"
"$(PYTHON)" -m coverage erase
PYTHONPATH="$(TEST_PYTHONPATH)" \
YOLO26N_CHECKPOINT="$(CHECKPOINT)" \
YOLO26N_E2E_RUNTIME="$(COVERAGE_RUNTIME)" \
YOLO26N_E2E_MODELS="$(MODELS)" \
"$(PYTHON)" -m coverage run -m pytest tests/unit tests/converter tests/end_to_end
@# Write one machine-readable report for each implementation language.
"$(PYTHON)" -m coverage combine
"$(PYTHON)" -m coverage report
"$(PYTHON)" -m coverage xml -o "$(PYTHON_COVERAGE_REPORT)"
"$(PYTHON)" -m gcovr --root "$(ROOT)" --filter "$(ROOT)/src/runtime/" \
--exclude-unreachable-branches --exclude-throw-branches \
"$(COVERAGE_RUNTIME_BUILD)/CMakeFiles/arm_yolo26n.dir" \
--txt - --cobertura "$(NATIVE_COVERAGE_REPORT)"