diff --git a/Makefile b/Makefile index 6f3e624..5db53ae 100644 --- a/Makefile +++ b/Makefile @@ -1,47 +1,69 @@ # Makefile for phbar # # Usage: make +# +# NOTE: Never invoke this Makefile with `sudo` (e.g. `sudo make install`). +# Running `swift build` as root corrupts `.build/` with root-owned files that +# break subsequent non-root builds (you get "invalid access to …/DerivedSources"). +# The `install`/`uninstall` targets elevate *only* the file copy, and only when +# the destination is not user-writable, so `sudo` is never needed from the user. -BIN_DIR := /usr/local/bin +# Install directory. Override with: make install BIN_DIR=/opt/homebrew/bin +BIN_DIR ?= /usr/local/bin BUILD_DIR := .build/release BINARIES := phbar -.PHONY: all build install clean test run +# Guard: any target that runs Swift must not run under sudo, otherwise it would +# sprinkle root-owned files across .build/. It fails fast with a helpful message +# instead of corrupting the build directory. Recursively-expanded (`=`) so that +# the `$@` automatic variable resolves per-target at recipe time. +guard_not_root = @if [ "$$(id -u)" = "0" ] && [ -n "$$SUDO_USER" ]; then \ + echo "❌ 'make $@' must not run under sudo (it would corrupt .build/)." >&2; \ + echo " Run 'make install' without sudo; it elevates only the copy step." >&2; \ + exit 1; \ +fi + +.PHONY: all build install clean test run uninstall reinstall help all: build # Build all targets in release mode build: + $(guard_not_root) swift build -c release -# Build and install binaries to /usr/local/bin +# Build and install binaries. Elevates only the copy when BIN_DIR isn't writable. install: build @mkdir -p $(BIN_DIR) - @for bin in $(BINARIES); do \ - cp -f $(BUILD_DIR)/$$bin $(BIN_DIR)/; \ - done + @if [ -w "$(BIN_DIR)" ]; then CP="cp -f"; else CP="sudo cp -f"; fi; \ + for bin in $(BINARIES); do \ + $$CP $(BUILD_DIR)/$$bin $(BIN_DIR)/; \ + done @echo "✅ Installed $(BINARIES) to $(BIN_DIR)" # Clean build artifacts clean: + $(guard_not_root) swift package clean # Run tests test: + $(guard_not_root) swift test # Quick test: build, install, and verify run: install @echo "Verifying installation..." @for bin in $(BINARIES); do \ - which $$bin > /dev/null && echo "✓ $$bin: $(shell which $$bin)" || echo "✗ $$bin: not found"; \ + which $$bin > /dev/null && echo "✓ $$bin: $$($$bin --version 2>/dev/null | head -1)" || echo "✗ $$bin: not found"; \ done -# Uninstall binaries from /usr/local/bin +# Uninstall binaries. Elevates only the remove when BIN_DIR isn't writable. uninstall: - @for bin in $(BINARIES); do \ - rm -f $(BIN_DIR)/$$bin; \ - done + @if [ -w "$(BIN_DIR)" ]; then RM="rm -f"; else RM="sudo rm -f"; fi; \ + for bin in $(BINARIES); do \ + $$RM $(BIN_DIR)/$$bin; \ + done @echo "✅ Uninstalled $(BINARIES) from $(BIN_DIR)" # Rebuild and reinstall (clean + install) @@ -51,9 +73,12 @@ reinstall: clean install help: @echo "Available targets:" @echo " make build - Build in release mode" - @echo " make install - Build and install to /usr/local/bin" + @echo " make install - Build and install to $(BIN_DIR)" @echo " make clean - Clean build artifacts" @echo " make test - Run tests" @echo " make run - Build, install, and verify" - @echo " make uninstall - Remove binaries from /usr/local/bin" + @echo " make uninstall - Remove binaries from $(BIN_DIR)" @echo " make reinstall - Clean and reinstall" + @echo "" + @echo "Override the install location with BIN_DIR, e.g.:" + @echo " make install BIN_DIR=$(HOME)/.local/bin"