# 目标文件名称 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:1 = 编入"显示布局边界"调试叠加层(约 13KB);0 = 剔除。 # 剔除后用 `make release LAYOUT_BOUNDS=0` 可得到最小体积,但发布版就没有该菜单项了。 LAYOUT_BOUNDS = 1 # 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 $(LAYOUT_BOUNDS)), 1) CXX_DEFS += JDI_LAYOUT_BOUNDS endif ifeq ($(strip $(NO_EXCEPTIONS)), 1) CXXFLAGS += -fno-exceptions endif # 丢弃未被引用的函数/数据(非 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 -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) $(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) $(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 目标文件编译 $(BUILD_DIR)/$(SUB_DIR_DEBUG)/%.o: %.c Makefile | $(BUILD_DIR)/$(SUB_DIR_DEBUG) @echo ====== [Debug] C Source File "$<" Compiling... ====== $(C_COMPLIER) -c $(CFLAGS) $(RELEASE_OPT) $(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) $(RELEASE_OPT) $(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)