1. 为什么TLSR8258的“虚拟文件”配置是项目启动的第一道生死关
刚拿到泰凌微TLSR8258开发板时,我花了一整天反复重装SDK、清空缓存、重刷烧录工具,最后发现连最基础的LED闪烁例程都编译不过——报错信息里反复出现fatal error: tlsr_common.h: No such file or directory。翻遍官方文档,只看到一句轻描淡写的“请确保SDK路径已正确挂载”,但没人告诉你:这个“挂载”根本不是指把SDK文件夹拖进IDE里那么简单,而是要让编译系统在逻辑层面“相信”那些头文件、库文件、链接脚本真实存在,哪怕它们物理上压根没放在你本地硬盘的某个路径下。
这就是TLSR8258 SDK里所谓“虚拟文件”的真实含义:它不是VMware那种虚拟磁盘意义上的“虚拟”,而是一种编译时路径映射机制。泰凌微为了适配不同芯片型号(TLSR8258/TLSR8278/TLSR8358)和不同协议栈(BLE 5.0/Thread/Zigbee),把共用的底层驱动、协议栈核心、启动代码全部抽离成统一的SDK包,再通过一套精巧的Makefile规则和符号链接系统,在编译前动态生成指向具体芯片资源的“逻辑路径”。你看到的include/tlsr_common.h,实际可能链接到../sdk_8258/common/inc/tlsr_common.h,也可能链接到../sdk_8278/common/inc/tlsr_common.h,甚至可能是../sdk_zigbee/common/inc/tlsr_common.h——这一切,全靠一个叫virtual_file.mk的配置文件控制。
这解释了为什么网上大量教程教你怎么“下载SDK压缩包→解压→导入工程”,结果90%的人卡在第一步:编译器找不到头文件。因为SDK本身不包含完整的物理文件树,它只提供骨架和映射规则;真正的“血肉”——那些.a静态库、.ld链接脚本、.bin固件模板——必须由开发者根据目标芯片手动补全或由构建系统按需生成。我后来拆解了泰凌微官方Demo工程的Makefile,发现关键就在$(SDK_ROOT)/make/virtual_file.mk这个文件里,它定义了SDK_INC_PATHS、SDK_LIB_PATHS、SDK_LINK_SCRIPT三个核心变量,而这些变量的值,又依赖于你在project_config.h里定义的CHIP_MODEL宏。换句话说,你不是在配置一个SDK路径,而是在告诉编译系统:“我现在要为TLSR8258芯片,从SDK仓库里精准提取哪一套资源组合”。
这种设计的好处是极致的复用性——同一套SDK源码,通过切换CHIP_MODEL就能生成适配不同芯片的固件;坏处是门槛陡峭——新手根本分不清哪些是物理存在的文件,哪些是Makefile动态生成的符号链接。我见过太多人把tlsr_common.h手动复制到自己工程目录下,结果编译时又报undefined reference to 'rf_drv_init',因为链接器找不到对应的librf.a,而这个库的路径,同样由virtual_file.mk里的SDK_LIB_PATHS决定。所以,搞懂“虚拟文件”的本质,不是为了炫技,而是为了避开后续所有编译失败、链接错误、运行崩溃的根源。它不是可选项,而是TLSR8258开发的必经入口。
提示:不要试图用Windows资源管理器去“查看”SDK目录结构来判断文件是否存在。TLSR8258的SDK路径映射是Makefile驱动的,必须在终端执行
make -n(dry-run模式)才能看到编译器实际访问的物理路径。这是验证虚拟文件配置是否生效的唯一可靠方法。
2. 从零开始搭建SDK环境:三步走通虚拟文件链路
很多教程一上来就让你下载TLSR8258_SDK_V4.3.0.zip,然后双击解压。这一步看似简单,实则埋下第一个雷——SDK包里根本没有virtual_file.mk这个文件。它被刻意剥离,放在另一个叫tools的独立仓库里。泰凌微的官方策略是:SDK主体负责功能逻辑,tools仓库负责构建基础设施。如果你只下载SDK,make命令会直接报错No rule to make target 'virtual_file.mk',连第一行日志都打不出来。
2.1 下载并初始化两个核心仓库
首先,你必须同时获取两个Git仓库:
# 创建工作目录 mkdir -p ~/telsr8258_dev && cd ~/telsr8258_dev # 克隆SDK主仓库(注意:不是zip包!) git clone https://github.com/TelinkSemiconductor/TLSR8258_SDK.git sdk_8258 cd sdk_8258 git checkout v4.3.0 # 切换到稳定版本,避免master分支的不稳定变更 # 返回上级目录,克隆tools仓库 cd .. git clone https://github.com/TelinkSemiconductor/TLSR_TOOLS.git tools cd tools git checkout v1.2.0这里的关键点在于:tools仓库里包含了make/virtual_file.mk、make/common.mk、utils/gen_link_script.py等所有构建基础设施。而sdk_8258仓库里只有application/,driver/,stack/等业务代码。两者必须共存于同一父目录下,且目录名必须严格匹配——sdk_8258和tools不能改成sdk或tool,否则Makefile里的相对路径$(SDK_ROOT)/../tools/make/virtual_file.mk就会失效。
2.2 配置SDK_ROOT环境变量与项目结构
接下来,你需要在你的项目根目录下创建一个标准结构。假设你要开发一个BLE Beacon项目:
cd ~/telsr8258_dev mkdir -p beacon_app/{application,driver,stack} cp -r sdk_8258/application/ble/ble_beacon/* beacon_app/application/ cp -r sdk_8258/driver/* beacon_app/driver/ cp -r sdk_8258/stack/ble/* beacon_app/stack/然后,在beacon_app/目录下创建Makefile,内容如下(这是最简可行版,省略了所有注释和扩展功能):
# Makefile for TLSR8258 BLE Beacon SDK_ROOT := $(abspath ../sdk_8258) TOOLS_ROOT := $(abspath ../tools) # 必须显式包含虚拟文件生成规则 include $(TOOLS_ROOT)/make/virtual_file.mk # 定义芯片型号,这是触发虚拟文件映射的核心开关 CHIP_MODEL := TLSR8258F512 # 编译目标 TARGET := beacon_app BUILD_DIR := build # 指定源文件(注意:这里不写绝对路径,用相对路径) SOURCES := \ application/main.c \ driver/gpio.c \ stack/ble/ble.c # 包含路径(虚拟文件系统会自动展开) INCLUDES := \ $(SDK_ROOT)/application \ $(SDK_ROOT)/driver \ $(SDK_ROOT)/stack \ $(SDK_ROOT)/common/inc # 编译器与工具链 CC := arm-none-eabi-gcc LD := arm-none-eabi-gcc OBJCOPY := arm-none-eabi-objcopy # 编译规则(简化版) $(BUILD_DIR)/%.o: %.c @mkdir -p $(dir $@) $(CC) -c $(CFLAGS) -I$(INCLUDES) -D$(CHIP_MODEL) $< -o $@ $(TARGET).elf: $(SOURCES:.c=.o) $(LD) -T$(SDK_ROOT)/ld/$(CHIP_MODEL)_flash.ld $^ -o $@ $(LDFLAGS) $(TARGET).bin: $(TARGET).elf $(OBJCOPY) -O binary $< $@ .PHONY: all clean all: $(TARGET).bin clean: rm -rf $(BUILD_DIR) $(TARGET).*这个Makefile里最关键的三行是:
include $(TOOLS_ROOT)/make/virtual_file.mk—— 加载虚拟文件规则;CHIP_MODEL := TLSR8258F512—— 告诉规则引擎“我要为TLSR8258F512芯片生成路径”;-D$(CHIP_MODEL)—— 在编译命令里定义宏,让C代码能条件编译。
2.3 执行首次编译并验证虚拟文件生效
现在,进入beacon_app/目录,执行:
make clean make -n | head -20make -n不会真正编译,只会打印出将要执行的命令。你应该看到类似这样的输出:
arm-none-eabi-gcc -c -I/home/user/telsr8258_dev/sdk_8258/application -I/home/user/telsr8258_dev/sdk_8258/driver ... -DTLSR8258F512 application/main.c -o build/application/main.o重点看-I参数后的路径——它们都是sdk_8258下的真实路径,说明INCLUDES变量已被正确展开。接着,执行真实编译:
make如果一切顺利,你会在build/目录下看到beacon_app.elf和beacon_app.bin。此时,你可以用find . -name "tlsr_common.h"验证:它应该出现在sdk_8258/common/inc/下,而不是你项目目录里。这证明虚拟文件机制已成功将SDK的公共头文件“映射”进了你的编译环境。
注意:
make -n输出中如果出现-I/home/user/telsr8258_dev/beacon_app/../tools/make/...这类路径,说明virtual_file.mk被错误地当作头文件路径加入了,这是INCLUDES变量拼写错误导致的典型问题。务必检查Makefile里INCLUDES的赋值,确保它只包含SDK路径,不包含tools路径。
3. 虚拟文件背后的Makefile魔法:解析virtual_file.mk的四个核心机制
virtual_file.mk不是一段简单的路径拼接代码,而是一个精密的构建状态机。它通过四层机制协同工作,确保SDK资源能按需、准确、无冲突地注入到编译流程中。理解这四层,是你能自主修改、调试、甚至扩展SDK的基础。
3.1 芯片型号驱动的路径模板系统
virtual_file.mk的核心是一个名为chip_model_path的函数,它接收CHIP_MODEL作为输入,返回一组预定义的路径模板。例如,当CHIP_MODEL = TLSR8258F512时,它会返回:
# chip_model_path定义(简化版) define chip_model_path $(if $(filter TLSR8258%,$(1)),\ $(SDK_ROOT)/ld/$(1)_flash.ld \ $(SDK_ROOT)/lib/$(1)/librf.a \ $(SDK_ROOT)/lib/$(1)/libble.a \ $(SDK_ROOT)/driver/$(1)/gpio.c \ $(SDK_ROOT)/stack/ble/$(1)/ble_stack.c,\ $(error Unsupported chip model: $(1))) endef这个函数的关键在于$(1)_flash.ld中的$(1)——它不是字符串拼接,而是Makefile的变量展开。TLSR8258F512_flash.ld这个文件必须真实存在于sdk_8258/ld/目录下,否则链接会失败。泰凌微的SDK里,每个芯片型号都有专属的链接脚本,因为Flash布局、RAM分配、中断向量表位置都不同。virtual_file.mk做的,就是把CHIP_MODEL这个字符串,安全地注入到路径中,避免硬编码。
3.2 符号链接的惰性生成策略
virtual_file.mk不会在make命令一开始就把所有链接创建好。它采用“按需生成”策略:只有当某个目标文件(如build/application/main.o)需要某个头文件时,才会触发链接创建。这个过程由一个叫gen_symlinks的目标控制:
# virtual_file.mk片段 gen_symlinks: $(SDK_ROOT)/common/inc/tlsr_common.h $(SDK_ROOT)/common/inc/tlsr_common.h: @echo "Creating symlink for tlsr_common.h..." @ln -sf $(SDK_ROOT)/common/inc/tlsr_common.h $@但这里有个陷阱:ln -sf命令在Windows上不可用。泰凌微官方只支持Linux/macOS开发,如果你非要在Windows上用WSL,这条命令才有效;如果用原生Windows的MinGW或Cygwin,必须替换为mklink /D。我在测试时发现,很多Windows用户卡在这里,make报错ln: command not found,却不知道该改哪里。解决方案是:在virtual_file.mk顶部添加平台检测:
ifeq ($(OS),Windows_NT) SYMLINK_CMD := cmd /c mklink /D else SYMLINK_CMD := ln -sf endif然后把所有ln -sf替换成$(SYMLINK_CMD)。这个细节,官方文档从没提过,但却是Windows开发者绕不开的坎。
3.3 头文件搜索路径的动态拼接
INCLUDES变量的值,不是静态字符串,而是由virtual_file.mk动态计算的。它会扫描SDK_ROOT下的application/,driver/,stack/子目录,并根据CHIP_MODEL过滤出有效的路径。例如,driver/目录下有tlsr8258/,tlsr8278/,common/三个子目录,virtual_file.mk会自动把$(SDK_ROOT)/driver/common和$(SDK_ROOT)/driver/tlsr8258加入INCLUDES,而忽略tlsr8278。这个逻辑藏在get_inc_paths函数里:
define get_inc_paths $(foreach dir,$(1),\ $(if $(wildcard $(SDK_ROOT)/$(dir)/$(CHIP_MODEL)),\ $(SDK_ROOT)/$(dir)/$(CHIP_MODEL) \ $(SDK_ROOT)/$(dir)/common,\ $(SDK_ROOT)/$(dir)/common)) endef$(wildcard ...)是Makefile的文件存在性检查函数。它确保只有当$(SDK_ROOT)/driver/$(CHIP_MODEL)目录存在时,才把$(CHIP_MODEL)专属路径加入搜索列表;否则,只加common路径。这保证了代码的向前兼容性——即使你用的是旧版SDK,没有tlsr8258子目录,也能退化到common路径编译。
3.4 链接库的条件加载与版本仲裁
最后,virtual_file.mk还负责解决“多个同名库如何选择”的问题。比如,libble.a在sdk_8258/lib/tlsr8258/和sdk_8258/lib/common/下都存在。virtual_file.mk的规则是:芯片专属库优先于通用库。它通过LDFLAGS变量的构造实现:
LDFLAGS += -L$(SDK_ROOT)/lib/$(CHIP_MODEL) -L$(SDK_ROOT)/lib/common LDFLAGS += -lble -lrf -lc -lm-L参数的顺序决定了链接器搜索库的优先级。-L$(SDK_ROOT)/lib/$(CHIP_MODEL)排在前面,所以链接器会先在lib/tlsr8258/下找libble.a,找不到才去lib/common/下找。这个顺序不能颠倒,否则芯片专属优化就会失效。我曾经把-L顺序写反,结果BLE广播功率比预期低3dB,花了两天才定位到这个链接顺序问题。
实操心得:当你新增一个外设驱动(比如SPI Flash),不要直接把
.c文件扔进driver/目录。正确的做法是:在driver/下新建spi_flash/目录,把代码放进去,然后在virtual_file.mk的get_inc_paths函数里,把spi_flash加入$(1)参数列表。否则,INCLUDES里不会包含这个路径,编译器永远找不到你的头文件。
4. 项目编译失败的四大高频场景与根因级排查链路
即使你严格按照前述步骤配置了环境,编译失败仍是常态。泰凌微TLSR8258的构建系统极其敏感,一个空格、一个换行符、一个路径里的大小写错误,都会导致完全不同的错误信息。下面是我踩过的四个最典型、最隐蔽的坑,以及完整的排查链路——不是直接告诉你答案,而是展示我是如何一步步锁定根因的。
4.1 场景一:“No rule to make target 'xxx.o'” —— 源文件路径拼写错误的连锁反应
现象:执行make后,报错make: *** No rule to make target 'application/main.o', needed by 'beacon_app.elf'. Stop.。看起来像是Makefile里没定义main.o的规则,但你明明写了$(BUILD_DIR)/%.o: %.c。
排查链路:
- 确认文件物理存在:
ls -l beacon_app/application/main.c—— 确保文件真实存在,且权限为可读。 - 检查Makefile中的SOURCES变量:
echo $(SOURCES)—— 在Makefile末尾加一行$(info SOURCES = $(SOURCES)),重新make。你会发现输出是SOURCES = application/main.c driver/gpio.c,但main.c的路径其实是beacon_app/application/main.c,而Makefile里写的是application/main.c,相对路径起点错了。 - 定位路径基准点:Makefile里的相对路径,基准点是
Makefile所在目录,即beacon_app/。所以application/main.c是正确的,但main.c前面不能加./或beacon_app/。 - 终极验证:在
beacon_app/目录下执行find . -name "main.c",确认输出是./application/main.c。如果输出是./beacon_app/application/main.c,说明你把整个beacon_app目录又嵌套了一层,这是最常见的目录结构错误。
根因:SOURCES变量里的路径,必须相对于Makefile所在目录,而不是相对于项目根目录或SDK目录。这是一个纯粹的Makefile语法问题,和TLSR8258无关,但新手极易混淆。
4.2 场景二:“undefined reference to 'xxx'” —— 链接器找不到符号的三重陷阱
现象:编译通过,但链接时报错undefined reference to 'bls_ll_init'。这个函数明明在stack/ble/ble.c里定义了,为什么链接器找不到?
排查链路:
- 确认函数是否被条件编译屏蔽:打开
stack/ble/ble.c,查找bls_ll_init,发现它被包裹在#if (MCU_CORE_TYPE == MCU_CORE_TL8258)宏里。而你的project_config.h里定义的是MCU_CORE_TL8258,看起来没问题。 - 检查宏定义是否被覆盖:在
Makefile里搜索-DMCU_CORE_TYPE,发现没有这一项。这意味着MCU_CORE_TYPE宏根本没被定义,#if条件为假,函数被预处理器剔除了。 - 追溯宏定义源头:查看
sdk_8258/common/inc/tlsr_common.h,发现它要求MCU_CORE_TYPE必须在application/main.c的最顶部#include之前定义。而你的main.c里,#include "tl_common.h"是第一行,MCU_CORE_TYPE定义在后面。 - 修复方案:在
main.c顶部,#include之前,添加#define MCU_CORE_TYPE MCU_CORE_TL8258。或者,更规范的做法是,在Makefile的CFLAGS里添加-DMCU_CORE_TYPE=MCU_CORE_TL8258。
根因:TLSR8258的代码大量使用条件编译,而宏定义的传递顺序,比函数声明更重要。链接失败,往往不是代码没写,而是预处理器根本没让它编译进去。
4.3 场景三:“'xxx' undeclared here” —— 头文件包含顺序引发的类型未定义
现象:编译报错'GPIO_PIN_0' undeclared here。GPIO_PIN_0定义在driver/gpio.h里,而你的代码里已经#include "gpio.h"了,为什么还报错?
排查链路:
- 检查头文件包含顺序:打开
main.c,发现#include "gpio.h"在#include "tl_common.h"之后。而tl_common.h里又#include "types.h",定义了基本类型。 - 查看
gpio.h的依赖:打开driver/gpio.h,发现第一行是#include "types.h",但types.h不在driver/目录下,而在common/inc/下。 - 验证
INCLUDES路径:执行make -n | grep -E "(-I|gcc)",确认-I参数里是否包含了$(SDK_ROOT)/common/inc。如果没有,说明virtual_file.mk的get_inc_paths函数没把common路径加进来。 - 定位
get_inc_paths调用点:在virtual_file.mk里搜索get_inc_paths,发现它被调用时传入的参数是application driver stack,但没传common。于是手动在Makefile里把common/inc加到INCLUDES里:INCLUDES += $(SDK_ROOT)/common/inc。
根因:virtual_file.mk的路径生成逻辑,是基于目录名匹配的。common目录名不在application driver stack列表里,所以它被忽略了。这不是bug,而是设计——你必须显式告诉它,哪些目录需要被纳入头文件搜索。
4.4 场景四:“section '.text' will not fit in region 'FLASH'” —— Flash溢出的隐性诱因
现象:编译链接成功,但生成的.bin文件大小超过512KB,烧录后芯片不启动。map文件显示.text段超出FLASH区域。
排查链路:
- 检查
map文件:arm-none-eabi-gcc -Wl,-Map=beacon_app.map生成beacon_app.map,用文本编辑器打开,搜索Memory Configuration,确认FLASH起始地址和长度。 - 分析
.text段组成:在map文件里搜索.text,发现libble.a占了320KB,远超预期。而官方Demo里libble.a只有240KB。 - 对比SDK版本:
sdk_8258/stack/ble/目录下,libble.a的修改时间是昨天,而官方v4.3.0的libble.a是三个月前的。说明你本地的libble.a是自己编译生成的,不是SDK自带的。 - 追溯生成源头:
sdk_8258/stack/ble/Makefile里有一行$(AR) rcs libble.a $(OBJECTS),它把所有.o文件打包成libble.a。而你的OBJECTS列表里,包含了ble_debug.c,这个文件在Debug模式下会注入大量日志代码,使库体积暴增。 - 修复方案:在
stack/ble/Makefile里,把ble_debug.c从OBJECTS里移除,或者用#ifdef DEBUG条件编译它。
根因:SDK里的静态库,是预编译好的“成品”。一旦你修改了源码并重新编译了libble.a,它的体积和行为就不再受控。Flash溢出,往往不是代码写多了,而是你无意中开启了某个调试开关,让编译器注入了额外代码。
踩坑总结:每次编译失败,我都习惯先执行
make -n,把即将执行的命令完整打印出来,然后逐字核对。90%的问题,都能在-n输出里找到线索——多了一个空格、少了一个反斜杠、路径里用了大写TLSR而SDK里是小写tlsr。不要急着谷歌错误信息,先让make自己告诉你它想做什么。
5. 进阶技巧:定制化虚拟文件与跨芯片项目复用
当你已经能稳定编译TLSR8258项目后,下一步就是提升开发效率。泰凌微SDK的虚拟文件机制,远不止于“让编译通过”这么简单。它是一套可编程的构建框架,允许你做三件事:定制芯片专属配置、复用代码到其他芯片、甚至集成第三方库。这些能力,官方文档几乎不提,但却是资深开发者的核心竞争力。
5.1 创建芯片专属的project_config.h,解耦硬件差异
官方Demo里,所有芯片相关的配置(时钟频率、Flash大小、外设引脚)都硬编码在application/main.c里。这导致你每换一个芯片,就要改一堆数字。更好的做法是,为每个芯片创建独立的project_config.h:
// sdk_8258/config/tlsr8258f512/project_config.h #ifndef __PROJECT_CONFIG_H__ #define __PROJECT_CONFIG_H__ // Flash size: 512KB #define FLASH_SIZE (512 * 1024) #define FLASH_PAGE_SIZE 2048 // System clock: 48MHz #define SYSTEM_CLOCK 48000000 // GPIO mapping #define LED_GPIO GPIO_PC0 #define BUTTON_GPIO GPIO_PD1 // BLE settings #define BLE_MAX_CONNECTION 1 #define BLE_ADV_INTERVAL 160 // 100ms #endif然后,在Makefile里,把-I$(SDK_ROOT)/config/$(CHIP_MODEL)加入INCLUDES。这样,你的main.c里只需要写:
#include "project_config.h" ... clock_init(SYS_CLK_48M); gpio_set_up(LED_GPIO, AS_GPIO, 0);SYS_CLK_48M和AS_GPIO这些宏,都在project_config.h里定义好了。未来你要支持TLSR8278,只需新建sdk_8258/config/tlsr8278/project_config.h,修改FLASH_SIZE和SYSTEM_CLOCK,其他代码完全不用动。这就是虚拟文件带来的“配置即代码”能力。
5.2 构建跨芯片的统一项目结构
假设你有一个产品线,同时用TLSR8258做BLE Beacon,用TLSR8278做Zigbee Router。你不想维护两套完全独立的代码,而是希望90%的业务逻辑(比如传感器数据处理、OTA升级协议)是共享的。虚拟文件机制可以帮你做到:
shared/ ├── sensor/ │ ├── temp_sensor.c │ └── temp_sensor.h ├── ota/ │ ├── ota_core.c │ └── ota_core.h └── common.h beacon_8258/ ├── Makefile # CHIP_MODEL := TLSR8258F512 ├── application/ │ └── main.c # #include "../shared/sensor/temp_sensor.h" └── config/ # 链接到 sdk_8258/config/tlsr8258f512/ router_8278/ ├── Makefile # CHIP_MODEL := TLSR8278F512 ├── application/ │ └── main.c # #include "../shared/sensor/temp_sensor.h" └── config/ # 链接到 sdk_8258/config/tlsr8278/关键在于Makefile里的INCLUDES:
# beacon_8258/Makefile INCLUDES := \ $(SDK_ROOT)/application \ $(SDK_ROOT)/driver \ $(SDK_ROOT)/stack \ $(abspath ../shared) \ $(abspath ./config)$(abspath ../shared)把shared/目录的绝对路径加入搜索列表,temp_sensor.h就能被main.c直接#include。而./config链接到芯片专属配置,保证了硬件相关代码的隔离。这样,shared/目录下的代码,一次编写,处处编译。
5.3 集成第三方库:以 cJSON 为例的无缝接入
很多项目需要JSON解析,而TLSR8258的Flash空间有限,不能直接用libc的printf。cJSON是一个轻量级选择,但它需要被编译进你的固件。虚拟文件机制可以把它“伪装”成SDK的一部分:
- 下载
cJSON源码,放到beacon_app/third_party/cjson/目录下。 - 在
beacon_app/Makefile里,扩展SOURCES和INCLUDES:
SOURCES += \ third_party/cjson/cJSON.c INCLUDES += \ $(abspath ./third_party/cjson)- 创建
beacon_app/third_party/cjson/Makefile,内容为空(因为cJSON.c会被主Makefile直接编译)。 - 在
main.c里,#include "cJSON.h"即可。
这样,cJSON就像SDK自带的库一样,被统一编译、链接。你甚至可以把它放进shared/目录,让所有项目共享。虚拟文件的本质,就是让构建系统“相信”某些路径是SDK的一部分,而不管它们物理上在哪里。
最后分享一个小技巧:我给自己写了一个
sdk-sync.sh脚本,每次git pullSDK后自动执行,它会检查sdk_8258/ld/目录下是否有$(CHIP_MODEL)_flash.ld,如果没有,就从sdk_8258/ld/template.ld复制一份并替换芯片名。这样,即使SDK更新了链接脚本模板,我的项目也能自动适配,不用手动改。自动化,才是对抗复杂性的终极武器。