PROJECT_NAME := ffdash
VERSION := $(shell grep '^version' Cargo.toml | head -1 | cut -d'"' -f2)
TARGET_DIR := target
RELEASE_DIR := $(TARGET_DIR)/release
DEBUG_DIR := $(TARGET_DIR)/debug
PREFIX ?= /usr/local
BINDIR := $(PREFIX)/bin
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
OS := linux
PKG_MANAGER := apt
INSTALL_CMD := sudo apt-get install -y
FFMPEG_PKG := ffmpeg
else ifeq ($(UNAME_S),Darwin)
OS := macos
PKG_MANAGER := brew
INSTALL_CMD := brew install
FFMPEG_PKG := ffmpeg
else
$(error Unsupported operating system: $(UNAME_S))
endif
CARGO := cargo
RUSTC := rustc
CARGO_BUILD_FLAGS :=
CARGO_RELEASE_FLAGS := --release
USE_GIT_CLI ?= 0
ifeq ($(USE_GIT_CLI),1)
export CARGO_NET_GIT_FETCH_WITH_CLI := true
endif
COLOR_RESET := \033[0m
COLOR_BOLD := \033[1m
COLOR_GREEN := \033[32m
COLOR_YELLOW := \033[33m
COLOR_BLUE := \033[34m
.PHONY: all build release debug install uninstall clean test check fmt clippy help deps install-rust install-ffmpeg docker-build docker-build-dev docker-run docker-dev-shell docker-push check-ui validate-profiles remote-smoke remote-test
all: release
help:
@echo "$(COLOR_BOLD)ffdash VP9 Encoder - Build System$(COLOR_RESET)"
@echo ""
@echo "$(COLOR_BOLD)Platform:$(COLOR_RESET) $(OS) ($(UNAME_S))"
@echo "$(COLOR_BOLD)Version:$(COLOR_RESET) $(VERSION)"
@echo ""
@echo "$(COLOR_BOLD)Available targets:$(COLOR_RESET)"
@echo " $(COLOR_GREEN)make build$(COLOR_RESET) - Build debug version"
@echo " $(COLOR_GREEN)make release$(COLOR_RESET) - Build release version (optimized)"
@echo " $(COLOR_GREEN)make debug$(COLOR_RESET) - Build debug version (alias)"
@echo " $(COLOR_GREEN)make install$(COLOR_RESET) - Install to $(BINDIR)"
@echo " $(COLOR_GREEN)make uninstall$(COLOR_RESET) - Uninstall from $(BINDIR)"
@echo " $(COLOR_GREEN)make clean$(COLOR_RESET) - Remove build artifacts"
@echo " $(COLOR_GREEN)make test$(COLOR_RESET) - Run tests"
@echo " $(COLOR_GREEN)make check$(COLOR_RESET) - Check code without building"
@echo " $(COLOR_GREEN)make check-ui$(COLOR_RESET) - Check UI interaction coverage"
@echo " $(COLOR_GREEN)make fmt$(COLOR_RESET) - Format code with rustfmt"
@echo " $(COLOR_GREEN)make clippy$(COLOR_RESET) - Lint code with clippy"
@echo " $(COLOR_GREEN)make deps$(COLOR_RESET) - Install all dependencies"
@echo " $(COLOR_GREEN)make install-rust$(COLOR_RESET) - Install Rust toolchain"
@echo " $(COLOR_GREEN)make install-ffmpeg$(COLOR_RESET) - Install FFmpeg"
@echo " $(COLOR_GREEN)make docker-build$(COLOR_RESET) - Build runtime Docker image (ffmpeg + ffdash)"
@echo " $(COLOR_GREEN)make docker-build-dev$(COLOR_RESET) - Build dev Docker image (toolchain inside)"
@echo " $(COLOR_GREEN)make docker-run$(COLOR_RESET) - Run runtime Docker container"
@echo " $(COLOR_GREEN)make docker-dev-shell$(COLOR_RESET) - Shell into dev container via docker compose"
@echo " $(COLOR_GREEN)make help$(COLOR_RESET) - Show this help message"
@echo " $(COLOR_GREEN)make validate-profiles$(COLOR_RESET) - Validate profiles before remote deployment"
@echo " $(COLOR_GREEN)make remote-smoke$(COLOR_RESET) - Run smoke test on remote hardware host"
@echo " $(COLOR_GREEN)make remote-test$(COLOR_RESET) - Deploy + test with real clips on remote host"
@echo ""
@echo "$(COLOR_BOLD)Examples:$(COLOR_RESET)"
@echo " make deps @echo " make release @echo " sudo make install @echo " make release USE_GIT_CLI=1 @echo " make validate-profiles PROFILES=av1-qsv,av1-vaapi @echo " make remote-smoke HOST=hw-container @echo " make remote-test HOST=hw-container CLIPS=clip1.mp4 @echo ""
@echo "$(COLOR_BOLD)Private Git Dependencies:$(COLOR_RESET)"
@echo " If you have private git dependencies, use:"
@echo " $(COLOR_YELLOW)USE_GIT_CLI=1 make release$(COLOR_RESET)"
@echo ""
build: debug
debug:
@echo "$(COLOR_BLUE)Building $(PROJECT_NAME) (debug)...$(COLOR_RESET)"
$(CARGO) build $(CARGO_BUILD_FLAGS)
@echo "$(COLOR_GREEN)✓ Debug build complete: $(DEBUG_DIR)/$(PROJECT_NAME)$(COLOR_RESET)"
release:
@echo "$(COLOR_BLUE)Building $(PROJECT_NAME) (release)...$(COLOR_RESET)"
$(CARGO) build $(CARGO_RELEASE_FLAGS)
@echo "$(COLOR_GREEN)✓ Release build complete: $(RELEASE_DIR)/$(PROJECT_NAME)$(COLOR_RESET)"
install:
@if [ ! -f "$(RELEASE_DIR)/$(PROJECT_NAME)" ]; then \
if command -v cargo >/dev/null 2>&1; then \
echo "$(COLOR_BLUE)Binary not found, building first...$(COLOR_RESET)"; \
$(MAKE) release; \
else \
echo "$(COLOR_RED)Error: $(RELEASE_DIR)/$(PROJECT_NAME) not found$(COLOR_RESET)"; \
echo "$(COLOR_YELLOW)Run 'make release' first (without sudo), then 'sudo make install'$(COLOR_RESET)"; \
exit 1; \
fi \
fi
@echo "$(COLOR_BLUE)Installing $(PROJECT_NAME) to $(BINDIR)...$(COLOR_RESET)"
@mkdir -p $(BINDIR)
install -m 755 $(RELEASE_DIR)/$(PROJECT_NAME) $(BINDIR)/$(PROJECT_NAME)
@echo "$(COLOR_GREEN)✓ Installed to $(BINDIR)/$(PROJECT_NAME)$(COLOR_RESET)"
@echo ""
@echo "$(COLOR_BOLD)Installation complete!$(COLOR_RESET)"
@echo "Run '$(PROJECT_NAME) --help' to get started."
uninstall:
@echo "$(COLOR_BLUE)Uninstalling $(PROJECT_NAME)...$(COLOR_RESET)"
rm -f $(BINDIR)/$(PROJECT_NAME)
@echo "$(COLOR_GREEN)✓ Uninstalled$(COLOR_RESET)"
clean:
@echo "$(COLOR_BLUE)Cleaning build artifacts...$(COLOR_RESET)"
$(CARGO) clean
@echo "$(COLOR_GREEN)✓ Clean complete$(COLOR_RESET)"
test:
@echo "$(COLOR_BLUE)Running tests...$(COLOR_RESET)"
$(CARGO) test
check:
@echo "$(COLOR_BLUE)Checking code...$(COLOR_RESET)"
$(CARGO) check
check-ui:
@echo "$(COLOR_BLUE)Checking UI interaction coverage...$(COLOR_RESET)"
@./scripts/check-ui-logic.sh
fmt:
@echo "$(COLOR_BLUE)Formatting code...$(COLOR_RESET)"
$(CARGO) fmt
@echo "$(COLOR_GREEN)✓ Code formatted$(COLOR_RESET)"
clippy:
@echo "$(COLOR_BLUE)Running clippy...$(COLOR_RESET)"
$(CARGO) clippy -- -D warnings
deps: install-rust install-ffmpeg
@echo "$(COLOR_GREEN)✓ All dependencies installed$(COLOR_RESET)"
install-rust:
@echo "$(COLOR_BLUE)Checking Rust installation...$(COLOR_RESET)"
@if command -v $(CARGO) >/dev/null 2>&1; then \
echo "$(COLOR_GREEN)✓ Rust already installed ($(shell $(RUSTC) --version))$(COLOR_RESET)"; \
else \
echo "$(COLOR_YELLOW)Installing Rust toolchain...$(COLOR_RESET)"; \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y; \
echo "$(COLOR_GREEN)✓ Rust installed$(COLOR_RESET)"; \
echo "$(COLOR_YELLOW)Please run: source $$HOME/.cargo/env$(COLOR_RESET)"; \
fi
install-ffmpeg:
@echo "$(COLOR_BLUE)Checking FFmpeg installation...$(COLOR_RESET)"
@if command -v ffmpeg >/dev/null 2>&1; then \
echo "$(COLOR_GREEN)✓ FFmpeg already installed ($(shell ffmpeg -version | head -1))$(COLOR_RESET)"; \
else \
echo "$(COLOR_YELLOW)Installing FFmpeg...$(COLOR_RESET)"; \
if [ "$(OS)" = "linux" ]; then \
sudo apt-get update && $(INSTALL_CMD) $(FFMPEG_PKG); \
else \
$(INSTALL_CMD) $(FFMPEG_PKG); \
fi; \
echo "$(COLOR_GREEN)✓ FFmpeg installed$(COLOR_RESET)"; \
fi
version:
@echo "$(PROJECT_NAME) version $(VERSION)"
@echo "OS: $(OS)"
@echo "Rust: $(shell $(RUSTC) --version 2>/dev/null || echo 'not installed')"
@echo "FFmpeg: $(shell ffmpeg -version 2>/dev/null | head -1 || echo 'not installed')"
dev: debug
@echo "$(COLOR_BLUE)Running in development mode...$(COLOR_RESET)"
$(DEBUG_DIR)/$(PROJECT_NAME)
watch:
@echo "$(COLOR_BLUE)Watching for changes...$(COLOR_RESET)"
$(CARGO) watch -x build
bench:
@echo "$(COLOR_BLUE)Running benchmarks...$(COLOR_RESET)"
$(CARGO) bench
docs:
@echo "$(COLOR_BLUE)Building documentation...$(COLOR_RESET)"
$(CARGO) doc --no-deps --open
update:
@echo "$(COLOR_BLUE)Updating dependencies...$(COLOR_RESET)"
$(CARGO) update
@echo "$(COLOR_GREEN)✓ Dependencies updated$(COLOR_RESET)"
DOCKER_IMAGE_NAME := ffdash
DOCKER_TAG := latest
DOCKER_IMAGE_NAME := ffdash
DOCKER_RUNTIME_TAG := latest
DOCKER_DEV_TAG := dev
DOCKER_FILE := docker/Dockerfile
docker-build: docker-build-runtime
docker-build-runtime:
@echo "$(COLOR_BLUE)Building Docker runtime image...$(COLOR_RESET)"
$(MAKE) release
docker build -f $(DOCKER_FILE) --target runtime -t $(DOCKER_IMAGE_NAME):$(DOCKER_RUNTIME_TAG) .
@echo "$(COLOR_GREEN)✓ Docker runtime image built: $(DOCKER_IMAGE_NAME):$(DOCKER_RUNTIME_TAG)$(COLOR_RESET)"
docker-build-dev:
@echo "$(COLOR_BLUE)Building Docker dev image...$(COLOR_RESET)"
docker build -f $(DOCKER_FILE) --target dev -t $(DOCKER_IMAGE_NAME):$(DOCKER_DEV_TAG) .
@echo "$(COLOR_GREEN)✓ Docker dev image built: $(DOCKER_IMAGE_NAME):$(DOCKER_DEV_TAG)$(COLOR_RESET)"
docker-run:
@echo "$(COLOR_BLUE)Running Docker runtime container...$(COLOR_RESET)"
docker run -it --rm \
--device /dev/dri:/dev/dri \
-v /path/to/videos:/video \
$(DOCKER_IMAGE_NAME):$(DOCKER_RUNTIME_TAG)
docker-dev-shell:
@echo "$(COLOR_BLUE)Starting Docker dev shell (docker compose)...$(COLOR_RESET)"
docker compose -f docker/docker-compose.dev.yml run --rm ffdash-dev
docker-push:
@echo "$(COLOR_BLUE)Pushing Docker image to registry...$(COLOR_RESET)"
@echo "$(COLOR_YELLOW)Note: GitHub Actions handles this automatically$(COLOR_RESET)"
@echo "For manual push, tag and push to your registry:"
@echo " docker tag $(DOCKER_IMAGE_NAME):$(DOCKER_RUNTIME_TAG) ghcr.io/YOUR_USERNAME/$(DOCKER_IMAGE_NAME):$(DOCKER_RUNTIME_TAG)"
@echo " docker push ghcr.io/YOUR_USERNAME/$(DOCKER_IMAGE_NAME):$(DOCKER_RUNTIME_TAG)"
@echo " docker tag $(DOCKER_IMAGE_NAME):$(DOCKER_DEV_TAG) ghcr.io/YOUR_USERNAME/$(DOCKER_IMAGE_NAME):$(DOCKER_DEV_TAG)"
@echo " docker push ghcr.io/YOUR_USERNAME/$(DOCKER_IMAGE_NAME):$(DOCKER_DEV_TAG)"
validate-profiles:
@if [ -z "$(PROFILES)" ]; then \
echo "$(COLOR_YELLOW)Usage: make validate-profiles PROFILES=profile1,profile2$(COLOR_RESET)"; \
exit 1; \
fi
@echo "$(COLOR_BLUE)Validating profiles: $(PROFILES)...$(COLOR_RESET)"
$(CARGO) run --bin ffdash --release --features dev-tools -- validate-profile $$(echo $(PROFILES) | tr ',' ' ')
remote-smoke:
@if [ -z "$(HOST)" ]; then \
echo "$(COLOR_YELLOW)Usage: make remote-smoke HOST=<hostname> [FORMAT=json|pretty]$(COLOR_RESET)"; \
exit 1; \
fi
@echo "$(COLOR_BLUE)Running remote smoke test on host: $(HOST)...$(COLOR_RESET)"
./scripts/remote-smoke.sh --host $(HOST) --format $(or $(FORMAT),json)
remote-test:
@if [ -z "$(HOST)" ]; then \
echo "$(COLOR_YELLOW)Usage: make remote-test HOST=<hostname> [CLIPS=clip1.mp4,clip2.mkv]$(COLOR_RESET)"; \
exit 1; \
fi
@echo "$(COLOR_BLUE)Running remote test on host: $(HOST)...$(COLOR_RESET)"
@if [ -n "$(CLIPS)" ]; then \
CLIPS=$(CLIPS) ./scripts/remote-smoke.sh --host $(HOST) --format pretty; \
else \
./scripts/remote-smoke.sh --host $(HOST) --format pretty; \
fi