MAKEFLAGS += --warn-undefined-variables
SHELL := bash
.SHELLFLAGS := -eu -o pipefail -c
.DEFAULT_GOAL := all
.DELETE_ON_ERROR:
.SUFFIXES:
DEBUG ?= false
BINNAME := git-semver
UNAME_S := $(shell uname -s)
UNAME_M := $(shell uname -m)
LINUX_X86_64 := x86_64-unknown-linux-gnu
LINUX_AARCH64 := aarch64-unknown-linux-gnu
MACOS_X86_64 := x86_64-apple-darwin
MACOS_AARCH64 := aarch64-apple-darwin
WINDOWS_X86_64 := x86_64-pc-windows-msvc
WINDOWS_GNU := x86_64-pc-windows-gnu
FREEBSD_X86_64 := x86_64-unknown-freebsd
TARGETS := $(LINUX_X86_64) $(LINUX_AARCH64) $(MACOS_X86_64) $(MACOS_AARCH64) $(WINDOWS_X86_64) $(FREEBSD_X86_64)
ifeq ($(UNAME_S),Linux)
ifeq ($(UNAME_M),x86_64)
DEFAULT_TARGET := $(LINUX_X86_64)
else ifeq ($(UNAME_M),aarch64)
DEFAULT_TARGET := $(LINUX_AARCH64)
endif
else ifeq ($(UNAME_S),Darwin)
ifeq ($(UNAME_M),x86_64)
DEFAULT_TARGET := $(MACOS_X86_64)
else ifeq ($(UNAME_M),arm64)
DEFAULT_TARGET := $(MACOS_AARCH64)
endif
else ifeq ($(UNAME_S),FreeBSD)
DEFAULT_TARGET := $(FREEBSD_X86_64)
endif
TARGET ?= $(DEFAULT_TARGET)
ifeq (true, $(DEBUG))
BUILD_MODE := debug
CARGO_FLAGS :=
else
BUILD_MODE := release
CARGO_FLAGS := --release
endif
ifeq ($(findstring windows,$(TARGET)),windows)
BINARY_EXT := .exe
INSTALL_PATH ?= C:/Program Files/$(BINNAME)
else
BINARY_EXT :=
ifeq ($(UNAME_S),Darwin)
INSTALL_PATH ?= /usr/local/bin
else
INSTALL_PATH ?= /usr/local/bin
endif
endif
BINDIR := $(CURDIR)/target/$(TARGET)/$(BUILD_MODE)
DISTDIR := $(CURDIR)/dist
BINARY := $(BINNAME)$(BINARY_EXT)
DATE = $(shell date -u +'%Y-%m-%dT%H:%M:%SZ')
GIT_COMMIT = $(shell git rev-parse HEAD)
GIT_SHA = $(shell git rev-parse --short HEAD)
GIT_BRANCH = $(shell git rev-parse --abbrev-ref HEAD)
GIT_TAG = $(shell git describe --tags --abbrev=0 --exact-match 2>/dev/null)
ifdef VERSION
BINARY_VERSION = $(VERSION)
endif
BINARY_VERSION ?= ${GIT_TAG}
SRC := $(shell find . -type f -name '*.rs' -print) Cargo.toml Cargo.lock
.PHONY: all
all: build test
.PHONY: help
help:
@echo "Build targets for $(BINNAME)"
@echo
@grep -E '^[a-zA-Z_/-]+:.*?## .*$$' $(MAKEFILE_LIST) \
| sort \
| awk 'BEGIN {FS = ":.*?## "; printf "Usage:\n"}; {printf " %-15s %s\n", $$1, $$2}'
@echo
@echo "Examples:"
@echo " make build-linux-x86_64"
@echo " make build-all"
@echo " make package-all"
@echo " make TARGET=x86_64-pc-windows-msvc build"
.PHONY: clean
clean:
@cargo clean
@rm -rf $(DISTDIR)
.PHONY: changelog
changelog:
@git-cliff \
--config "cliff.toml" \
--output="CHANGELOG.md" \
--tag "$(BINARY_VERSION)"
.PHONY: patch-notes
patch-notes:
@git-cliff \
--config "cliff.toml" \
--unreleased \
--tag "$(BINARY_VERSION)" \
| tail -n+6
.PHONY: check
check:
@cargo check --all-targets
.PHONY: fmt
fmt:
@cargo fmt
.PHONY: lint
lint:
@cargo clippy --all-targets --all-features -- -D warnings
.PHONY: test
test:
@cargo test --target $(TARGET)
.PHONY: quality
quality: check fmt lint test
.PHONY: build
build: $(BINDIR)/$(BINARY)
$(BINDIR)/$(BINARY): $(SRC)
@echo "Building $(BINNAME) for $(TARGET) ($(BUILD_MODE))"
@cargo build --target $(TARGET) $(CARGO_FLAGS)
.PHONY: build-linux-x86_64
build-linux-x86_64:
@$(MAKE) TARGET=$(LINUX_X86_64) build
.PHONY: build-linux-aarch64
build-linux-aarch64:
@$(MAKE) TARGET=$(LINUX_AARCH64) build
.PHONY: build-macos-x86_64
build-macos-x86_64:
@$(MAKE) TARGET=$(MACOS_X86_64) build
.PHONY: build-macos-aarch64
build-macos-aarch64:
@$(MAKE) TARGET=$(MACOS_AARCH64) build
.PHONY: build-windows-x86_64
build-windows-x86_64:
@$(MAKE) TARGET=$(WINDOWS_X86_64) build
.PHONY: build-freebsd-x86_64
build-freebsd-x86_64:
@$(MAKE) TARGET=$(FREEBSD_X86_64) build
.PHONY: build-all
build-all: setup-cross
@for target in $(TARGETS); do \
echo "Building for $$target..."; \
$(MAKE) TARGET=$$target build || exit 1; \
done
.PHONY: test-all
test-all:
@for target in $(TARGETS); do \
echo "Testing for $$target..."; \
$(MAKE) TARGET=$$target test || exit 1; \
done
.PHONY: package
package: build
@mkdir -p $(DISTDIR)
@if [[ "$(TARGET)" == *"windows"* ]]; then \
cd $(BINDIR) && zip -r $(DISTDIR)/$(BINNAME)-$(BINARY_VERSION)-$(TARGET).zip $(BINARY); \
else \
cd $(BINDIR) && tar -czf $(DISTDIR)/$(BINNAME)-$(BINARY_VERSION)-$(TARGET).tar.gz $(BINARY); \
fi
@echo "Package created: $(DISTDIR)/$(BINNAME)-$(BINARY_VERSION)-$(TARGET).*"
.PHONY: package-all
package-all: build-all
@mkdir -p $(DISTDIR)
@for target in $(TARGETS); do \
echo "Packaging for $$target..."; \
bindir="$(CURDIR)/target/$$target/$(BUILD_MODE)"; \
if [[ "$$target" == *"windows"* ]]; then \
binary="$(BINNAME).exe"; \
cd "$$bindir" && zip -r $(DISTDIR)/$(BINNAME)-$(BINARY_VERSION)-$$target.zip "$$binary"; \
else \
binary="$(BINNAME)"; \
cd "$$bindir" && tar -czf $(DISTDIR)/$(BINNAME)-$(BINARY_VERSION)-$$target.tar.gz "$$binary"; \
fi; \
done
@echo "All packages created in $(DISTDIR)/"
@ls -la $(DISTDIR)/
.PHONY: install
install: build
@echo "Installing $(BINARY) to $(INSTALL_PATH)"
@mkdir -p "$(INSTALL_PATH)"
@install "$(BINDIR)/$(BINARY)" "$(INSTALL_PATH)/$(BINARY)"
.PHONY: release
release: clean lint test package-all
@echo "Release build complete!"
@echo "Artifacts:"
@ls -la $(DISTDIR)/
.PHONY: info
info:
@echo "Configuration:"
@echo " Binary name: $(BINNAME)"
@echo " Target: $(TARGET)"
@echo " Build mode: $(BUILD_MODE)"
@echo " Binary path: $(BINDIR)/$(BINARY)"
@echo " Install path: $(INSTALL_PATH)"
@echo " Version: $(BINARY_VERSION)"
@echo " Git SHA: $(GIT_SHA)"
@echo " Platform: $(UNAME_S)/$(UNAME_M)"
.PHONY: setup-cross
setup-cross:
@echo "Installing cross-compilation targets..."
@rustup target add $(TARGETS)
@if ! command -v cross >/dev/null 2>&1; then \
echo "Installing cross..."; \
cargo install cross; \
fi
.PHONY: setup-git-cliff
setup-git-cliff:
@if ! command -v git-cliff >/dev/null 2>&1; then \
echo "Installing git-cliff..."; \
cargo install git-cliff; \
fi
.PHONY: watch
watch:
@cargo watch -x "check --all-targets" -x test