# 目标文件名称
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)
