笔者在使用 rbenv
装 ruby
时,遇到一个标准库头文件缺失导致编译失败的问题。
本文会记录笔者对该问题产生的原因分析,并通过分析 clang
源码的方式提供一个通用的解决方案。
通过该方案,可以解决所有类似的头文件缺失问题。
rbenv
安装 ruby 失败
笔者是执行 rbenv install 2.7.2
命令时遇到了问题。
控制台输出如下:
通过添加 --verbose
参数,我们可以得到更加详细的错误信息:
|
|
从上面的日志中,我们可以发现三个关键点:
rbenv
最终调用了clang
执行编译任务clang
执行编译任务时,无法找到系统库头文件<inttypes.h>
clang
命令缺失-isysroot
参数
系统库文件查找路径
通常情况下,我们可以添加参数 -isysroot <dir>
的方式解决上面的报错:
|
|
但是,本次是通过 rbenv
命令执行 ruby
的安装任务,所以上面的解决方案行不通。
Clang driver
为了查找变通方案,我们需要先了解一下 Clang driver
。
在 llvm 编译器高级用法:第三方库插桩中,我们曾经提到过 clang
会按照以下顺序执行。
而负责组装每个任务的就是 clang Driver
。
clang Driver
的处理逻辑分为以下几步:
-
Parse: Option Parsing:解析传入的参数
-
Pipeline: Compilation Action Construction:根据每个输入的文件和类型,组建
action
(比如PreprocessJobAction
)- 通过
-ccc-print-phases
可以查看action
- 通过
|
|
-
Bind: Tool & Filename Selection:根据
action
选择对应的工具和文件名信息- 通过
-ccc-print-bindings
可以查看对应的工具和文件名信息
- 通过
|
|
-
Translate: Tool Specific Argument Translation:根据输入的参数转为不同
tool
的参数-
比如,插桩参数
-fsanitize-coverage=trace-pc-guard
会变为-fsanitize-coverage-type=3 -fsanitize-coverage-trace-pc-guard
参数 -
比如
-target arm64-apple-ios8.0
会根据clang
和ld
变成不同的参数原始参数:
1
xcrun --sdk iphoneos clang -target arm64-apple-ios8.0 main.m -v
各个
tool
的参数:1 2
clang -cc1 -triple arm64-apple-ios8.0.0 -o main-a28fc8.o -x objective-c main.m ld -arch arm64 -platform_version ios 8.0.0 -o a.out main-a28fc8.o
-
-
Execute:真正的执行不同的工具
为了方便理解,我们可以将下面的图片和上面的流程对应:
第一版方案:通过环境变量控制头文件搜索路径
因为 mac
与 Darwin tool chain
对应,所以我们需要重点关注 Darwin tool chain
相关的逻辑。
通过查看 clang::driver::toolchains::Darwin
相关的代码,我们会发现下面的执行逻辑:
Compilation
调用 clang::driver::toolchains::Darwin
的 TranslateArgs
函数时,会在命令行的 -isysroot
参数缺失的情况下,通过环境变量 SDKROOT
获取。
|
|
如下图,通过 export
命令设置环境变量后,clang
命令可以正常执行
|
|
xcrun
考虑到不同的系统会对应不同的 SYSROOT
,每次都通过 export
方式设置环境变量比较繁琐,我们需要换一种更简单的配置方式。
根据 xcrun
的帮助文件,我们可以发现,通过 xcrun
命令可以方便的调整不同系统对应的 SDKROOT
路径。
|
|
比如,我们可以通过以下代码完成编译:
|
|
优化方案:xcrun
根据上面的信息,我们可以尝试使用 xcrun
调用安装命令:
通过截图,我们可以发现 xcrun rbenv install 2.7.2
命令组合可以安装 ruby
。
总结
通过本文,我们可以得到以下经验:当因为标准库头文件缺失导致编译失败时,可以通过搭配 xcrun
完成编译任务。