程序员啊,你要认识你自己(的工具)!
神秘的编译问题
我目前从事嵌入式裸机开发,所以免不了需要用 Keil. 今天(指本文发表的一个月内)遇到了一个非常诡异的问题:头文件更改后不生效,链接阶段由于宏定义不正确会出错。二次确认更改的是正确的项目下的正确的头文件;重启无效;甚至 Keil 重装都无效。
具体来说,链接脚本中是包含一些宏处理的。它差不多长这个样子:
#! armcc -E -I.
#include "./mem_config.h"
---8<---<snip>---8<---
#if USE_ADVANCED_MEMORY_CONFIG
ADVANCED_MEMORY_REGION ADVANCED_MEMORY_ADDRESS ADVANCED_MEMORY_SIZE
{
(.amm.*)
}
#endif
---8<---<snip>---8<---
在编译时,会先调用 armcc
对这个文件中的宏进行展开,和 C 语言的预处理器行为一致。这就意味着如果 memory_config.h
中没有定义 USE_ADVANCED_MEMORY_CONFIG
或者定义为 0
的话,对应的区域就不会生成。
自然,代码中也有对应的宏处理:
#include "mem_config.h"
#if USE_ADVANCED_MEMORY_CONFIG
void advanced_memory_config_init() {
extern uint32_t Image$$ADVANCED_MEMORY_REGION$$Base;
extern uint32_t Image$$ADVANCED_MEMORY_REGION$$Length;
// do funny things with symbols defined above
}
#endif
其中,Image$$ADVANCED_MEMORY_REGION$$Base
和 Image$$ADVANCED_MEMORY_REGION$$Length
是链接器定义的符号。当这个区域存在时有定义,分别对应该区域的加载地址和长度。
十分有趣的是,在某台计算机上修改 mem_config.h
的并不会产生任何效果,USE_ADVANCED_MEMORY_CONFIG
的值始终为 0. 而换一台电脑,同样的工程、同样的代码就可以正常编译。
诊断
一般遇到这种问题,很容易先想到是不是改错了文件或者头文件包含目录设置得有问题。之前已经确认了没有改错头文件,那么就只能考虑是包含目录设置有问题,引入了不属于这个项目的文件。
可是翻遍了项目的属性设置,检查了每一个可能出问题的地方,都没有找到任何问题。更可气的是其他计算机编译同样的工程就没有问题。
这个时候已经不能再依赖 Keil 本身尝试找到问题了。掏出 ProcMon 看看吧。
ProcMon 很快就抓到了这样一个条目:
armcc.exe
正在尝试从 %TEMP%
中取头文件,而 %TEMP%
中却又莫名地有一份 mem_config.h
存在。打开确认这个文件并不是编译中介文件后,删除该文件。编译终于能正常通过了。
查阅工具的手册,并没有说明 armcc
会选择 %TEMP%
作为默认包含目录[1]——毕竟,这份文件本身并不是一个编译中间文件,也不应该出现在 %TEMP%
下;而且一个编译器会选择将 %TEMP%
作为默认包含路径这种事情也让我觉得十分神奇——你能想象 gcc
或者 clang
将 /tmp
作为优先级极高的默认包含目录(而且不告诉你)会导致多少问题么?
工具的易用性
又到了喜闻乐见的喷 Keil 时间。上一次喷 Keil 还是因为这玩意差点挂了我的实验。
如果之前文本配置和 GUI 配置之间的差异只是个人喜好上的不同,这次的事件就反映了一个更严重的问题:你的工具会包含一些你可能完全无法预期的行为,而且这种行为并不会被告知:它会耍一些小聪明。当这个小聪明炸掉了你的工程的时候,就需要你有更多的调试经验才能完成诊断。
「工具应该符合直觉。」这里的「符合直觉」并不一定表示学习成本低,而是指它的行为和手册上描述的行为是一致的,最重要的是它不会做你没有告知它去做的事情。
许多人认为命令行是「不符合直觉」的,这个我想还是跟现在的计算环境有关:现在的孩子们已经不再需要和命令行打交道;哦,抱歉,是不再需要和 IBM 兼容机打交道[2]。图形界面是直观的,这是因为图形界面是给人类设计的;无数的界面设计师和程序员教会了机器以人类的方式说话。但是作为系统工程师的你,并不能依赖机器说人类的语言:你总有一天需要学会机器的语言,然后以机器的方式直接和机器对话。
参考资料
↑1 | ARM. ARM Compiler armcc User Guide[F/OL]. ARM Developer. [2022-12-05]. https://developer.arm.com/documentation/dui0472/k/Getting-Started-with-the-Compiler/Compiler-command-line-options-and-search-paths. |
---|---|
↑2 | 八私今天进步了没 等. 当代年轻一代信息技术现状[V/OL]. Bilibili. (2022-01-29)[2022-03-05]. https://www.bilibili.com/video/BV1pL4y1x7i7 |
Your comments will be submitted to a human moderator and will only be shown publicly after approval. The moderator reserves the full right to not approve any comment without reason. Please be civil.