Makefile 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. # Standard of Iron - Makefile
  2. # Provides standard targets for building, running, and managing the project
  3. # Default target
  4. .DEFAULT_GOAL := help
  5. # Configuration
  6. BUILD_DIR := build
  7. BUILD_TIDY_DIR := build-tidy
  8. BINARY_NAME := standard_of_iron
  9. MAP_EDITOR_BINARY := map_editor
  10. DEFAULT_LANG ?= en
  11. # Clang-tidy auto-fixer (git-only by default; --all scans whole project)
  12. CLANG_TIDY_FIXER := scripts/run-clang-tidy-fixes.sh
  13. # Optional knobs (override on the command line)
  14. # e.g. make tidy CLANG_TIDY_JOBS=2 CLANG_TIDY_AUTO_FIX_CHECKS="-*,bugprone-*"
  15. CLANG_TIDY_JOBS ?=
  16. CLANG_TIDY_AUTO_FIX_CHECKS ?=
  17. CLANG_TIDY_FIX_PATHS ?=
  18. # Base for git diff (fallback is origin/main inside the script if unset)
  19. CLANG_TIDY_GIT_BASE ?=
  20. # Formatting config
  21. CLANG_FORMAT ?= clang-format
  22. # Try to find qmlformat in common Qt installation paths if not in PATH
  23. QMLFORMAT ?= $(shell command -v qmlformat 2>/dev/null || echo /usr/lib/qt5/bin/qmlformat)
  24. FMT_GLOBS := -name "*.cpp" -o -name "*.c" -o -name "*.h" -o -name "*.hpp"
  25. SHADER_GLOBS := -name "*.frag" -o -name "*.vert"
  26. QML_GLOBS := -name "*.qml"
  27. # Colors for output
  28. BOLD := \033[1m
  29. GREEN := \033[32m
  30. BLUE := \033[34m
  31. YELLOW := \033[33m
  32. RED := \033[31m
  33. RESET := \033[0m
  34. # Help target - shows available commands
  35. .PHONY: help
  36. help:
  37. @echo "$(BOLD)Standard of Iron - Build System$(RESET)"
  38. @echo ""
  39. @echo "$(BOLD)Available targets:$(RESET)"
  40. @echo " $(GREEN)install$(RESET) - Install all dependencies"
  41. @echo " $(GREEN)configure$(RESET) - Configure build with CMake"
  42. @echo " $(GREEN)build$(RESET) - Build the project"
  43. @echo " $(GREEN)build-tidy$(RESET) - Build with clang-tidy static analysis enabled"
  44. @echo " $(GREEN)debug$(RESET) - Build with debug symbols and GDB support (no optimizations)"
  45. @echo " $(GREEN)release$(RESET) - Build optimized release version"
  46. @echo " $(GREEN)run$(RESET) - Run the main application"
  47. @echo " $(GREEN)editor$(RESET) - Run the map editor"
  48. @echo " $(GREEN)clean$(RESET) - Clean build directory"
  49. @echo " $(GREEN)rebuild$(RESET) - Clean and build"
  50. @echo " $(GREEN)test$(RESET) - Run tests (if any)"
  51. @echo " $(GREEN)format$(RESET) - Format all code (C++, QML, shaders)"
  52. @echo " $(GREEN)format-check$(RESET) - Verify formatting (CI-friendly, no changes)"
  53. @echo " $(GREEN)tidy$(RESET) - Run clang-tidy fixes on changed files (git diff vs origin/main)"
  54. @echo " $(GREEN)tidy-all$(RESET) - Run clang-tidy fixes on the whole project"
  55. @echo " $(GREEN)check-deps$(RESET) - Check if dependencies are installed"
  56. @echo " $(GREEN)dev$(RESET) - Set up development environment (install + configure + build)"
  57. @echo " $(GREEN)all$(RESET) - Full build (configure + build)"
  58. @echo ""
  59. @echo "$(BOLD)Examples:$(RESET)"
  60. @echo " make install # Install dependencies"
  61. @echo " make dev # Complete development setup"
  62. @echo " make debug # Build for debugging with GDB"
  63. @echo " make run # Build and run the game"
  64. @echo " DEFAULT_LANG=de make build # Build with German as default language"
  65. # Install dependencies
  66. .PHONY: install
  67. install:
  68. @echo "$(BOLD)$(BLUE)Installing dependencies...$(RESET)"
  69. @bash scripts/setup-deps.sh --yes
  70. @echo "$(GREEN)✓ Dependencies installed successfully$(RESET)"
  71. # Check if dependencies are installed
  72. .PHONY: check-deps
  73. check-deps:
  74. @echo "$(BOLD)$(BLUE)Checking dependencies...$(RESET)"
  75. @bash scripts/setup-deps.sh --dry-run
  76. # Create build directory
  77. build-dir:
  78. @mkdir -p $(BUILD_DIR)
  79. # Configure build with CMake
  80. .PHONY: configure
  81. configure: build-dir
  82. @echo "$(BOLD)$(BLUE)Configuring build with CMake...$(RESET)"
  83. @cd $(BUILD_DIR) && cmake -DENABLE_CLANG_TIDY=OFF -DDEFAULT_LANG=$(DEFAULT_LANG) ..
  84. @echo "$(GREEN)✓ Configuration complete$(RESET)"
  85. # Build the project
  86. .PHONY: build
  87. build: configure
  88. @echo "$(BOLD)$(BLUE)Building project...$(RESET)"
  89. @cd $(BUILD_DIR) && make -j$$(nproc)
  90. @echo "$(GREEN)✓ Build complete$(RESET)"
  91. # Build with clang-tidy enabled
  92. .PHONY: build-tidy
  93. build-tidy:
  94. @echo "$(BOLD)$(BLUE)Configuring build with clang-tidy enabled...$(RESET)"
  95. @mkdir -p $(BUILD_TIDY_DIR)
  96. @cd $(BUILD_TIDY_DIR) && cmake -DENABLE_CLANG_TIDY=ON -DDEFAULT_LANG=$(DEFAULT_LANG) ..
  97. @echo "$(BOLD)$(BLUE)Building with clang-tidy analysis...$(RESET)"
  98. @cd $(BUILD_TIDY_DIR) && make -j$$(nproc)
  99. @echo "$(GREEN)✓ Build with clang-tidy complete$(RESET)"
  100. # Build everything (alias for build)
  101. .PHONY: all
  102. all: build
  103. # Run the main application
  104. .PHONY: run
  105. run: build
  106. @echo "$(BOLD)$(BLUE)Running Standard of Iron...$(RESET)"
  107. @cd $(BUILD_DIR) && \
  108. BIN_PATH="./bin/$(BINARY_NAME)"; \
  109. if [ ! -x "$$BIN_PATH" ]; then \
  110. echo "$(RED)$(BINARY_NAME) not found at $$BIN_PATH$(RESET)"; \
  111. exit 127; \
  112. fi; \
  113. PLATFORM="$$(uname -s)"; \
  114. DEFAULT_QPA="offscreen"; \
  115. case "$$PLATFORM" in \
  116. Darwin) DEFAULT_QPA="cocoa" ;; \
  117. MINGW*|MSYS*|CYGWIN*) DEFAULT_QPA="windows" ;; \
  118. *) \
  119. if [ -n "$$WAYLAND_DISPLAY" ]; then \
  120. DEFAULT_QPA="wayland"; \
  121. elif [ -n "$$DISPLAY" ]; then \
  122. DEFAULT_QPA="xcb"; \
  123. fi ;; \
  124. esac; \
  125. if [ -z "$$QT_QPA_PLATFORM" ]; then \
  126. echo "$(YELLOW)QT_QPA_PLATFORM not set; defaulting to $$DEFAULT_QPA$(RESET)"; \
  127. QT_QPA_PLATFORM="$$DEFAULT_QPA" "$${BIN_PATH}"; \
  128. else \
  129. "$${BIN_PATH}"; \
  130. fi
  131. # Run with xvfb for headless environments (software rasterization)
  132. .PHONY: run-headless
  133. run-headless: build
  134. @echo "$(BOLD)$(BLUE)Running Standard of Iron under xvfb...$(RESET)"
  135. @if ! command -v xvfb-run >/dev/null 2>&1; then \
  136. echo "$(YELLOW)xvfb-run not found. Installing...$(RESET)"; \
  137. sudo apt-get update -y >/dev/null 2>&1 && sudo apt-get install -y xvfb >/dev/null 2>&1; \
  138. fi
  139. @cd $(BUILD_DIR) && \
  140. BIN_PATH="./bin/$(BINARY_NAME)"; \
  141. if [ ! -x "$$BIN_PATH" ]; then \
  142. echo "$(RED)$(BINARY_NAME) not found at $$BIN_PATH$(RESET)"; \
  143. exit 127; \
  144. fi; \
  145. xvfb-run -s "-screen 0 1280x720x24" "$$BIN_PATH"
  146. # Run the map editor
  147. .PHONY: editor
  148. editor: build
  149. @echo "$(BOLD)$(BLUE)Running Map Editor...$(RESET)"
  150. @cd $(BUILD_DIR) && ./tools/map_editor/$(MAP_EDITOR_BINARY)
  151. # Clean build directory
  152. .PHONY: clean
  153. clean:
  154. @echo "$(BOLD)$(YELLOW)Cleaning build directory...$(RESET)"
  155. @rm -rf $(BUILD_DIR) $(BUILD_TIDY_DIR)
  156. @echo "$(GREEN)✓ Clean complete$(RESET)"
  157. # Rebuild (clean + build)
  158. .PHONY: rebuild
  159. rebuild: clean build
  160. # Development setup (install + configure + build)
  161. .PHONY: dev
  162. dev: install build
  163. @echo "$(GREEN)✓ Development environment ready!$(RESET)"
  164. @echo "$(BOLD)You can now run:$(RESET)"
  165. @echo " make run # Run the game"
  166. @echo " make editor # Run the map editor"
  167. # Run tests (placeholder for future test implementation)
  168. .PHONY: test
  169. test: build
  170. @echo "$(BOLD)$(BLUE)Running tests...$(RESET)"
  171. @if [ -f "$(BUILD_DIR)/bin/standard_of_iron_tests" ]; then \
  172. cd $(BUILD_DIR) && ./bin/standard_of_iron_tests; \
  173. else \
  174. echo "$(RED)Test executable not found. Build may have failed.$(RESET)"; \
  175. exit 1; \
  176. fi
  177. # ---- Formatting: strip comments first, then format (strict) ----
  178. .PHONY: format format-check
  179. EXCLUDE_DIRS := ./$(BUILD_DIR) ./$(BUILD_TIDY_DIR) ./third_party
  180. EXCLUDE_FIND := $(foreach d,$(EXCLUDE_DIRS),-not -path "$(d)/*")
  181. format:
  182. @echo "$(BOLD)$(BLUE)Stripping comments in app/... game/... render/... tools/... ui/... assets/shaders/...$(RESET)"
  183. @if [ -x scripts/remove-comments.sh ]; then \
  184. ./scripts/remove-comments.sh app/ game/ render/ tools/ ui/ assets/shaders/; \
  185. elif [ -f scripts/remove-comments.sh ]; then \
  186. bash scripts/remove-comments.sh app/ game/ render/ tools/ ui/ assets/shaders/; \
  187. else \
  188. echo "$(RED)scripts/remove-comments.sh not found$(RESET)"; exit 1; \
  189. fi
  190. @echo "$(BOLD)$(BLUE)Applying clang-tidy auto fixes (git-only, nice)...$(RESET)"
  191. @bash $(CLANG_TIDY_FIXER) --nice --build-dir="$(BUILD_DIR)" --default-lang="$(DEFAULT_LANG)" $(if $(CLANG_TIDY_AUTO_FIX_CHECKS),--checks="$(CLANG_TIDY_AUTO_FIX_CHECKS)")
  192. @echo "$(BOLD)$(BLUE)Formatting C/C++ files with clang-format...$(RESET)"
  193. @if command -v $(CLANG_FORMAT) >/dev/null 2>&1; then \
  194. find . -type f \( $(FMT_GLOBS) \) $(EXCLUDE_FIND) -print0 \
  195. | xargs -0 -r $(CLANG_FORMAT) -i --style=file; \
  196. echo "$(GREEN)✓ C/C++ formatting complete$(RESET)"; \
  197. else \
  198. echo "$(RED)clang-format not found. Please install it.$(RESET)"; exit 1; \
  199. fi
  200. @echo "$(BOLD)$(BLUE)Formatting QML files...$(RESET)"
  201. @if command -v $(QMLFORMAT) >/dev/null 2>&1 || [ -x "$(QMLFORMAT)" ]; then \
  202. find . -type f \( $(QML_GLOBS) \) $(EXCLUDE_FIND) -print0 \
  203. | xargs -0 -r $(QMLFORMAT) -i; \
  204. echo "$(GREEN)✓ QML formatting complete$(RESET)"; \
  205. else \
  206. echo "$(YELLOW)⚠ qmlformat not found. Skipping QML formatting.$(RESET)"; \
  207. fi
  208. @echo "$(BOLD)$(BLUE)Formatting shader files (.frag, .vert)...$(RESET)"
  209. @if command -v $(CLANG_FORMAT) >/dev/null 2>&1; then \
  210. find . -type f \( $(SHADER_GLOBS) \) $(EXCLUDE_FIND) -print0 \
  211. | xargs -0 -r $(CLANG_FORMAT) -i --style=file; \
  212. echo "$(GREEN)✓ Shader formatting complete$(RESET)"; \
  213. else \
  214. echo "$(YELLOW)⚠ clang-format not found. Shader files not formatted.$(RESET)"; \
  215. fi
  216. @echo "$(GREEN)✓ All formatting complete$(RESET)"
  217. format-check:
  218. @echo "$(BOLD)$(BLUE)Checking formatting compliance...$(RESET)"
  219. @FAILED=0; \
  220. if command -v $(CLANG_FORMAT) >/dev/null 2>&1; then \
  221. echo "$(BLUE)Checking C/C++ files...$(RESET)"; \
  222. find . -type f \( $(FMT_GLOBS) \) $(EXCLUDE_FIND) -print0 \
  223. | xargs -0 -r $(CLANG_FORMAT) --dry-run -Werror --style=file || FAILED=1; \
  224. echo "$(BLUE)Checking shader files...$(RESET)"; \
  225. find . -type f \( $(SHADER_GLOBS) \) $(EXCLUDE_FIND) -print0 \
  226. | xargs -0 -r $(CLANG_FORMAT) --dry-run -Werror --style=file || FAILED=1; \
  227. fi; \
  228. if command -v $(QMLFORMAT) >/dev/null 2>&1 || [ -x "$(QMLFORMAT)" ]; then \
  229. echo "$(BLUE)Checking QML files...$(RESET)"; \
  230. for file in $$(find . -type f \( $(QML_GLOBS) \) $(EXCLUDE_FIND)); do \
  231. $(QMLFORMAT) "$$file" > /tmp/qmlformat_check.tmp 2>/dev/null; \
  232. if ! diff -q "$$file" /tmp/qmlformat_check.tmp >/dev/null 2>&1; then \
  233. echo "$(RED)QML file needs formatting: $$file$(RESET)"; \
  234. FAILED=1; \
  235. fi; \
  236. done; \
  237. rm -f /tmp/qmlformat_check.tmp; \
  238. fi; \
  239. if [ $$FAILED -eq 0 ]; then \
  240. echo "$(GREEN)✓ All formatting checks passed$(RESET)"; \
  241. else \
  242. echo "$(RED)✗ Formatting check failed. Run 'make format' to fix.$(RESET)"; \
  243. exit 1; \
  244. fi
  245. # ---- Static analysis: clang-tidy (driven by fixer script) ----
  246. .PHONY: tidy tidy-all
  247. tidy:
  248. @echo "$(BOLD)$(BLUE)Running clang-tidy fixes on changed files (vs $${CLANG_TIDY_GIT_BASE:-origin/main})...$(RESET)"
  249. @bash $(CLANG_TIDY_FIXER) \
  250. --nice \
  251. --build-dir="$(BUILD_DIR)" \
  252. --default-lang="$(DEFAULT_LANG)" \
  253. $(if $(CLANG_TIDY_JOBS),--jobs="$(CLANG_TIDY_JOBS)") \
  254. $(if $(CLANG_TIDY_FIX_PATHS),--paths="$(CLANG_TIDY_FIX_PATHS)") \
  255. $(if $(CLANG_TIDY_AUTO_FIX_CHECKS),--checks="$(CLANG_TIDY_AUTO_FIX_CHECKS)")
  256. tidy-all:
  257. @echo "$(BOLD)$(BLUE)Running clang-tidy fixes on ALL source files...$(RESET)"
  258. @bash $(CLANG_TIDY_FIXER) \
  259. --all \
  260. --nice \
  261. --build-dir="$(BUILD_DIR)" \
  262. --default-lang="$(DEFAULT_LANG)" \
  263. $(if $(CLANG_TIDY_JOBS),--jobs="$(CLANG_TIDY_JOBS)") \
  264. $(if $(CLANG_TIDY_FIX_PATHS),--paths="$(CLANG_TIDY_FIX_PATHS)") \
  265. $(if $(CLANG_TIDY_AUTO_FIX_CHECKS),--checks="$(CLANG_TIDY_AUTO_FIX_CHECKS)")
  266. # Debug build
  267. .PHONY: debug
  268. debug: build-dir
  269. @echo "$(BOLD)$(BLUE)Configuring debug build with GDB support...$(RESET)"
  270. @cd $(BUILD_DIR) && cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_CLANG_TIDY=OFF -DDEFAULT_LANG=$(DEFAULT_LANG) ..
  271. @echo "$(BOLD)$(BLUE)Building debug version...$(RESET)"
  272. @cd $(BUILD_DIR) && make -j$$(nproc)
  273. @echo "$(GREEN)✓ Debug build complete$(RESET)"
  274. @echo "$(BOLD)Debug Info:$(RESET)"
  275. @echo " Debug symbols: $(GREEN)Enabled (-g3 -ggdb3)$(RESET)"
  276. @echo " Optimizations: $(YELLOW)Disabled (-O0)$(RESET)"
  277. @echo " Frame pointers: $(GREEN)Preserved$(RESET)"
  278. @echo " Inlining: $(YELLOW)Disabled$(RESET)"
  279. @echo ""
  280. @echo "$(BOLD)Run with GDB:$(RESET)"
  281. @echo " cd $(BUILD_DIR) && gdb ./$(BINARY_NAME)"
  282. @echo " cd $(BUILD_DIR) && gdb --args ./$(BINARY_NAME) [args]"
  283. # Release build
  284. .PHONY: release
  285. release: build-dir
  286. @echo "$(BOLD)$(BLUE)Configuring release build...$(RESET)"
  287. @cd $(BUILD_DIR) && cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_CLANG_TIDY=OFF ..
  288. @cd $(BUILD_DIR) && make -j$$(nproc)
  289. @echo "$(GREEN)✓ Release build complete$(RESET)"
  290. # Show build info
  291. .PHONY: info
  292. info:
  293. @echo "$(BOLD)Project Information:$(RESET)"
  294. @echo " Build directory: $(BUILD_DIR)"
  295. @echo " Binary name: $(BINARY_NAME)"
  296. @echo " Map editor: $(MAP_EDITOR_BINARY)"
  297. @echo " CMake version: $$(cmake --version | head -1)"
  298. @echo " GCC version: $$(gcc --version | head -1)"
  299. @if [ -f "$(BUILD_DIR)/$(BINARY_NAME)" ]; then \
  300. echo " Binary built: $(GREEN)✓$(RESET)"; \
  301. else \
  302. echo " Binary built: $(RED)✗$(RESET)"; \
  303. fi
  304. # Quick start for new developers
  305. .PHONY: quickstart
  306. quickstart:
  307. @echo "$(BOLD)$(GREEN)Quick Start Guide:$(RESET)"
  308. @echo "1. Install dependencies: $(BLUE)make install$(RESET)"
  309. @echo "2. Build the project: $(BLUE)make build$(RESET)"
  310. @echo "3. Run the game: $(BLUE)make run$(RESET)"
  311. @echo ""
  312. @echo "Or use the shortcut: $(BLUE)make dev$(RESET)"