182 lines
8.3 KiB
Makefile
182 lines
8.3 KiB
Makefile
SHELL := /usr/bin/env bash
|
|
.SHELLFLAGS := -eu -o pipefail -c
|
|
.DEFAULT_GOAL := help
|
|
.DELETE_ON_ERROR:
|
|
|
|
PROJECT := latchd
|
|
BINARY := latchd
|
|
PREFIX ?= /usr
|
|
DESTDIR ?=
|
|
BINDIR := $(DESTDIR)$(PREFIX)/bin
|
|
CONFDIR := $(DESTDIR)/etc/$(PROJECT)
|
|
SYSTEMD_DIR := $(DESTDIR)/usr/lib/systemd/system
|
|
BUILD_DIR ?= build
|
|
DIST_DIR ?= dist
|
|
CARGO_TARGET_DIR ?= target
|
|
CARGO ?= cargo
|
|
GO ?= go
|
|
TARGET ?=
|
|
TAG ?=
|
|
|
|
VERSION ?= $(shell sed -n 's/^version = "\([^"]*\)"/\1/p' Cargo.toml | head -1)
|
|
GIT_COMMIT ?= $(shell git rev-parse --short=12 HEAD 2>/dev/null || printf unknown)
|
|
SOURCE_DATE_EPOCH ?= $(shell git log -1 --format=%ct 2>/dev/null || date +%s)
|
|
TARGET_TRIPLE ?= $(shell rustc -vV 2>/dev/null | sed -n 's/^host: //p' || printf unknown)
|
|
|
|
TARGET_ARGS := $(if $(TARGET),--target $(TARGET),)
|
|
RELEASE_DIR := $(CARGO_TARGET_DIR)/$(if $(TARGET),$(TARGET)/)release
|
|
PACKAGE_NAME := $(PROJECT)-$(VERSION)-$(if $(TARGET),$(TARGET),$(TARGET_TRIPLE))
|
|
PACKAGE_FILE := $(DIST_DIR)/$(PACKAGE_NAME).tar.gz
|
|
PACKAGE_STAGE := $(DIST_DIR)/stage/$(PACKAGE_NAME)
|
|
CARGO_ENV := LATCHD_VERSION=$(VERSION) LATCHD_COMMIT=$(GIT_COMMIT) LATCHD_BUILD_TIME=$(SOURCE_DATE_EPOCH) SOURCE_DATE_EPOCH=$(SOURCE_DATE_EPOCH)
|
|
|
|
COLOR ?= 1
|
|
ifeq ($(COLOR),1)
|
|
RESET := \033[0m
|
|
DIM := \033[2m
|
|
CYAN := \033[36m
|
|
GREEN := \033[32m
|
|
YELLOW := \033[33m
|
|
RED := \033[31m
|
|
else
|
|
RESET :=
|
|
DIM :=
|
|
CYAN :=
|
|
GREEN :=
|
|
YELLOW :=
|
|
RED :=
|
|
endif
|
|
|
|
.PHONY: all deps build release-build test check fmt lint doc audit docker-test \
|
|
integration-test pam-fixture-test system-e2e system-e2e-strict \
|
|
package release release-check version install uninstall clean run-preview \
|
|
legacy-build legacy-check enable update help
|
|
|
|
all: release-build ## Build the optimized release binary
|
|
|
|
version: ## Print the package, commit, and reproducibility metadata
|
|
@printf 'version=%s\ncommit=%s\nepoch=%s\ntarget=%s\n' \
|
|
'$(VERSION)' '$(GIT_COMMIT)' '$(SOURCE_DATE_EPOCH)' '$(if $(TARGET),$(TARGET),$(TARGET_TRIPLE))'
|
|
|
|
deps: ## Check native dependencies needed for builds and tests
|
|
@command -v $(CARGO) >/dev/null || { echo 'cargo (Rust 1.88+) is required'; exit 1; }
|
|
@command -v rustfmt >/dev/null || { echo 'rustfmt is required'; exit 1; }
|
|
@command -v pkg-config >/dev/null || { echo 'pkg-config is required'; exit 1; }
|
|
@pkg-config --exists pam || { echo 'libpam development files are required'; exit 1; }
|
|
@printf '$(GREEN)dependencies ok$(RESET)\n'
|
|
|
|
build: release-check ## Build and copy the optimized binary to build/
|
|
@mkdir -p '$(BUILD_DIR)'
|
|
@printf '$(CYAN)building$(RESET) %s %s\n' '$(VERSION)' '$(if $(TARGET),$(TARGET),$(TARGET_TRIPLE))'
|
|
@env $(CARGO_ENV) $(CARGO) build --release --locked $(TARGET_ARGS)
|
|
@install -m755 '$(RELEASE_DIR)/$(BINARY)' '$(BUILD_DIR)/$(BINARY)'
|
|
@'$(BUILD_DIR)/$(BINARY)' --version
|
|
|
|
release-build: build ## Alias for the optimized release build
|
|
|
|
fmt: ## Check Rust formatting
|
|
@$(CARGO) fmt --all -- --check
|
|
|
|
test: ## Run all Rust tests
|
|
@$(CARGO) test --locked --workspace --all-targets
|
|
|
|
lint: ## Run Clippy with warnings denied
|
|
@$(CARGO) clippy --locked --workspace --all-targets --all-features -- -D warnings
|
|
|
|
doc: ## Build rustdoc with warnings denied
|
|
@RUSTDOCFLAGS='-D warnings' $(CARGO) doc --locked --workspace --no-deps
|
|
|
|
audit: ## Run cargo-audit when installed
|
|
@command -v cargo-audit >/dev/null || { echo 'cargo-audit is required; install with cargo install cargo-audit --locked'; exit 1; }
|
|
@cargo audit
|
|
|
|
check: fmt test lint doc ## Run the complete local verification gate
|
|
|
|
docker-test: ## Run the pinned Docker verification suite
|
|
@./scripts/test-docker.sh
|
|
|
|
integration-test: build ## Exercise safe, non-authenticating CLI paths
|
|
@./scripts/test-cli.sh '$(BUILD_DIR)/$(BINARY)'
|
|
|
|
pam-fixture-test: ## Run the disposable PAM fault-injection tests when privileged
|
|
@./scripts/test-pam-fixture.sh
|
|
|
|
system-e2e: ## Probe PAM, VT, X11, Wayland, and logind without requiring privileges
|
|
@./scripts/test-system-e2e.sh '$(BUILD_DIR)/$(BINARY)'
|
|
|
|
system-e2e-strict: ## Require all real login-manager capabilities (prepared VM only)
|
|
@./scripts/test-system-e2e.sh --strict '$(BUILD_DIR)/$(BINARY)'
|
|
|
|
release-check: ## Validate a release version and optional tag
|
|
@[[ '$(VERSION)' =~ ^[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.-]+)?$$ ]] || { echo 'invalid VERSION: $(VERSION)'; exit 1; }
|
|
@cargo_version="$$(sed -n 's/^version = "\([^"]*\)"/\1/p' Cargo.toml | head -1)"; \
|
|
[[ "$$cargo_version" == '$(VERSION)' ]] || { echo "Cargo version $$cargo_version does not match $(VERSION)"; exit 1; }
|
|
@if [[ -n '$(TAG)' ]]; then \
|
|
tag_version="$$(printf %s '$(TAG)' | sed 's/^v//')"; \
|
|
[[ "$$tag_version" == '$(VERSION)' ]] || { echo "tag $(TAG) does not match $(VERSION)"; exit 1; }; \
|
|
fi
|
|
|
|
package: release-check build ## Create a deterministic install archive and checksum
|
|
@rm -rf '$(PACKAGE_STAGE)'
|
|
@mkdir -p '$(PACKAGE_STAGE)/usr/bin' '$(PACKAGE_STAGE)/usr/lib/systemd/system' \
|
|
'$(PACKAGE_STAGE)/etc/pam.d' '$(PACKAGE_STAGE)/etc/$(PROJECT)/wms' '$(PACKAGE_STAGE)/etc/$(PROJECT)/wayland'
|
|
@install -m755 '$(BUILD_DIR)/$(BINARY)' '$(PACKAGE_STAGE)/usr/bin/$(BINARY)'
|
|
@install -m644 extra/config.toml '$(PACKAGE_STAGE)/etc/$(PROJECT)/config.toml'
|
|
@install -m644 extra/latchd.pam '$(PACKAGE_STAGE)/etc/pam.d/$(PROJECT)'
|
|
@install -m644 extra/latchd.service '$(PACKAGE_STAGE)/usr/lib/systemd/system/$(PROJECT).service'
|
|
@install -m644 README.md LICENSE CHANGELOG.md '$(PACKAGE_STAGE)/'
|
|
@printf 'version=%s\ncommit=%s\nepoch=%s\ntarget=%s\n' \
|
|
'$(VERSION)' '$(GIT_COMMIT)' '$(SOURCE_DATE_EPOCH)' '$(if $(TARGET),$(TARGET),$(TARGET_TRIPLE))' \
|
|
> '$(PACKAGE_STAGE)/BUILD-METADATA'
|
|
@mkdir -p '$(DIST_DIR)'
|
|
@tar --sort=name --numeric-owner --owner=0 --group=0 --mtime='@$(SOURCE_DATE_EPOCH)' \
|
|
-C '$(DIST_DIR)/stage' -czf '$(PACKAGE_FILE)' '$(PACKAGE_NAME)'
|
|
@(cd '$(DIST_DIR)' && sha256sum '$(notdir $(PACKAGE_FILE))' > '$(notdir $(PACKAGE_FILE)).sha256')
|
|
@(cd '$(DIST_DIR)' && sha256sum --check '$(notdir $(PACKAGE_FILE)).sha256')
|
|
@checksum="$$(sha256sum '$(PACKAGE_FILE)' | cut -d' ' -f1)"; \
|
|
printf 'package=%s\nsha256=%s\nversion=%s\n' \
|
|
'$(PACKAGE_FILE)' "$$checksum" '$(VERSION)' > '$(DIST_DIR)/release.env'
|
|
@printf '$(GREEN)created$(RESET) %s\n' '$(PACKAGE_FILE)'
|
|
|
|
release: check audit integration-test system-e2e docker-test package ## Run all gates and produce release artifacts
|
|
|
|
install: build ## Install the binary, config, PAM policy, and systemd unit
|
|
@install -Dm755 '$(BUILD_DIR)/$(BINARY)' '$(BINDIR)/$(BINARY)'
|
|
@install -Dm644 extra/config.toml '$(CONFDIR)/config.toml'
|
|
@install -Dm644 extra/latchd.pam '$(DESTDIR)/etc/pam.d/$(PROJECT)'
|
|
@install -Dm644 extra/latchd.service '$(SYSTEMD_DIR)/$(PROJECT).service'
|
|
@install -d '$(CONFDIR)/wms' '$(CONFDIR)/wayland'
|
|
|
|
uninstall: ## Remove files installed by make install
|
|
@rm -f '$(BINDIR)/$(BINARY)' '$(DESTDIR)/etc/pam.d/$(PROJECT)' '$(SYSTEMD_DIR)/$(PROJECT).service'
|
|
@printf '$(YELLOW)configuration at %s was preserved; remove it manually if no longer needed$(RESET)\n' '$(CONFDIR)'
|
|
|
|
clean: ## Remove generated build, release, and Cargo artifacts
|
|
@rm -rf '$(BUILD_DIR)' '$(DIST_DIR)'
|
|
@$(CARGO) clean
|
|
|
|
run-preview: build ## Run the safe non-authenticating preview UI
|
|
@'$(BUILD_DIR)/$(BINARY)' --preview --no-log
|
|
|
|
legacy-build: ## Build the quarantined Go reference only when explicitly requested
|
|
@printf '$(YELLOW)legacy Go is reference-only and excluded from release builds$(RESET)\n'
|
|
@command -v $(GO) >/dev/null || { echo 'go is required for legacy-build'; exit 1; }
|
|
@mkdir -p '$(BUILD_DIR)'
|
|
@cd legacy/go && $(GO) build -trimpath -o '../../$(BUILD_DIR)/$(BINARY)-go' .
|
|
|
|
legacy-check: ## Format-check and test the quarantined Go reference
|
|
@command -v $(GO) >/dev/null || { echo 'go is required for legacy-check'; exit 1; }
|
|
@command -v gofmt >/dev/null || { echo 'gofmt is required for legacy-check'; exit 1; }
|
|
@test -z "$$(find legacy/go -type f -name '*.go' -print0 | xargs -0 gofmt -l)" || { echo 'legacy Go files need gofmt'; exit 1; }
|
|
@cd legacy/go && $(GO) test ./...
|
|
|
|
enable: ## Enable the systemd service after host validation
|
|
@sudo systemctl enable $(PROJECT).service
|
|
|
|
update: ## Rebuild and reinstall the current checkout without changing git state
|
|
@sudo $(MAKE) install
|
|
|
|
help: ## Show available targets
|
|
@printf '$(CYAN)$(PROJECT)$(RESET) $(VERSION) — production build targets\n\n'
|
|
@awk -F ':|##' '/^[a-zA-Z0-9_-]+:.*##/ { printf " %-18s %s\n", $$1, $$NF }' '$(MAKEFILE_LIST)' | sort
|