# ============================================================================= # Tools/Makefile —— 验证工具(单元测试 / 离屏验证)的统一构建入口 # ============================================================================= # # 职责边界(很重要,别混): # * 根目录 `Makefile` **只负责应用本体**(`Build/Release`、`Build/Debug`)。 # 它按目录通配收集源文件,`Tools/` 不在其 `CXX_SOURCES_PATHS` 里, # 因此**新增工具不需要改根 Makefile**,也不会把工具代码编进应用本体。 # * 本文件负责 `Tools/` 下所有工具的编译 / 链接 / 清理。 # 工具是"白盒验证":直接链接应用本体已构建好的目标文件 # (`../Build/Release/*.o`,排除入口 `Main.o`),因此**必须先构建本体**。 # # 用法(在 `Tools/` 目录下执行,或 `make -C Tools ...`): # make 构建全部工具 # make UnitTests 只构建单元测试 # make OffscreenChecks 只构建离屏验证工具 # make clean 删除所有工具的构建目录(**不动**应用本体) # make help 打印帮助 # # 目录约定: # Tools//Inc 仅头文件 # Tools//Src 仅实现(每个 `.cpp` 都会编译) # Tools//Build 产物(与 Inc/Src 同级;已被 .gitignore 忽略) # # ⚠️ 工具目标文件必须与应用本体使用**同一套 ABI 相关开关**,否则链接会失败: # `-fno-exceptions`(本体 `NO_EXCEPTIONS = 1`) # `-fno-rtti` (本体 `NO_RTTI = 1`,本仓已无 dynamic_cast) # `-flto -flto-partition=one`(本体 `LTO = 1`,目标文件是 LTO 字节码) # ============================================================================= # ---- 工具清单(新增工具只需在这里追加,并保证目录名 = 目标名)-------------- TOOLS := UnitTests OffscreenChecks # ⚠️ 必须显式指定默认目标:下面用 `$(foreach)` 生成的工具目标会先于 `all` 定义, # 而 make 会把"第一个出现的显式目标"当作默认目标 ⇒ 直接 `make` 只会建第一个工具。 .DEFAULT_GOAL := all # 工具内构建目录名(与 Inc / Src 同级) BUILD_DIR_NAME := Build # ---- 工具链 ---------------------------------------------------------------- C_COMPILER := gcc CXX_COMPILER := g++ C_STD := c17 CXX_STD := c++17 INPUT_CHAR_ENCODING := UTF-8 OUTPUT_CHAR_ENCODING := GBK OUTPUT_WCHAR_ENCODING := UTF-16LE # 工具自身优化等级:验证工具追求"跑得快",不参与体积红线 TOOL_OPT := -O2 TOOL_DEBUG_INFO := -g # ---- 与被测应用本体保持一致的 ABI 开关------------------------------------- NO_EXCEPTIONS := 1 NO_RTTI := 1 LTO := 1 # ---- 应用本体 -------------------------------------------------------------- APP_BUILD_DIR := ../Build/Release # 排除入口 `Main.o`(它定义 `WinMain`,与工具的 `main` 不冲突但不需要) APP_OBJECTS := $(filter-out $(APP_BUILD_DIR)/Main.o,$(wildcard $(APP_BUILD_DIR)/*.o)) # ---- 头文件目录(与应用本体一致)------------------------------------------ PROJECT_INCLUDES := \ ../Utilities/Inc \ ../Resources/Inc \ ../Components/Main/Inc \ ../Components/Shared/Inc \ ../Components/Application/Inc \ ../Components/TabBar/Inc \ ../Components/Toolbar/Inc \ ../Components/SignalPanel/Inc \ ../Components/PlotView/Inc # ---- 链接库(与应用本体一致)---------------------------------------------- LINK_LIBS := \ -lgdi32 \ -lgdiplus \ -lmsimg32 \ -lcomdlg32 \ -ld3d11 \ -ldwmapi \ -ld3dcompiler # ---- 编译 / 链接选项 ------------------------------------------------------- # ⚠️ 这里用 `=`(递归展开)而不是 `:=`:`-MF"$(@:%.o=%.d)"` 里的 `$@` # 必须在**展开 recipe 时**才求值(那时才确定是哪一个目标文件)。 BASE_CXXFLAGS = \ -D_UNICODE -DUNICODE \ $(addprefix -I,$(PROJECT_INCLUDES)) \ -std=$(CXX_STD) \ -finput-charset=$(INPUT_CHAR_ENCODING) \ -fexec-charset=$(OUTPUT_CHAR_ENCODING) \ -fwide-exec-charset=$(OUTPUT_WCHAR_ENCODING) \ -static \ -Wall \ $(TOOL_OPT) \ $(TOOL_DEBUG_INFO) \ -MMD -MP -MF"$(@:%.o=%.d)" ifeq ($(strip $(NO_EXCEPTIONS)),1) BASE_CXXFLAGS += -fno-exceptions endif ifeq ($(strip $(NO_RTTI)),1) BASE_CXXFLAGS += -fno-rtti endif BASE_LDFLAGS = ifeq ($(strip $(LTO)),1) # 本体目标文件是 LTO 字节码 ⇒ **链接**必须带 `-flto -flto-partition=one`。 # ⚠️ 工具自身源文件**不要**加 `-flto`:把工具代码也交给 LTRANS 会在 # `choose_baseaddr`(i386.cc:7447)处 ICE(GCC 15.2.0 实测), # 而工具体量很小,根本不需要跨单元内联。 BASE_LDFLAGS += -flto -flto-partition=one endif # ---- 每个工具的源文件 / 目标文件 / 产物 ------------------------------------ $(foreach tool,$(TOOLS),$(eval $(tool)_SRC := $(wildcard $(tool)/Src/*.cpp))) $(foreach tool,$(TOOLS),$(eval $(tool)_OBJ := $(patsubst $(tool)/Src/%.cpp,$(tool)/$(BUILD_DIR_NAME)/%.o,$($(tool)_SRC)))) $(foreach tool,$(TOOLS),$(eval $(tool)_EXE := $(tool)/$(BUILD_DIR_NAME)/$(tool).exe)) # 本体的目标文件缺失时给出明确提示(而不是让链接器报一堆 undefined reference) ifeq ($(strip $(APP_OBJECTS)),) $(warning ********************************************************************) $(warning 未找到应用本体目标文件:$(APP_BUILD_DIR)/*.o) $(warning 请先在项目根目录构建本体,例如:make release -j8) $(warning ********************************************************************) endif # ---- 每个工具的规则(模板)------------------------------------------------- .SUFFIXES: define TOOL_RULES .PHONY: $(1) $(1): $$($(1)_EXE) $$($(1)_EXE): $$($(1)_OBJ) $(APP_OBJECTS) Makefile @echo ====== [$(1)] All File Compiled. Now Linking... ====== $(CXX_COMPILER) $$($(1)_OBJ) $(APP_OBJECTS) -static $(TOOL_DEBUG_INFO) -o $$@ $(LINK_LIBS) $(BASE_LDFLAGS) -mconsole @echo ====== [$(1)] Link Finished: $$@ ====== $$($(1)_OBJ): $(1)/$(BUILD_DIR_NAME)/%.o: $(1)/Src/%.cpp Makefile | $(1)/$(BUILD_DIR_NAME) @echo ====== [$(1)] C++ Source File "$$<" Compiling... ====== $(CXX_COMPILER) -c $$(BASE_CXXFLAGS) -I$(1)/Inc $$< -o $$@ $(1)/$(BUILD_DIR_NAME): @powershell -NoProfile -Command "New-Item -ItemType Directory -Force -Path '$$@' | Out-Null" endef $(foreach tool,$(TOOLS),$(eval $(call TOOL_RULES,$(tool)))) # ---- 聚合目标 -------------------------------------------------------------- .PHONY: all clean help unit offscreen all: $(TOOLS) @echo ====== [Tools] All Tools Built ====== # 便利别名 unit: UnitTests offscreen: OffscreenChecks # 各工具的构建目录(空格分隔 → PowerShell 里 `-split ' '` 成数组) TOOL_BUILD_DIRS := $(strip $(foreach tool,$(TOOLS),$(tool)/$(BUILD_DIR_NAME))) clean: @echo ====== [Tools] Cleaning Build Directories... ====== @powershell -NoProfile -Command "'$(TOOL_BUILD_DIRS)' -split ' ' | ForEach-Object { if (Test-Path $$_) { Remove-Item -Recurse -Force $$_ } }" @echo ====== [Tools] Clean Accomplished ====== help: @echo Tools/Makefile —— 验证工具构建入口 @echo. @echo make 构建全部工具:$(TOOLS) @echo make UnitTests 只构建单元测试(Tools/UnitTests) @echo make OffscreenChecks 只构建离屏验证工具(Tools/OffscreenChecks) @echo make clean 删除各工具自己的 Build/ 目录 @echo make help 显示本帮助 @echo. @echo 依赖:应用本体已构建(../Build/Release/*.o) # ---- 头文件依赖(与根 Makefile 同一套 `-MMD -MP` 机制)-------------------- # ⚠️ 删掉/重命名头文件后若出现"莫名为 undefined reference",先 `make clean`。 -include $(foreach tool,$(TOOLS),$($(tool)_OBJ:.o=.d))