Makefile 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  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 (includes map pipeline)"
  47. @echo " $(GREEN)run-map-pipeline$(RESET) - Run map preprocessing pipeline only"
  48. @echo " $(GREEN)editor$(RESET) - Run the map editor"
  49. @echo " $(GREEN)clean$(RESET) - Clean build directory"
  50. @echo " $(GREEN)rebuild$(RESET) - Clean and build"
  51. @echo " $(GREEN)test$(RESET) - Run tests (if any)"
  52. @echo " $(GREEN)validate-content$(RESET) - Validate mission and campaign JSON files"
  53. @echo " $(GREEN)test-validator$(RESET) - Run validator integration tests"
  54. @echo " $(GREEN)format$(RESET) - Format all code (C++, QML, shaders, Python)"
  55. @echo " $(GREEN)format-check$(RESET) - Verify formatting (CI-friendly, no changes)"
  56. @echo " $(GREEN)tidy$(RESET) - Run clang-tidy fixes on changed files (git diff vs origin/main)"
  57. @echo " $(GREEN)tidy-all$(RESET) - Run clang-tidy fixes on the whole project"
  58. @echo " $(GREEN)check-deps$(RESET) - Check if dependencies are installed"
  59. @echo " $(GREEN)dev$(RESET) - Set up development environment (install + configure + build)"
  60. @echo " $(GREEN)all$(RESET) - Full build (configure + build)"
  61. @echo ""
  62. @echo "$(BOLD)Examples:$(RESET)"
  63. @echo " make install # Install dependencies"
  64. @echo " make dev # Complete development setup"
  65. @echo " make debug # Build for debugging with GDB"
  66. @echo " make run # Build and run the game"
  67. @echo " DEFAULT_LANG=de make build # Build with German as default language"
  68. # Install dependencies
  69. .PHONY: install
  70. install:
  71. @echo "$(BOLD)$(BLUE)Installing dependencies...$(RESET)"
  72. @bash scripts/setup-deps.sh --yes
  73. @echo "$(GREEN)✓ Dependencies installed successfully$(RESET)"
  74. # Check if dependencies are installed
  75. .PHONY: check-deps
  76. check-deps:
  77. @echo "$(BOLD)$(BLUE)Checking dependencies...$(RESET)"
  78. @bash scripts/setup-deps.sh --dry-run
  79. # Create build directory
  80. build-dir:
  81. @mkdir -p $(BUILD_DIR)
  82. # Configure build with CMake
  83. .PHONY: configure
  84. configure: build-dir
  85. @echo "$(BOLD)$(BLUE)Configuring build with CMake...$(RESET)"
  86. @cd $(BUILD_DIR) && cmake -DENABLE_CLANG_TIDY=OFF -DDEFAULT_LANG=$(DEFAULT_LANG) ..
  87. @echo "$(GREEN)✓ Configuration complete$(RESET)"
  88. # Build the project
  89. .PHONY: build
  90. build: configure
  91. @echo "$(BOLD)$(BLUE)Building project...$(RESET)"
  92. @cd $(BUILD_DIR) && make -j$$(nproc)
  93. @echo "$(GREEN)✓ Build complete$(RESET)"
  94. # Build with clang-tidy enabled
  95. .PHONY: build-tidy
  96. build-tidy:
  97. @echo "$(BOLD)$(BLUE)Configuring build with clang-tidy enabled...$(RESET)"
  98. @mkdir -p $(BUILD_TIDY_DIR)
  99. @cd $(BUILD_TIDY_DIR) && cmake -DENABLE_CLANG_TIDY=ON -DDEFAULT_LANG=$(DEFAULT_LANG) ..
  100. @echo "$(BOLD)$(BLUE)Building with clang-tidy analysis...$(RESET)"
  101. @cd $(BUILD_TIDY_DIR) && make -j$$(nproc)
  102. @echo "$(GREEN)✓ Build with clang-tidy complete$(RESET)"
  103. # Build everything (alias for build)
  104. .PHONY: all
  105. all: build
  106. # Run map pipeline preprocessing
  107. .PHONY: run-map-pipeline
  108. run-map-pipeline:
  109. @echo "$(BOLD)$(BLUE)Running map pipeline preprocessing...$(RESET)"
  110. @bash scripts/run-map-pipeline.sh $(if $(map_pipeline_rebuild),--rebuild,)
  111. @echo "$(GREEN)✓ Map pipeline complete$(RESET)"
  112. # Run the main application
  113. .PHONY: run
  114. run: run-map-pipeline build
  115. @echo "$(BOLD)$(BLUE)Running Standard of Iron...$(RESET)"
  116. @cd $(BUILD_DIR) && \
  117. BIN_PATH="./bin/$(BINARY_NAME)"; \
  118. if [ ! -x "$$BIN_PATH" ]; then \
  119. echo "$(RED)$(BINARY_NAME) not found at $$BIN_PATH$(RESET)"; \
  120. exit 127; \
  121. fi; \
  122. PLATFORM="$$(uname -s)"; \
  123. DEFAULT_QPA="offscreen"; \
  124. case "$$PLATFORM" in \
  125. Darwin) DEFAULT_QPA="cocoa" ;; \
  126. MINGW*|MSYS*|CYGWIN*) DEFAULT_QPA="windows" ;; \
  127. *) \
  128. if [ -n "$$WAYLAND_DISPLAY" ]; then \
  129. DEFAULT_QPA="wayland"; \
  130. elif [ -n "$$DISPLAY" ]; then \
  131. DEFAULT_QPA="xcb"; \
  132. fi ;; \
  133. esac; \
  134. if [ -z "$$QT_QPA_PLATFORM" ]; then \
  135. echo "$(YELLOW)QT_QPA_PLATFORM not set; defaulting to $$DEFAULT_QPA$(RESET)"; \
  136. QT_QPA_PLATFORM="$$DEFAULT_QPA" "$${BIN_PATH}"; \
  137. else \
  138. "$${BIN_PATH}"; \
  139. fi
  140. # Run with xvfb for headless environments (software rasterization)
  141. .PHONY: run-headless
  142. run-headless: build
  143. @echo "$(BOLD)$(BLUE)Running Standard of Iron under xvfb...$(RESET)"
  144. @if ! command -v xvfb-run >/dev/null 2>&1; then \
  145. echo "$(YELLOW)xvfb-run not found. Installing...$(RESET)"; \
  146. sudo apt-get update -y >/dev/null 2>&1 && sudo apt-get install -y xvfb >/dev/null 2>&1; \
  147. fi
  148. @cd $(BUILD_DIR) && \
  149. BIN_PATH="./bin/$(BINARY_NAME)"; \
  150. if [ ! -x "$$BIN_PATH" ]; then \
  151. echo "$(RED)$(BINARY_NAME) not found at $$BIN_PATH$(RESET)"; \
  152. exit 127; \
  153. fi; \
  154. xvfb-run -s "-screen 0 1280x720x24" "$$BIN_PATH"
  155. # Run the map editor
  156. .PHONY: editor
  157. editor: build
  158. @echo "$(BOLD)$(BLUE)Running Map Editor...$(RESET)"
  159. @cd $(BUILD_DIR) && ./bin/$(MAP_EDITOR_BINARY)
  160. # Clean build directory
  161. .PHONY: clean
  162. clean:
  163. @echo "$(BOLD)$(YELLOW)Cleaning build directory...$(RESET)"
  164. @rm -rf $(BUILD_DIR) $(BUILD_TIDY_DIR)
  165. @echo "$(GREEN)✓ Clean complete$(RESET)"
  166. # Rebuild (clean + build)
  167. .PHONY: rebuild
  168. rebuild: clean build
  169. # Development setup (install + configure + build)
  170. .PHONY: dev
  171. dev: install build
  172. @echo "$(GREEN)✓ Development environment ready!$(RESET)"
  173. @echo "$(BOLD)You can now run:$(RESET)"
  174. @echo " make run # Run the game"
  175. @echo " make editor # Run the map editor"
  176. # Run tests (placeholder for future test implementation)
  177. .PHONY: test
  178. test: build
  179. @echo "$(BOLD)$(BLUE)Running tests...$(RESET)"
  180. @if [ -f "$(BUILD_DIR)/bin/standard_of_iron_tests" ]; then \
  181. cd $(BUILD_DIR) && ./bin/standard_of_iron_tests; \
  182. else \
  183. echo "$(RED)Test executable not found. Build may have failed.$(RESET)"; \
  184. exit 1; \
  185. fi
  186. # Validate mission and campaign content
  187. .PHONY: validate-content
  188. validate-content: build
  189. @echo "$(BOLD)$(BLUE)Validating mission and campaign content...$(RESET)"
  190. @if [ -f "$(BUILD_DIR)/bin/content_validator" ]; then \
  191. $(BUILD_DIR)/bin/content_validator assets; \
  192. if [ $$? -eq 0 ]; then \
  193. echo "$(GREEN)✓ Content validation passed$(RESET)"; \
  194. else \
  195. echo "$(RED)✗ Content validation failed$(RESET)"; \
  196. exit 1; \
  197. fi \
  198. else \
  199. echo "$(RED)Content validator not found. Build may have failed.$(RESET)"; \
  200. exit 1; \
  201. fi
  202. # Test validator with integration tests
  203. .PHONY: test-validator
  204. test-validator: build
  205. @echo "$(BOLD)$(BLUE)Running validator integration tests...$(RESET)"
  206. @if [ -f "tests/validator_integration_test.sh" ]; then \
  207. bash tests/validator_integration_test.sh; \
  208. else \
  209. echo "$(RED)Validator integration test not found.$(RESET)"; \
  210. exit 1; \
  211. fi
  212. # ---- Formatting: strip comments first, then format (strict) ----
  213. .PHONY: format format-check
  214. EXCLUDE_DIRS := ./$(BUILD_DIR) ./$(BUILD_TIDY_DIR) ./third_party
  215. EXCLUDE_PATHS := */venv/* */.venv/*
  216. EXCLUDE_FIND := $(foreach d,$(EXCLUDE_DIRS),-not -path "$(d)/*") \
  217. $(foreach p,$(EXCLUDE_PATHS),-not -path "$(p)")
  218. format:
  219. @echo "$(BOLD)$(BLUE)Stripping comments in app/... game/... render/... scripts/... tools/... ui/... assets/shaders/...$(RESET)"
  220. @if [ -x scripts/remove-comments.sh ]; then \
  221. ./scripts/remove-comments.sh app/ game/ render/ scripts/ tools/ ui/ assets/shaders/; \
  222. elif [ -f scripts/remove-comments.sh ]; then \
  223. bash scripts/remove-comments.sh app/ game/ render/ scripts/ tools/ ui/ assets/shaders/; \
  224. else \
  225. echo "$(RED)scripts/remove-comments.sh not found$(RESET)"; exit 1; \
  226. fi
  227. @echo "$(BOLD)$(BLUE)Applying clang-tidy auto fixes (git-only, nice)...$(RESET)"
  228. @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)")
  229. @echo "$(BOLD)$(BLUE)Formatting C/C++ files with clang-format...$(RESET)"
  230. @if command -v $(CLANG_FORMAT) >/dev/null 2>&1; then \
  231. find . -type f \( $(FMT_GLOBS) \) $(EXCLUDE_FIND) -print0 \
  232. | xargs -0 -r $(CLANG_FORMAT) -i --style=file; \
  233. echo "$(GREEN)✓ C/C++ formatting complete$(RESET)"; \
  234. else \
  235. echo "$(RED)clang-format not found. Please install it.$(RESET)"; exit 1; \
  236. fi
  237. @echo "$(BOLD)$(BLUE)Formatting QML files...$(RESET)"
  238. @if command -v $(QMLFORMAT) >/dev/null 2>&1 || [ -x "$(QMLFORMAT)" ]; then \
  239. find . -type f \( $(QML_GLOBS) \) $(EXCLUDE_FIND) -print0 \
  240. | xargs -0 -r $(QMLFORMAT) -i; \
  241. echo "$(GREEN)✓ QML formatting complete$(RESET)"; \
  242. else \
  243. echo "$(YELLOW)⚠ qmlformat not found. Skipping QML formatting.$(RESET)"; \
  244. fi
  245. @echo "$(BOLD)$(BLUE)Formatting shader files (.frag, .vert)...$(RESET)"
  246. @if command -v $(CLANG_FORMAT) >/dev/null 2>&1; then \
  247. find . -type f \( $(SHADER_GLOBS) \) $(EXCLUDE_FIND) -print0 \
  248. | xargs -0 -r $(CLANG_FORMAT) -i --style=file; \
  249. echo "$(GREEN)✓ Shader formatting complete$(RESET)"; \
  250. else \
  251. echo "$(YELLOW)⚠ clang-format not found. Shader files not formatted.$(RESET)"; \
  252. fi
  253. @echo "$(BOLD)$(BLUE)Formatting Python files with black...$(RESET)"
  254. @if command -v black >/dev/null 2>&1; then \
  255. find . -type f -name "*.py" $(EXCLUDE_FIND) -print0 \
  256. | xargs -0 -r black --quiet || true; \
  257. echo "$(GREEN)✓ Python formatting complete$(RESET)"; \
  258. else \
  259. echo "$(YELLOW)⚠ black not found. Skipping Python formatting.$(RESET)"; \
  260. fi
  261. @echo "$(GREEN)✓ All formatting complete$(RESET)"
  262. format-check:
  263. @echo "$(BOLD)$(BLUE)Checking formatting compliance...$(RESET)"
  264. @FAILED=0; \
  265. if command -v $(CLANG_FORMAT) >/dev/null 2>&1; then \
  266. echo "$(BLUE)Checking C/C++ files...$(RESET)"; \
  267. find . -type f \( $(FMT_GLOBS) \) $(EXCLUDE_FIND) -print0 \
  268. | xargs -0 -r $(CLANG_FORMAT) --dry-run -Werror --style=file || FAILED=1; \
  269. echo "$(BLUE)Checking shader files...$(RESET)"; \
  270. find . -type f \( $(SHADER_GLOBS) \) $(EXCLUDE_FIND) -print0 \
  271. | xargs -0 -r $(CLANG_FORMAT) --dry-run -Werror --style=file || FAILED=1; \
  272. fi; \
  273. if command -v $(QMLFORMAT) >/dev/null 2>&1 || [ -x "$(QMLFORMAT)" ]; then \
  274. echo "$(BLUE)Checking QML files...$(RESET)"; \
  275. for file in $$(find . -type f \( $(QML_GLOBS) \) $(EXCLUDE_FIND)); do \
  276. $(QMLFORMAT) "$$file" > /tmp/qmlformat_check.tmp 2>/dev/null; \
  277. if ! diff -q "$$file" /tmp/qmlformat_check.tmp >/dev/null 2>&1; then \
  278. echo "$(RED)QML file needs formatting: $$file$(RESET)"; \
  279. FAILED=1; \
  280. fi; \
  281. done; \
  282. rm -f /tmp/qmlformat_check.tmp; \
  283. fi; \
  284. if command -v black >/dev/null 2>&1; then \
  285. echo "$(BLUE)Checking Python files...$(RESET)"; \
  286. if ! find . -type f -name "*.py" $(EXCLUDE_FIND) -print0 \
  287. | xargs -0 -r black --check --quiet; then \
  288. echo "$(RED)Python files need formatting$(RESET)"; \
  289. FAILED=1; \
  290. fi; \
  291. fi; \
  292. if [ $$FAILED -eq 0 ]; then \
  293. echo "$(GREEN)✓ All formatting checks passed$(RESET)"; \
  294. else \
  295. echo "$(RED)✗ Formatting check failed. Run 'make format' to fix.$(RESET)"; \
  296. exit 1; \
  297. fi
  298. # ---- Static analysis: clang-tidy (driven by fixer script) ----
  299. .PHONY: tidy tidy-all
  300. tidy:
  301. @echo "$(BOLD)$(BLUE)Running clang-tidy fixes on changed files (vs $${CLANG_TIDY_GIT_BASE:-origin/main})...$(RESET)"
  302. @bash $(CLANG_TIDY_FIXER) \
  303. --nice \
  304. --build-dir="$(BUILD_DIR)" \
  305. --default-lang="$(DEFAULT_LANG)" \
  306. $(if $(CLANG_TIDY_JOBS),--jobs="$(CLANG_TIDY_JOBS)") \
  307. $(if $(CLANG_TIDY_FIX_PATHS),--paths="$(CLANG_TIDY_FIX_PATHS)") \
  308. $(if $(CLANG_TIDY_AUTO_FIX_CHECKS),--checks="$(CLANG_TIDY_AUTO_FIX_CHECKS)")
  309. tidy-all:
  310. @echo "$(BOLD)$(BLUE)Running clang-tidy fixes on ALL source files...$(RESET)"
  311. @bash $(CLANG_TIDY_FIXER) \
  312. --all \
  313. --nice \
  314. --build-dir="$(BUILD_DIR)" \
  315. --default-lang="$(DEFAULT_LANG)" \
  316. $(if $(CLANG_TIDY_JOBS),--jobs="$(CLANG_TIDY_JOBS)") \
  317. $(if $(CLANG_TIDY_FIX_PATHS),--paths="$(CLANG_TIDY_FIX_PATHS)") \
  318. $(if $(CLANG_TIDY_AUTO_FIX_CHECKS),--checks="$(CLANG_TIDY_AUTO_FIX_CHECKS)")
  319. # Debug build
  320. .PHONY: debug
  321. debug: build-dir
  322. @echo "$(BOLD)$(BLUE)Configuring debug build with GDB support...$(RESET)"
  323. @cd $(BUILD_DIR) && cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_CLANG_TIDY=OFF -DDEFAULT_LANG=$(DEFAULT_LANG) ..
  324. @echo "$(BOLD)$(BLUE)Building debug version...$(RESET)"
  325. @cd $(BUILD_DIR) && make -j$$(nproc)
  326. @echo "$(GREEN)✓ Debug build complete$(RESET)"
  327. @echo "$(BOLD)Debug Info:$(RESET)"
  328. @echo " Debug symbols: $(GREEN)Enabled (-g3 -ggdb3)$(RESET)"
  329. @echo " Optimizations: $(YELLOW)Disabled (-O0)$(RESET)"
  330. @echo " Frame pointers: $(GREEN)Preserved$(RESET)"
  331. @echo " Inlining: $(YELLOW)Disabled$(RESET)"
  332. @echo ""
  333. @echo "$(BOLD)Run with GDB:$(RESET)"
  334. @echo " cd $(BUILD_DIR) && gdb ./$(BINARY_NAME)"
  335. @echo " cd $(BUILD_DIR) && gdb --args ./$(BINARY_NAME) [args]"
  336. # Release build
  337. .PHONY: release
  338. release: build-dir
  339. @echo "$(BOLD)$(BLUE)Configuring release build...$(RESET)"
  340. @cd $(BUILD_DIR) && cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_CLANG_TIDY=OFF ..
  341. @cd $(BUILD_DIR) && make -j$$(nproc)
  342. @echo "$(GREEN)✓ Release build complete$(RESET)"
  343. # Show build info
  344. .PHONY: info
  345. info:
  346. @echo "$(BOLD)Project Information:$(RESET)"
  347. @echo " Build directory: $(BUILD_DIR)"
  348. @echo " Binary name: $(BINARY_NAME)"
  349. @echo " Map editor: $(MAP_EDITOR_BINARY)"
  350. @echo " CMake version: $$(cmake --version | head -1)"
  351. @echo " GCC version: $$(gcc --version | head -1)"
  352. @if [ -f "$(BUILD_DIR)/$(BINARY_NAME)" ]; then \
  353. echo " Binary built: $(GREEN)✓$(RESET)"; \
  354. else \
  355. echo " Binary built: $(RED)✗$(RESET)"; \
  356. fi
  357. # Quick start for new developers
  358. .PHONY: quickstart
  359. quickstart:
  360. @echo "$(BOLD)$(GREEN)Quick Start Guide:$(RESET)"
  361. @echo "1. Install dependencies: $(BLUE)make install$(RESET)"
  362. @echo "2. Build the project: $(BLUE)make build$(RESET)"
  363. @echo "3. Run the game: $(BLUE)make run$(RESET)"
  364. @echo ""
  365. @echo "Or use the shortcut: $(BLUE)make dev$(RESET)"