Files
Just-DataInspector/Makefile
T
DeepSeek V4.1 Flash 5c48bf922e review(follow-ups): 落地上轮建议并给出实测结论(#2/#4 回退、#3 不重写)
一、Tools 完善
* OffscreenChecks `edgestats`:改为自动挑"最有起伏"的信号。旧实现固定取
  `signals[0]`,而 MF4 的第一条信号是常量(6000)⇒ 三个阈值的断言退化成
  "0 == 0" 的空转。现在扫描前 200 条挑 `max-min` 最大者,SCATTER_TEST 上
  三个阈值分别得到 (20,19)/(20,19)/(20,19),与独立参考实现逐位相同。
* UnitTests 新增 `stdio_mingw_vs_ucrt_formats`:把 MinGW ANSI stdio 与系统
  UCRT 的 printf 在**本程序实际用到的全部格式**(`%.*f` / `%.6g` / `%g` /
  `%.4e` / `%.2f %ls` / `%llu` / `%lld` / `%02X` / `%.2f%%`)上做逐字符比对,
  13.6 万次调用 **0 处差异**(覆盖 1e-40~1e40)。它是"把 swprintf 让给 UCRT"
  这条体积优化的验收标准。
* Tools/README.md:同步新增用例与断言数量(219 项 / 18 用例)。

二、第 33 轮报告 §10 后续建议的结论(详细数字见 Progress 第 34 轮)

1. **`EdgeScanFor` 状态查找哈希化:实测后回退。**
   实现方式为 `deque` + `unordered_multimap<keyHash, EdgeScanState*>`(桶内仍
   逐字段比对,碰撞安全;新建/淘汰/定向作废/清空四处同步索引),功能与像素
   验证全绿。但 198 个状态的整轮查询只从 0.097 降到 0.073 us/query(一轮省
   ≈4.8 us),而产物体积 +5,632 B。⇒ 回退。
   同时修正报告的量级:原估"288 信号 ≈0.3ms/帧",实测 ≈20us/帧。

2. **`EdgePanelLayoutFor` 表驱动化:不做。** 实测 1.9~2.5 us/次(每帧约 14 次
   ≈30us),不是热点;第 33 轮已消除"按种类分派"的重复,剩余 957 行是几何算式
   本身,无法表驱动。本轮改为交付"让这类重写可验证"的手段:
   `-run panels -mode record/compare` 逐面板比对几何 + 像素哈希。

3. **`wcstod` 自写替身:实测 0 字节收益。** 只加
   `-D__USE_MINGW_ANSI_STDIO=0` 全量重编后产物仍是 788,480 B、`mingw_wpformat.o`
   仍被链接。原因是预编译 libstdc++ 自己引用 `__mingw_swprintf`:只要还调用
   `std::to_wstring` / `std::to_string`(全仓约 80 处,若干处实参是浮点),
   gdtoa 必然被链入。⇒ 回退,并在 Makefile 留下说明防止重复尝试。

三、文档
* `Documents/Code_Review_Plan.md` 新增 §六(第 34 轮节点与验收标准)。
* `Documents/Code_Review_Progress.md` 新增第 34 轮逐节点记录(含全部实测数字)。
* `Documents/Code_Review_Report.md` 新增 §11(结论表 + 两处量级修正 + 更新的后续建议)。
* `Documents/agent_mem.md` 新增第 34 轮规则:Tools 约定、两个构建陷阱、
  两条"别再重复做"的实验结论。

验证:
* `make release`(删 .o 全量)零 error / 零 warning,产物 **788,480 B**(与改动前一致)。
* `make -C Tools` 零 error / 零 warning。
* `UnitTests.exe` → checks=219 failed=0 cases=18。
* `OffscreenChecks.exe -all` → 5/5 检查 PASS,checks=71 failed=0。
2026-11-04 11:52:10 +08:00

347 lines
14 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.
# 目标文件名称
TARGET_FILE_NAME = Target
# 编译目录
BUILD_DIR = Build
# Release 编译子目录
SUB_DIR_RELEASE = Release
# Debug 编译子目录
SUB_DIR_DEBUG = Debug
# ⚠️ 产物体积**预警阈值**(用户 2026-10-29 更新):**1MB(1,048,576 B)**。
# 这不是"不许超过":**超过仍可发布**,但到达阈值时必须主动提醒用户,
# 并说明"超在哪一段(`.text` / `.rdata` / `.xdata`+`.pdata` / `.rsrc`)、由哪个改动贡献"。
# 旧红线 900KB / 800KB 均已作废。
# 查段构成的命令(排查超限原因的标准做法):objdump -h Build/Release/Target.exe
RELEASE_OPT = -Oz
# ---- 体积相关编译开关 ------------------------------------------------------
# ⚠️ 曾经有一个 `LAYOUT_BOUNDS` 开关(声称能剔除"显示布局边界"调试叠加层),
# 但它从来没有被任何 `#if` 引用过 —— 是个**名实不符**的死开关,已删除(2026-10-29 审计)。
# 该叠加层现在是**正式功能**(设置菜单里的开关),随产物一起编译;
# 要真正把它变成可剔除项,需要给每个组件的 `BuildBoxes` 重载都加 `#if`,
# 代价远大于它占的十几 KB(当前产物 791,552 B,离 1MB 预警线还有 250+ KB)。
# NO_EXCEPTIONS:1 = 用 `-fno-exceptions` 构建(省 `.xdata`+`.pdata` 约 28KB)。
# ⚠️ 代价:所有"按数据规模预留大块内存"的地方必须用 `MemProbe.hpp` 的
# `MemReserveIfRoom()` **预检验**,不能再靠 try/catch。
# 兜底:`Application::Initialize` 里安装了 `MemInstallTerminateHandler()`
# (万一仍有遗漏,OOM 时弹一次带建议的模态提示后干净退出,而不是静默消失)。
NO_EXCEPTIONS = 1
# LTO:1 = 链接时优化(实测 −94KB)。构建时间约翻倍。
# ⚠️ **切换 LTO 必须 `make clean`**:LTO 与非 LTO 目标文件混链会让
# GCC 15.2.0 在 `binds_to_current_def_p` 处 ICE(已实测复现)。
# ⚠️ 开启 LTO 后 `-Wa,...,-ahlms` 的 `.lst` 汇编清单不再生成(lto-wrapper 会丢弃并告警),
# 因此 `ASM_LISTING` 在 LTO 下为空。
LTO = 1
# ---------------------------------------------------------------------------
# Debug 优化等级
DEBUG_OPT = -O0
# GUI或CUI编译选项 [0]CUI/Console [1]GUI
GUI = 1
# C编译标准
C_STD = c17
# C++编译标准
CXX_STD = c++17
# 源文件编码定义
INPUT_CHAR_ENCODING = UTF-8
# 编译产物单字节字符(char)编码定义
OUTPUT_CHAR_ENCODING = GBK
# 编译产物宽字符(wchar_t)编码定义
OUTPUT_WCHAR_ENCODING = UTF-16LE
# 编译工具前缀
COMPLIER_PREFIX = \
# C编译工具
C_COMPLIER = $(strip $(COMPLIER_PREFIX))gcc
# C++编译工具
CXX_COMPLIER = $(strip $(COMPLIER_PREFIX))g++
# Windows 资源文件编译工具
WIN_RES_COMPLIER = windres
##################################################################################
# 链接库
LIB_LINK = \
-lgdi32 \
-lgdiplus \
-lmsimg32 \
-lcomdlg32 \
-ld3d11 \
-ldwmapi \
-ld3dcompiler \
# C定义
C_DEFS = \
_UNICODE \
UNICODE \
# C头文件目录
C_INCLUDES_PATHS = \
# C源文件目录
C_SOURCES_PATHS = \
# C额外单个源文件
C_EXTERA_SOURCES = \
# C++定义
CXX_DEFS = \
_UNICODE \
UNICODE \
# C++ 头文件目录
# ⚠️ 目录约定:**所有头文件(.hpp)只放在 `Inc`**,`Src` 只放实现(.cpp)。
# `*Internal.hpp`(组件内部共享的常量/工具,如 `PlotViewInternal.hpp`)同样属于头文件,
# 历史上曾被放在 `Src` 下(那里也能编译,因为编译器会先找 `#include` 所在目录),
# 但那是靠“同目录优先”隐式生效的 —— 一旦文件被搬走就会无声失效。现已全部移入 `Inc`。
CXX_INCLUDES_PATHS = \
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 \
# C++源文件目录
CXX_SOURCES_PATHS = \
Utilities/Src \
Components/Main/Src \
Components/Shared/Src \
Components/Application/Src \
Components/TabBar/Src \
Components/Toolbar/Src \
Components/SignalPanel/Src \
Components/PlotView/Src \
# C++额外单个源文件
CXX_EXTERA_SOURCES = \
# Windows 资源文件脚本头文件路径
WIN_RESOURCE_INCLUDES_PATHS = \
Resources \
Resources/Inc \
# Windows 资源文件脚本输入代码页(对应 INPUT_CHAR_ENCODING = UTF-8)
WIN_RESOURCE_CODEPAGE = 65001
# Windows 资源文件脚本列表
WIN_RESOURCE_SCRIPTS = \
Resources/JustDataInspector.rc \
##################################################################################
# C编译选项
CFLAGS = $(foreach text,$(C_DEFS),$(addprefix -D,$(text))) \
$(foreach path,$(C_INCLUDES_PATHS),$(addprefix -I,$(path))) \
-std=$(strip $(C_STD)) \
-finput-charset=$(strip $(INPUT_CHAR_ENCODING)) \
-fexec-charset=$(strip $(OUTPUT_CHAR_ENCODING)) \
-fwide-exec-charset=$(strip $(OUTPUT_WCHAR_ENCODING)) \
-static \
$(FUNC_SECTIONS) \
-Wall \
-MMD -MP -MF"$(@:%.o=%.d)" \
# C++编译选项
CXXFLAGS = $(foreach text,$(CXX_DEFS),$(addprefix -D,$(text))) \
$(foreach path,$(CXX_INCLUDES_PATHS),$(addprefix -I,$(path))) \
-std=$(strip $(CXX_STD)) \
-finput-charset=$(strip $(INPUT_CHAR_ENCODING)) \
-fexec-charset=$(strip $(OUTPUT_CHAR_ENCODING)) \
-fwide-exec-charset=$(strip $(OUTPUT_WCHAR_ENCODING)) \
-static \
$(FUNC_SECTIONS) \
-Wall \
-MMD -MP -MF"$(@:%.o=%.d)" \
-lstdc++ \
# 链接选项
LDFLAGS = -Wl,-Map,$(basename $@).map \
$(GC_SECTIONS) \
##################################################################################
# GUI与CUI选项附加
ifeq ($(GUI), 1)
LDFLAGS += -mwindows
else
LDFLAGS += -mconsole
endif
# ---- 体积/行为相关开关(必须在 CFLAGS / CXXFLAGS / LDFLAGS 定义之后)--------
# 说明:这些是 `=` / `+=` 的递归变量,make 在展开 recipe 时才求值,
# 因此放在 LDFLAGS 定义之后仍然能正确作用到前面的 CFLAGS / LDFLAGS。
ifeq ($(strip $(NO_EXCEPTIONS)), 1)
CXXFLAGS += -fno-exceptions
endif
# ---- RTTI:全项目不使用 `dynamic_cast` / `typeid` ----
# 2026-11-04 审查:唯一的 `dynamic_cast`(Toolbar::BuildBoxes)是多余的强转,
# 已改为直接使用已有的 `UiButtonWidget*`。RTTI 会链入 `cp-demangle.o`
# (Release map 实测 56,320 B)+ `type_info` 表,最小复现程序实测 +75,264 B。
NO_RTTI = 1
ifeq ($(strip $(NO_RTTI)), 1)
CXXFLAGS += -fno-rtti
endif
# ---- SEH 展开表:只对 Release 关闭 `-fasynchronous-unwind-tables` ----
# 2026-11-04 审查实测:**只对 Release** 关闭后产物从 829,440 → 793,088 B(**−36,352 B**),
# 因为 `-fno-exceptions` 下绝大多数函数不再需要 `.pdata`/`.xdata`。
# * `.pdata` 只用于栈回溯(崩溃诊断);本程序不打印栈回溯,功能不受影响。
# * ⚠️ 代价:Release 崩溃时 WER 拿不到可靠调用栈(Debug 构建保留完整展开表)。
# * ⚠️ 全关(`-fno-unwind-tables`)会链接失败(历史踩坑),**不要**打开。
RELEASE_ONLY_OPT = -fno-asynchronous-unwind-tables
# ---- 曾评估并放弃:把 `swprintf` 交给系统 UCRT(`-D__USE_MINGW_ANSI_STDIO=0`)----
# 动机:MinGW 的 ANSI stdio(`mingw_wpformat.o`)会链入 gdtoa 整套
#(`strtodg`/`gdtoa`/`gethex`/`hexnan`),map 实测 `.text` 合计 ≈33 KB。
# 实测结论(2026-11-04):**加这个宏体积一个字节都不减**。
# 原因:预编译的 libstdc++ 自己就引用 `__mingw_swprintf` ——
# 只要工程里还调用 `std::to_wstring` / `std::to_string`(全仓约 80 处),
# 链接器就必须把 MinGW 的 printf 引擎拉进来(用 3 行最小程序 `std::to_wstring(5)` 实测)。
# ⇒ 要真省下这 33 KB,得把 80 处 `std::to_*string` 全换成自写整数格式化,
# 其中若干处实参是浮点(语义会变),风险与收益不匹配。
# 保留证据:`Tools/UnitTests` 的 `stdio_mingw_vs_ucrt_formats`(13.6 万次调用逐字符比对,0 差异)
# 证明两套 printf 在这些格式上**完全等价**,将来若真要做迁移,那条测试就是验收标准。
# 丢弃未被引用的函数/数据(非 LTO 时还需要配合 -ffunction-sections 才有粒度)。
GC_SECTIONS = -Wl,--gc-sections
ifeq ($(strip $(LTO)), 1)
# LTO 自己就做跨编译单元的内联与去重,不需要再开 function-sections;
# 实测 `-flto` 与 `-ffunction-sections -fdata-sections` 同时打开会让
# GCC 15.2.0 在 `binds_to_current_def_p (symtab.cc:2598)` 处 ICE。
FUNC_SECTIONS =
CFLAGS += -flto
CXXFLAGS += -flto
# ⚠️ `-flto-partition=one` 是绕过上面那个 ICE 的关键:
# 默认的多分区 LTRANS 在解析 COMDAT 定义时必崩(同一份代码拆成 11 个分区稳定复现,
# 单分区从未复现)。代价是 LTRANS 只能串行,全量构建比非 LTO 慢约一倍。
LDFLAGS += -flto -flto-partition=one
# lto-wrapper 会丢弃 -Wa 的汇编清单并告警,故 LTO 下不生成 .lst。
ASM_LISTING =
ASM_LISTING_C =
else
FUNC_SECTIONS = -ffunction-sections -fdata-sections
ASM_LISTING = -Wa,-a,-ad,-ahlms=$(@:%.o=%.lst)
ASM_LISTING_C = -Wa,-a,-ad,-alms=$(@:%.o=%.lst)
endif
# ---------------------------------------------------------------------------
# C目标文件及索引目录关联
OBJECTS = $(notdir $(C_EXTERA_SOURCES:.c=.o))
OBJECTS += $(subst .c,.o,$(notdir $(foreach path,$(C_SOURCES_PATHS),$(wildcard $(path)/*.c))))
vpath %.c $(sort $(dir $(C_EXTERA_SOURCES))) $(sort $(C_SOURCES_PATHS))
# C++目标文件及索引目录关联
OBJECTS += $(notdir $(CXX_EXTERA_SOURCES:.cpp=.o))
OBJECTS += $(subst .cpp,.o,$(notdir $(foreach path,$(CXX_SOURCES_PATHS),$(wildcard $(path)/*.cpp))))
vpath %.cpp $(sort $(dir $(CXX_EXTERA_SOURCES))) $(sort $(CXX_SOURCES_PATHS))
# Windows资源文件及索引目录关联
OBJECTS += $(notdir $(WIN_RESOURCE_SCRIPTS:.rc=.o))
vpath %.rc $(sort $(dir $(WIN_RESOURCE_SCRIPTS)))
# Release 目标文件
RELEASE_OBJECTS = $(addprefix $(BUILD_DIR)/$(SUB_DIR_RELEASE)/,$(OBJECTS))
# Debug 目标文件
DEBUG_OBJECTS = $(addprefix $(BUILD_DIR)/$(SUB_DIR_DEBUG)/,$(OBJECTS))
##################################################################################
# all任务 目标为 release 和 debug 产物
all: $(BUILD_DIR)/$(SUB_DIR_RELEASE)/$(TARGET_FILE_NAME).exe $(BUILD_DIR)/$(SUB_DIR_DEBUG)/$(TARGET_FILE_NAME).exe
@echo ====== [All] Build Procedure Accomplished ======
# release任务 目标为 release 产物
release: $(BUILD_DIR)/$(SUB_DIR_RELEASE)/$(TARGET_FILE_NAME).exe
@echo ====== [Release] Build Procedure Accomplished ======
# debug任务 目标为 debug 产物
debug: $(BUILD_DIR)/$(SUB_DIR_DEBUG)/$(TARGET_FILE_NAME).exe
@echo ====== [Debug] Build Procedure Accomplished ======
# 清理任务
clean: $(BUILD_DIR)
powershell rm -r $(BUILD_DIR)
# 构建目录生成
$(BUILD_DIR):
powershell mkdir $@
# Release 工作目录生成
$(BUILD_DIR)/$(SUB_DIR_RELEASE): | $(BUILD_DIR)
powershell mkdir $@
# Debug 工作目录生成
$(BUILD_DIR)/$(SUB_DIR_DEBUG): | $(BUILD_DIR)
powershell mkdir $@
# Release 最终可执行文件编译任务
$(BUILD_DIR)/$(SUB_DIR_RELEASE)/$(TARGET_FILE_NAME).exe: $(RELEASE_OBJECTS)
@echo ====== [Release] All File Compiled. Now Linking... ======
$(CXX_COMPLIER) $(RELEASE_OBJECTS) -static -s -o $@ $(LIB_LINK) $(LDFLAGS)
@echo ====== [Release] Program Link Finished ======
# Debug 最终可执行文件编译任务
$(BUILD_DIR)/$(SUB_DIR_DEBUG)/$(TARGET_FILE_NAME).exe: $(DEBUG_OBJECTS)
@echo ====== [Debug] All File Compiled. Now Linking... ======
$(CXX_COMPLIER) $(DEBUG_OBJECTS) -static -g -o $@ $(LIB_LINK) $(LDFLAGS)
@echo ====== [Debug] Program Link Finished ======
# C Release 目标文件编译
$(BUILD_DIR)/$(SUB_DIR_RELEASE)/%.o: %.c Makefile | $(BUILD_DIR)/$(SUB_DIR_RELEASE)
@echo ====== [Release] C Source File "$<" Compiling... ======
$(C_COMPLIER) -c $(CFLAGS) $(RELEASE_OPT) $(RELEASE_ONLY_OPT) $(ASM_LISTING_C) $< -o $@
# C++ Release 目标文件编译
$(BUILD_DIR)/$(SUB_DIR_RELEASE)/%.o: %.cpp Makefile | $(BUILD_DIR)/$(SUB_DIR_RELEASE)
@echo ====== [Release] C++ Source File "$<" Compiling... ======
$(CXX_COMPLIER) -c $(CXXFLAGS) $(RELEASE_OPT) $(RELEASE_ONLY_OPT) $(ASM_LISTING) $< -o $@
# Release 资源脚本文件编译
$(BUILD_DIR)/$(SUB_DIR_RELEASE)/%.o: %.rc Makefile | $(BUILD_DIR)/$(SUB_DIR_RELEASE)
@echo ====== [Release] Windows Resource Script File "$<" Compiling... ======
$(WIN_RES_COMPLIER) -c $(WIN_RESOURCE_CODEPAGE) $(foreach path,$(WIN_RESOURCE_INCLUDES_PATHS),$(addprefix -I,$(path))) $< -o $@
# C Debug 目标文件编译
# ⚠️ 2026-11-04 审查修正:原来这里误用了 `$(RELEASE_OPT)`,导致 `DEBUG_OPT = -O0`
# **从未生效**(Debug 实际上也是 -Oz,且不带 `-g`,根本无法调试)。
$(BUILD_DIR)/$(SUB_DIR_DEBUG)/%.o: %.c Makefile | $(BUILD_DIR)/$(SUB_DIR_DEBUG)
@echo ====== [Debug] C Source File "$<" Compiling... ======
$(C_COMPLIER) -c $(CFLAGS) $(DEBUG_OPT) -g $(ASM_LISTING_C) $< -o $@
# C++ Debug 目标文件编译
$(BUILD_DIR)/$(SUB_DIR_DEBUG)/%.o: %.cpp Makefile | $(BUILD_DIR)/$(SUB_DIR_DEBUG)
@echo ====== [Debug] C++ Source File "$<" Compiling... ======
$(CXX_COMPLIER) -c $(CXXFLAGS) $(DEBUG_OPT) -g $(ASM_LISTING) $< -o $@
# Debug 资源脚本文件编译
$(BUILD_DIR)/$(SUB_DIR_DEBUG)/%.o: %.rc Makefile | $(BUILD_DIR)/$(SUB_DIR_DEBUG)
@echo ====== [Debug] Windows Resource Script File "$<" Compiling... ======
$(WIN_RES_COMPLIER) -c $(WIN_RESOURCE_CODEPAGE) $(foreach path,$(WIN_RESOURCE_INCLUDES_PATHS),$(addprefix -I,$(path))) $< -o $@
# 依赖关系
-include $(RELEASE_OBJECTS:.o=.d)
-include $(DEBUG_OBJECTS:.o=.d)