Files
Just-DataInspector/Tools/Makefile
T
DeepSeek V4.1 Flash 97e9163314 test(tools): 重建单元测试与离屏验证工具(Tools/ 入库)
背景:第 33 轮 Code Review 的三项后续建议(EdgeScanFor 查找哈希化、
EdgePanelLayoutFor 表驱动化、wcstod 自写替身)都卡在"没有可自证的验证手段"上。
本轮先补齐基础设施,再动被建议的代码。

一、Tools/ 从忽略改为入库(只忽略各工具的 Build/ 与 Temp/ 临时产物)
* `.gitignore`:删除 `Tools/` 整目录忽略,改为 `Tools/**/Build/`;
  截图 / 基线 / 报告一律写 `Temp/tools/`(`Temp/` 早已忽略)。

二、新增 Tools/Makefile(统一构建入口,不碰应用本体)
* 目标:`make` 构建全部、`make UnitTests` / `make OffscreenChecks` 单个、
  `make clean` 清工具产物、`make help`。
* 工具链接应用本体 `../Build/Release/*.o`(排除入口 `Main.o`),
  因此 ABI 开关必须与本体一致:`-fno-exceptions` / `-fno-rtti` /
  链接 `-flto -flto-partition=one`。
* 工具源文件自身**不**编译成 LTO 字节码(否则 GCC 15.2.0 在
  `choose_baseaddr` 处 ICE),只在链接阶段带 LTO 参数。
* `-MMD -MP` + `-include`:改头文件 / 增删源文件自动重编重链。
* 修正一个构建陷阱:`$(foreach)` 生成的工具目标会早于 `all`,
  make 会把第一个显式目标当默认目标 ⇒ 显式 `.DEFAULT_GOAL := all`。

三、Tools/UnitTests:纯数据层单元测试(218 项断言,17 个用例)
* 极简自研框架(`Inc/UnitTestFramework.hpp`):无异常、无 RTTI、
  支持布尔 / 整数 / 近似 / **逐位相等** / 宽字符串断言。
* 覆盖:`TryParseDouble` 与 libc `strtod` 逐位一致性(6 万条随机串)、
  接受性语义、`FormatDecimal*` 永不科学计数法、`NiceStep/BuildTicks`、
  公式语法/语义/归一化、批量求值 `FormulaProgram` 与逐点求值逐位一致、
  `ApplyDrawTransform` 顺序、`TransformValueRange`、
  `ComputeMovingAverageIterated` 阶数等价性、
  `DataSet::FormulaTransformFor` 的值域与分组极值(逐点参考实现比对)。

四、Tools/OffscreenChecks:隐藏窗口离屏验证(5 个检查)
* 窗口 `SW_HIDE`;"鼠标"用 `Application::HandleMouseMove()` 合成注入;
  像素从后备缓冲区读取(不依赖窗口可见性),可保存 PNG 与计算区域 FNV 哈希。
* `smoke`:初始化 / 加载 / 渲染 / 落盘,并断言"同状态两帧逐像素相同"。
* `frames`:连续帧抓取(PNG + 哈希 + 耗时),用于问题分析。
* `panels`:7 个实验性面板全部展开后的几何不变式 + **像素基线 A/B**
  (`-mode record` / `-mode compare`)。
* `edgestats`:边沿统计与工具内**独立参考实现**逐位比对,
  覆盖阈值变化与重复查询稳定性。
* `perf`:整帧中位数、`EdgePanelLayoutFor` 单次耗时、
  198 个统计状态的整轮查询耗时(用于优化前后对比)。

五、文档
* `Tools/README.md`:目录结构、构建/运行方式、每个工具的作用、测试项与
  验收标准、临时产物位置、A/B 基线用法、扩展步骤与已知限制。

实测(本机 GCC 15.2.0 UCRT / 125% DPI):
* `UnitTests.exe` → `checks=218 failed=0`
* `OffscreenChecks -all` → 全部 PASS;`SCATTER_TEST.csv` 整帧 ~5ms;
  `MF4-Record.MF4`(283 信号)198 个统计状态整轮查询 0.097 us/query。
2026-11-04 11:41:08 +08:00

188 lines
7.7 KiB
Makefile
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# =============================================================================
# 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/<Tool>/Inc 仅头文件
# Tools/<Tool>/Src 仅实现(每个 `.cpp` 都会编译)
# Tools/<Tool>/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))