首页 / 资讯中心 / 文章详情

IAR报错排查实战:从错误码分诊到链接、授权与RTOS移植

IAR报错排查实战:从错误码分诊到链接、授权与RTOS移植 ★ FEATURED ARTICLE
1. IAR报错处理的第一原则别从最后一条错误开始读刚上手 IAR Embedded Workbench 的人十有八九会犯同一个毛病编译失败Build 窗口一拉到底盯着最底下那条红色的 Error 开始搜。搜半天没结果最后发现在输出窗口往上翻二十行真正的根因早就写在那里了。我自己也这么干过整整一年直到有一次被一个Error[Li005]折腾了一下午才发现罪魁祸首是三条警告之前的一个头文件路径写错了。IAR 的编译流程大致是预处理器展开、编译器逐个翻译单元编译、汇编器处理.s文件、链接器把所有目标文件和库拼在一起。每一个阶段都有自己的错误编号前缀这个前缀就是最便宜的诊断线索。Pe开头的多半是编译器Parser/Compiler抛的比如Error[Pe020]、Fatal Error[Pe1696]Li是 Linker典型的是Error[Li005]Lp是 Linker 的段放置PlacementError[Lp011]最常见Lc跟链接器配置文件.icf的语法有关LMS是 License Management System也就是授权系统Be通常是构建工具链本身或者命令行调用出的问题。搞清这一层映射等于先把报错范围砍掉一大半。真正麻烦的是报错位置和根因位置不一致。举个例子某个.c文件里写了一行#include app_config.h但这个头文件被别人删了编译器会先报一条Fatal Error[Pe1696]: cannot open source file然后紧接着对这个文件里所有用到app_config.h中宏的地方报一串identifier is undefined。后面那几十条错误全是噪声修好第一条就全消失了。所以我的习惯是Build 窗口从上往下看找到第一个Fatal级别的错误先只解决它其他一律当噪声忽略。IAR 遇到致命错误会跳过当前文件继续编下一个所以后面的错误既可能是连锁反应也可能是完全独立的第二个问题得先排除掉连锁反应才看得清。下面这张表是我自己在项目里沉淀下来的快速分诊表平时对着它扫一遍基本能定位到该去哪个配置页面翻东西报错前缀/关键词所属阶段首要排查位置典型成因LMS001 / license授权IAR License Manager授权未激活、主机标识变化、版本与授权不匹配Pe1696 / cannot open source file预处理Options → C/C Compiler → Preprocessor头文件路径缺失、$PROJ_DIR$用法错误Pe020 / identifier is undefined编译头文件与宏定义头文件没包含、条件编译宏未开、拼写错误Li005 / no definition链接工程 Group 与库配置源文件未加入工程、库未链接、函数名不匹配Lp011 / section placement failed链接.icf文件与器件型号选错芯片、RAM/ROM 区间写错、大数组占用 RAMLc / config error链接配置.icf文件语法.icf表达式写错、region 定义冲突The generation feature is not of version工程打开.ewp版本号工程由更高版本 IDE 创建连接/下载失败调试Debugger → Setup → Driver仿真器驱动选错、SWD 引脚被复用、Flash loader 不匹配再补一句关于环境的小经验同一个 IAR 安装目录下可以并存多个产品线比如 EWARMARM 内核、EW80518051 内核CC2530 属于这一类、EWSTM8、EWAVR它们各自有独立的可执行文件、独立的插件目录甚至独立的授权。很多人第一次拿到 CC2530 的例程打不开以为软件坏了其实是装了 EWARM 却拿它去开 8051 工程这个后面单独说。2. 授权与许可类报错Fatal Error[LMS001]的完整处理链路Fatal Error[LMS001]: License check failed. Use the IAR License Manager to resolve the problem.这条报错在搜索引擎里被问得最多因为它出现得毫无征兆——昨天还好好的今天打开就报甚至刚装完第一次编译就报。它跟代码一点关系都没有纯粹是 IAR 在启动构建之前做的一次授权校验没通过。授权校验这件事IAR 做得比较严格。它在安装时会生成一个绑定当前机器的标识通常跟网卡物理地址、主板信息、磁盘卷序列号这类硬件指纹相关然后用这个标识去激活一份授权。校验的内容包括授权文件是否存在且未过期、授权类型是否覆盖当前使用的产品线、绑定的硬件标识是否和当前机器一致、授权的节点数是否已被占满。任何一项不满足都会在编译开始前直接拦截连一个.c文件都不会编。处理这条报错正规路径只有一条打开 IAR License Manager看清楚当前状态再按状态决定下一步动作。我习惯按这样的顺序走启动 IAR License Manager在开始菜单里能搜到或者从 IAR 的 Help 菜单进入。查看列表里有没有已激活的授权以及它对应的产品线和到期时间。如果列表是空的说明激活信息丢了需要重新走一次激活流程。如果有授权但状态异常先检查系统时间。授权校验对系统时间很敏感如果系统时间被改到了授权生效日之前或者干脆错到了几年后会直接判定授权无效。这个坑我踩过一次主板电池没电导致时间回到出厂值折腾了半天才发现。如果提示绑定的标识不匹配说明硬件指纹变了。换主板、换网卡、把系统盘插到另一台机器都会触发这种情况。解决办法是走一次重新激活把授权重新绑到当前机器上。如果提示节点数已满多半是之前的机器没有正确释放授权。这种情况需要联系授权管理员在管理端释放掉旧节点。要特别说一句网上流传着大量所谓密钥生成器注册补丁一键激活工具我的建议是碰都别碰。第一这类东西在合规层面站不住脚商业项目里用会带来实打实的法律风险第二它们往往通过替换 IAR 安装目录下的授权相关文件来起作用版本一升级就失效还会把原本正常的安装搞得乱七八糟最后连正版授权都激活不上去只能整个卸载重装。我在上一家公司见过同事这么干结果重装加清理注册表花了小半天。还有一种容易被误判成授权问题的情况报错发生在链接阶段提示找不到某个库或者某个运行时组件。这本质上不是 LMS 报错而是授权类型不覆盖对应的功能模块比如某些高级优化选项、某些特定内核的支持包报错文本里会提到具体的 feature 名称。这种时候要看清楚报错里的功能名再去核对授权覆盖范围而不是盲目重装。顺便说一下多版本共存的处理。很多人机器上同时装着 EWARM 的 8.x 和 9.x或者同时装着 EWARM 和 EW8051。每个大版本各自管理自己的授权License Manager 里能看到多条记录。我的建议是不要为了省事把老版本的授权文件拷到新版本的目录下格式和校验规则可能不兼容轻则报错重则让新版本也起不来。要升级就老老实实走一次激活流程。3. 编译期报错头文件路径、宏定义与未定义标识符三件套Pe系列错误是日常遇到最多的一类其中Fatal Error[Pe1696]: cannot open source file xxx.h又是最高频的。表面上看这是文件找不到实际上有五种截然不同的成因需要分别处理。第一种路径压根没加。IAR 的头文件搜索路径在Project → Options → C/C Compiler → Preprocessor这一页的Additional include directories里配置。注意这里填的每一条路径基准目录是工程文件.ewp所在目录而不是.c文件所在目录。这个规则非常关键很多人习惯性地写相对路径..\inc结果发现只有放在工程同级的文件能找到下层目录里的全丢。正确写法是用内置变量比如$PROJ_DIR$\..\..\middlewares\inc这样无论工程文件被挪到哪里路径都不会断。第二种大小写和分隔符问题。Windows 文件系统不区分大小写所以#include GPIO.h能找到gpio.h一切正常。但如果你在 Linux 构建机或者区分大小写的文件系统上编译同一份代码立刻报文件不存在。同理反斜杠\在字符串里有转义含义#include hardware\led.h在某些预处理实现下会被解释成带转义字符的路径。统一用正斜杠\改成/是最省心的做法IAR 在 Windows 上同样接受正斜杠。第三种路径里有空格或中文。IAR 对包含空格和中文的路径兼容性时好时坏尤其是当工程放在桌面或者我的文档这类带中文的默认目录下时。我见过一个案例工程路径里有个中文目录名单个头文件包含没问题但一旦用了批处理脚本调用iarbuild就疯狂报错。所以我的习惯是任何嵌入式工程都放在纯英文、无空格的短路径下比如D:\work\stm32_f103\proj这个习惯能省掉大量玄学问题。第四种文件真的不存在。比如从别人那里拷来的工程只带了源码没带Inc目录或者从 Git 上拉下来忘了初始化子模块中间件目录是空的。这种时候报错路径会明显指向一个中间件名字一眼能看出来。第五种宏开关把整个头文件的内容吃掉了。这种情况比较隐蔽文件明明存在也不报cannot open但所有接口都报未定义。原因是头文件的实体被#ifdef包了起来而对应的宏没定义。典型的像 HAL 库的stm32f1xx_hal_conf.h里面有几十个#define HAL_XXX_MODULE_ENABLED注释掉哪一个模块哪个模块的接口就全部消失。Error[Pe020]: identifier xxx is undefined也是同理。看到这条错误我的排查顺序是报错的标识符是不是标准类型uint8_t、int32_t这类需要#include stdint.hbool、true、false需要#include stdbool.hsize_t需要#include stddef.h。裸工程不包含任何头文件时这些全都不认识。是不是自定义类型结构体、枚举、typedef都在头文件里忘了包含就是一堆 undefined。头文件包含了但被条件编译屏蔽了检查该头文件顶部的宏保护。是不是在.c文件里定义了函数却没在头文件里声明然后在别的文件里调用C 语言默认不允许隐式声明C99 之后严格禁止这时报的也是 undefined。拼写错误。别笑SysTick_Handler和SysTickHandler差一个下划线编译器不会给你任何提示只报找不到。关于警告我想多说一句。IAR 的警告分级可以通过Project → Options → C/C Compiler → Diagnostics调整很多人嫌烦直接全关。我的做法恰恰相反把警告开得比较全然后把较真的警告逐条处理。原因是嵌入式代码里的警告往往指向真实缺陷比如有符号无符号比较、隐式类型截断、未使用的变量、switch 缺 default 分支。这些问题在 PC 上顶多算出错在 MCU 上可能直接导致数据错乱或者栈溢出。我养成的习惯是每次接入一份新代码先把警告清零再开始做功能这样后面出问题时心理负担小很多。4. 链接期报错Li005、Lp011 与 .icf 链接文件的段放置编译全部通过不代表能出固件。链接阶段是 IAR 报错里最考验理解深度的一块也是很多人第一次接触.icf文件的地方。Error[Li005]: no definition for 函数名 [referenced from xxx.o]的含义是某个目标文件引用了这个符号但链接器在所有输入里找不到它的定义。常见成因有四个。一是源文件没加进工程比如FreeRTOS的tasks.c、queue.c忘了拖进 Group编译阶段不会报错因为没有文件引用它们的内部实现到链接才炸。二是库文件没链接比如用了sinf()却忘记在Linker → Library里勾选数学库或者用的是精简版库不含浮点函数。三是条件编译导致的实现缺失比如某个函数体被#if (configUSE_TRACE_FACILITY 1)包着而配置里是 0。四是 C 和 C 混编时忘了extern C导致符号名被 C 的 name mangling 破坏链接器找不到对应名字。Error[Lp011]: section placement failed是另一类高频错误报错文本会带上一个总大小和一个可用区间比如提示有若干字节无处安放。这条错误的本质是链接器手里的.icf文件定义了各个段可以放在哪些内存区间而某个段的实际占用量超过了区间容量。排查方向有这么几个器件型号选错。IAR 的Project → Options → General Options → Target里选的芯片决定了预定义的内存大小。选成 RAM 更小的型号或者选成了 Flash 只有 64K 的版本而不是 128K 的版本链接一定会失败。这个错误很容易被忽略因为报错信息只谈容量不谈型号。常量数组没加 const。一个几百字节的查表数组如果声明成uint8_t table[] {...}而不加const编译器会把它放到.data段也就是既占 Flash 又占 RAM。加个const它就只待在 Flash 里了通过__flash或者直接 const 修饰即可。这是真实项目中省 RAM 最立竿见影的一招。栈和堆开得太大。.icf里通常有__ICFEDIT_size_heap__和__ICFEDIT_size_stack__两个符号直接决定堆栈预留空间。默认值有时是 8K 或者更大小容量芯片上直接吃掉一半 RAM。.icf里的区间写错了。手工改过链接文件的话region 的起止地址、block的 size 表达式都可能是罪魁祸首。Lc开头的错误一般就是.icf的语法问题。现在说回一个具体的例子这个语法在很多代码库里能见到uint8_t ucheap[4096] __section(.heap) {0};这行的意图是把一个 4K 的数组强制放进名为.heap的段。__section(...)是 IAR 提供的段放置修饰符旧版本里还有 section_name的写法。问题在于.heap这个名字在 IAR 的默认链接配置里是被 C 库运行时已经占用的段——它对应__ICFEDIT_size_heap__定义的那块区域。你手动往里塞一个 4K 数组等于声称这块内存既归库管又归你管链接器要么报 placement 冲突要么在运行时出现堆区和你的数组重叠表现就是malloc 之后某个全局数组莫名被改写这种极其难查的故障。正确的做法是自己定义一个专属段然后让.icf明确分配它的位置。.icf里加define symbol __MY_HEAP_start__ 0x20004000; define symbol __MY_HEAP_end__ 0x20004FFF; define region MY_HEAP_region mem:[from __MY_HEAP_start__ to __MY_HEAP_end__]; place in MY_HEAP_region { section MY_HEAP };然后 C 文件里这么写#pragma section MY_HEAP uint8_t ucheap[4096] MY_HEAP {0};这里要注意两个细节。第一#pragma section后面的名字和后面的名字必须完全一致大小写敏感。第二.icf里section MY_HEAP的名字也要一致且不带点号。如果你的工程里在别处已经有同名段链接器会合并它们所以命名最好带项目前缀避免撞车。排查链接问题时我还依赖一个工具Project → Options → Linker → List里可以生成.map文件。把这个文件打开能看到每个段被放在了哪个地址、占了多少字节、还剩多少。Lp011 报错时我第一件事就是翻.map的最后几行那里通常会列出一个总需求和可用空间的对比一眼就能看出是哪个段撑爆了。5. 器件支持与插件类报错CC2530、STM8、GD Addon 的坑这一类报错不来自代码来自工具链配置和芯片对不对得上。先讲一个高频疑问打开别人的工程弹出一句 The generation feature is not of version 18。这句话的含义是工程文件.ewp/.eww里记录的格式版本号高于当前 IDE 支持的版本说白了就是这个工程是用比我新的 IAR 建的我打不开。IAR 的工程文件是 XML你可以用文本编辑器打开.ewp在state节点下能看到类似state$version$/state或者具体的版本号字段。反过来的情况也存在用新版本打开老工程通常能正常打开只是首次保存后会提示工程格式升级。这句话的处理办法没什么技巧就三条路升级到创建该工程的 IAR 版本或更高让对方用低版本 IDE 重新导出一份工程或者手工把.ewp里的版本号字段改成当前版本支持的数值——第三条我试过简单工程能蒙混过关但只要工程里用了高版本才有的配置项比如新的优化选项、新的段放置语法打开后照样会在编译时报一堆莫名其妙的错所以不推荐作为常规手段。再说产品线混用。IAR 的产品线是按内核划分的EWARM 管 ARM Cortex-M/A/REW8051 管 8051 内核EWSTM8 管 STM8EWAVR 管 AVREWRL78 管瑞萨。它们共用一部分安装框架但编译器、汇编器、链接器、调试驱动全是独立的。CC2530 是 8051 内核必须用 EW8051STM8 必须用 EWSTM8STM32 用 EWARM。你装了 EWARM双击 CC2530 的.eww系统可能用 EWARM 去尝试打开然后报器件找不到或者工程格式不识别。正确做法是右键选择用 EW8051 打开或者干脆从 EW8051 的启动界面里选File → Open → Workspace。然后是插件目录。IAR 安装根目录下有一个common\bin\Plugins之类的路径里面放着供 IDE 调用的各种扩展比如调试探针的接口组件、器件厂商提供的支持文件。这个目录平时不需要动但如果杀毒软件误删了里面的文件会表现为某个仿真器突然找不到了或者某项功能报初始化失败。遇到这类报错先检查这个目录的完整性必要时覆盖安装修复。GD Addon 是兆易创新提供的器件支持包解决的是 IAR 原生器件库里没有 GD32 系列的问题。安装它要注意几点从官方渠道下载对应 IAR 版本的安装包安装程序需要指定 IAR 的安装根目录安装完成后必须重启 IAR 才能在Options → General Options → Target → Device的下拉列表里看到 GD32 的型号。我见过有人装完没重启就去找器件然后以为安装失败来回装了三遍。另一个常见问题是版本不匹配Addon 的版本号和 IAR 大版本之间是有对应关系的装错了会出现器件列表里有 GD32但一编译就报找不到启动文件或者找不到链接脚本因为 Addon 里自带的.icf和.s文件没有被正确注册。顺带提一个操作系统移植相关的坑。很多人从 ST 的官方例程或者 GitHub 上拷来一份 RT-Thread、FreeRTOS 工程用 IAR 打开后发现启动文件报一堆语法错误形如bad instruction、unexpected token。原因很简单那份启动文件是给 GCC 写的用的是 GNU 汇编语法而 IAR 用的是自己的汇编器语法完全不同IAR 用PUBLIC、SECTION、THUMB、REQUIRE8这类伪指令GCC 用.global、.section、.syntax unified之类。这种情况下不要试图改那份文件正确做法是从 IAR 的安装目录或者 CubeMX 里找 IAR 版本的启动文件替换掉省时得多。6. 移植 FreeRTOS 与 RT-Thread 时的报错集中区实时操作系统移植是 IAR 报错的另一个高发场景因为涉及到汇编、链接脚本、中断向量三者的配合。第一类中断向量重复定义。FreeRTOS 的port.c里通常定义了几个处理函数比如vPortSVCHandler、xPortPendSVHandler、xPortSysTickHandler。同时芯片的启动文件里默认已经定义了SVC_Handler、PendSV_Handler、SysTick_Handler这几个弱符号。如果不在FreeRTOSConfig.h里做映射链接器会看到两套实现报重复定义更隐蔽的情况是链接器挑了一个而你想用的是另一个编译链接都过了但一调度就进 HardFault。标准做法是在FreeRTOSConfig.h里加上这几行#define vPortSVCHandler SVC_Handler #define xPortPendSVHandler PendSV_Handler #define xPortSysTickHandler SysTick_Handler这样port.c里定义的名字就会被宏替换成启动文件里的名字正好覆盖掉弱定义。第二类堆空间告急。FreeRTOS 自带heap_1.c到heap_5.c五个内存管理实现它们自己维护一块静态数组作为堆。这块数组的大小由configTOTAL_HEAP_SIZE控制默认值往往偏大比如 17K。在 STM32F103C8T6 这种只有 20K RAM 的芯片上17K 的堆加上栈、加上 BSS 段Lp011 报错几乎是必然的。这时候要么把configTOTAL_HEAP_SIZE砍到 4K 到 6K要么改用heap_4.c并配合更精细的内存规划。注意这里的堆跟 IAR 的.icf里那个__ICFEDIT_size_heap__是两回事如果你同时用 C 库的malloc和 FreeRTOS 的pvPortMalloc那就是两块独立内存非常容易算错总量。第三类__section相关的自定义段。上一节讲的__section(.heap)那种写法在 RTOS 移植代码里偶尔能见到多半是为了把某个内存池放到指定位置。处理思路和前面一样定义自己的段名在.icf里明确放置不要跟系统保留段重名。第四类编译选项不匹配。移植 RT-Thread 时经常遇到Error[Pe147]之类的错误指向内联汇编或者特定语法。这是因为 RT-Thread 的port.c里大量使用了行内汇编和编译器特定的扩展语法不同编译器的写法不一样。RT-Thread 的代码库通常分libcpu/arm/cortex-m3/iar/和gcc/两个目录必须选 iar 目录下的那份。选错了就是一堆语法错误。第五类宏配置冲突。FreeRTOSConfig.h和rtconfig.h里的宏如果和工程里其他地方定义的宏撞车会报重复定义警告某些情况下升级为错误。典型的是USE_HAL_DRIVER、STM32F103xB这类芯片相关的宏不同来源的头文件可能会重复定义用#undef处理掉或者统一到一处定义就行。我在移植 RTOS 时有个固定的验证流程分享给经常踩这块的同行第一步先只让内核跑起来任务里什么都不做只有一个空循环加一个延时确认调度器能切换第二步加串口输出和printf重定向确认中断能正常响应第三步加外设驱动。分三步走的好处是每一步只引入一个新的变量出问题时排查范围极小。很多人一上来就把所有驱动都塞进去结果在 HardFault 里挣扎一整天。7. 调试下载阶段仿真器找不到、Flash loader 报错的处理代码编过了、链接过了、固件生成了点下载却报错这一段的报错文本跟前面完全不同属于硬件连接和调试配置的范畴。最常见的一条是找不到调试探针或者无法连接到目标。排查顺序我是这样走的。先看Project → Options → Debugger → Setup里的Driver选对没有——机器上插着 ST-Link驱动却选成 J-Link那当然找不到。改完之后Download页签里的Flash loader也要跟着对应因为不同探针支持的目标器件列表不一样。再往下看接口类型SWD 还是 JTAG引脚映射对不对速度是不是设得太高长杜邦线加高速率通信会不稳定降到 1MHz 试试。最后看目标板供电很多初学者忘了给板子上电或者用探针供电但电流不够一连接就掉。还有一类很容易让人怀疑硬件的现象第一次能下载之后就连不上了。这通常是因为程序里把 SWD 用的引脚复用成了普通 GPIO。STM32 上 PA13/PA14 默认是 SWDIO/SWCLK如果程序初始化时把它们配置成普通输出下载完复位运行后调试接口就被程序抢走了。解决办法是在下载配置里勾选连接时复位或者在复位后暂停实际 IAR 里对应Debugger → Reset里的选项选择在复位状态下建立连接。另一个办法是手工把板子置于复位状态按住复位键点击下载在连接建立的一瞬间松开。这招虽然土但屡试不爽。Flash 相关的报错还有几种提示芯片被读保护需要先全片擦除提示 Flash loader 加载失败一般是Flash loader路径下的.flash文件损坏或者跟器件不匹配重新选一次器件就好提示校验失败可能是写进去的数据和读出来的不一致这时候要怀疑时钟配置是否正确、Flash 等待周期是否设置合理。调试器还有一个跟工程配置强相关的坑优化等级太高导致单步调试时断点乱跳、变量看不见。IAR 默认在 Debug 配置下是低优化但有些人为了对比性能会把 Release 配置拿去调试。高优化下变量可能被完全优化掉或者合并static变量的值可能永远显示为上一次刷新。这时候如果看到一个变量在单步过程中数值诡异变化别急着怀疑代码逻辑先确认当前用的是哪个 configuration以及优化等级是不是High (Balanced)或更高。8. 几个我长期坚持的习惯能省下大量排查时间报错处理这件事本质上是一个信息检索和假设验证的过程。工具再好也比不上几条刻进肌肉记忆的习惯。第一条新环境先跑最小可运行工程。拿到一块新板子或者在新电脑上装完 IAR我做的第一件事永远是新建一个空工程配好器件型号写一个空的main加一个死循环编译、下载、点灯。这一步不涉及任何中间件和第三方代码纯粹验证工具链、授权、探针、Flash 算法这条链路是通的。链路通了之后后面报的每一个错都可以归因到代码本身不用再怀疑环境。这一步看起来浪费时间实际上是我用过的最高效的环境校准手段。第二条把 Build 日志存成文件。IAR 的 Build 窗口可以右键保存输出我习惯每次遇到难缠的报错就把日志存下来文件名带上日期和一句简述比如20240312_lp011_ram_overflow.log。存多了之后会发现同一类问题在不同的项目里反复出现直接翻旧日志比重新搜一遍快得多。命令行构建的情况下更简单iarbuild.exe app.ewp -build Debug -log warnings build_debug.log 21把日志丢进全文搜索或者用脚本统计各类错误码出现的频次能快速判断这次是老问题复发还是新问题。第三条路径全部用$PROJ_DIR$打头。这个习惯解决的是工程共享问题。用相对路径和绝对路径混着写换台机器、换个盘符就全军覆没。统一用$PROJ_DIR$工程放在哪里都能编。第四条把每次报错的成因和解决方式记进一个 Markdown 文件。我的这个文件现在有三十多条从授权绑定到.icf段冲突从启动文件语法到 SWD 引脚复用。写的时候多花两分钟下次遇到同类问题能省半小时。这个文件比任何论坛帖子都管用因为它记录的恰好是我自己踩过的坑。第五条遇到玄学报错时先做一次完整重建。IAR 的增量编译偶尔会因为依赖关系判断失误出现改了头文件但没重新编译相关源文件的情况表现就是报错指向的代码明明已经改好了。Project → Rebuild All一遍如果问题消失那就是增量编译的依赖缓存问题如果问题依然存在才值得深入分析。这个动作只需要几秒钟却能排除掉相当一部分假故障。最后再补一个容易被忽略的细节IAR 的工程配置里有个Project → Options → Build Actions可以配置编译前和编译后执行的命令。如果某个工程被前任维护者加了奇怪的预处理脚本脚本执行失败也会以报错形式出现在 Build 窗口里而且报错文本看起来跟编译毫无关系比如找不到某个批处理文件。接手别人工程时顺手看一眼这个页面能挡掉一些莫名其妙的错误。
阅读完成 · 觉得有帮助?
咨询建站