Clang Module 内部实现原理及源码分析

简介: 钉钉工程开始支持Swift,在适配clang module的过程中,遇到了各种各样的编译问题,为了弄清楚这些编译失败的真正原因,以及clang module的最佳实践,决定通过深入阅读clang module的实现代码,来解开这些谜团。

编译参数

Xcode的Build Settings针对Clang Module有专门的设置分组,如下图:

image.png

针对这几个设置参数,下面分别解释一下其作用。

Enable Modules (C and Objective-C)

是否开启Clang Module特性。

当设置为YES的时候,会设置编译器参数-fmodules,开启clang module特性。当设置为NO的时候,其它4个选项也会随之失效,不会设置编译器参数-fmodules。

Enable Clang Module Debugging

对引用的外部clang module或者预编译头文件生成调试信息

当设置为YES的时候,会设置编译器参数-gmodules

举例说明一下这个参数,我们自己模块的Objective-C源代码中如果有#import <Foundation/Foundation.h>,那Foundation模块就属于被引用的外部clang module。当开启Clang Module特性的时候,会根据Foundation模块提供的modulemap生成clang module编译缓存,其缓存的目录是通过编译器参数-fmodules-cache-path来设定的。

默认Xcode会设定编译缓存目录为的ModuleCache.noindex

-fmodules-cache-path=/Users/baozhifei/Library/Developer/Xcode/DerivedData/ModuleCache.noindex

如下图红框中所示,ModuleCache.noindex为clang module缓存目录,Foundation-3DFYNEBRQSXST.pcm为Foundation的缓存文件

image.png

当Enable Clang Module Debugging为YES的时候,这个缓存文件为Mach-O格式的文件,其中__CLANG,__clangast节为缓存内容,这个文件还携带__DWARF,__debug_info等一些调试信息。

其中缓存内容的头4个字节签名是CPCH,应该是Compiled PCH的缩写。

image.png

当Enable Clang Module Debugging为NO的时候,缓存文件直接就是CPCH文件,不会生成Mach-O格式且携带调试信息。

image.png

建议正常开发的时候关闭这个设置,当出现clang module编译问题的时候,可以开启这个调试选项,有了DWARF的调试信息,可以精确定位的错误代码的行号和列号。

开启这个选项后,编译时会有性能损失,因为缓存变成了Mach-O格式,需要完整加载整个文件,读取__clangast节,才能获取真正的缓存内容。

Apple的官方文档《Precompiled Header and Modules Internals》中原文描述如下:

Clang’s AST files are loaded “lazily” from disk. When an AST file is initially loaded, Clang reads only a small amount of data from the AST file to establish where certain important data structures are stored. The amount of data read in this initial load is independent of the size of the AST file, such that a larger AST file does not lead to longer AST load times.

从下面代码中,就可以看出,CPCH文件内容其实就是AST的bitcode,所以,clang module的实现机制是和预编译头文件一致的,clang module可以认为是更通用的预编译头文件。

640.png

Disable Private Modules Warnings

对于Private Module概念还不了解,后面再展开

Allow Non-modular Includes In Framework Modules

允许framework模块中有非clang module的include

当设置为NO的时候,会设置编译器参数-Wnon-modular-include-in-framework-module。如果在引用的模块中,遇到非clang module的头文件,例如 #import "XXX.h" 这样格式的import指令,就会报错。

Link Frameworks Automatically

对于开启clang module后,import clang module会自动对链接器ld64增加链接参数,如下图红框所示:

image.png

因为我们都是使用CocoaPod来管理依赖,所以,最好关闭此选项,统一在podspec中声明依赖的frameworks和weak_frameworks

关闭后,会增加编译器参数-fno-autolink

Defines Module

image.png

-fmodule-name=ClangModuleTest

一个是形成一个虚拟的clang module层,让我们当前源码编译的模块也可以伪装成clang module格式

-ivfsoverlay /Users/baozhifei/Library/Developer/Xcode/DerivedData/ClangModuleTest-frvkjzzwryjshkeuimnrjtpuowxm/Build/Intermediates.noindex/ClangModuleTest.build/Debug-iphonesimulator/ClangModuleTest.build/all-product-headers.yaml

这个文件格式有点问题,说是yaml格式,实际上内容是json

image.png

源代码

llvm项目地址:https://github.com/llvm/llvm-project

我们编译调试的版本是14.0.6,是目前最新的tag

调试的代码,用Xcode创建一个新的framework工程叫ClangModuleTest,新增Test类,我们分析Test.m的编译流程。


image.png

image.png

Xcode内置的clang版本应该是有一些功能没有开源,我们开源的clang不认识-index-unit-output-path-index-store-path,调试的时候这两个参数删除即可。

最新版本的clang的编译参数,都统一定义在Options.td文件中,通过clang-tblgen来统一生成,这样生成出来的rst文档和Options.inc是一致的,在Options.td中没有找到上述两个参数。

我这里调试的环境是Qt Creator,设置运行参数如下图

image.png

如果只调试不写代码,可以使用Xcode来调试,如果还要编译代码,最好使用vscode,clion,qt creator这种支持cmake的IDE,可以使用ninja来构建,编译会非常快。Qt Creator界面丑一点,但是稳定,cmake自带一个GUI配置界面,很方便。

image.png

如果是vscode,要配置settings.json和launch.json文件,内容大致如下:

image.png

预处理

clang::Preprocessor是负责预处理的类,预处理主要是处理编译单元中的一些#号开头的预处理指令,比如,#import导入头文件预编译指令,这些指令定义在TokenKinds.def文件中。

640 (1).png

image.png

image.png

image.png

image.png

image.pngimage.png

image.png

clang会开启另外一个线程来编译Foundation模块,并把编译结果写入到pcm文件中。这个CompilerInstance执行的FrontendAction就是GenerateModuleFromModuleMapAction,我们之前编译.o文件实际上是EmitObjAction。


image.png

image.png

image.png


相关文章
SwiftUI—使用section将列表分为几个组
SwiftUI—使用section将列表分为几个组
1100 0
SwiftUI—使用section将列表分为几个组
|
9月前
|
网络协议 Java
SpringBoot快速搭建TCP服务端和客户端
由于工作需要,研究了SpringBoot搭建TCP通信的过程,对于工程需要的小伙伴,只是想快速搭建一个可用的服务.其他的教程看了许多,感觉讲得太复杂,很容易弄乱,这里我只讲效率,展示快速搭建过程。
922 58
|
人工智能 自然语言处理 JavaScript
体验通义灵码 @workspace:轻松分析项目结构,结合代码仓库理解工程、查询问答等
当你需要快速了解一个工程、查找工程内的实现逻辑,或有新的诉求需要进行代码变更时,可以在智能问答窗口中通过 @ 可唤起 @workspace,选中后输入你的问题或诉求,通义灵码可快速结合当前仓库进行工程理解、代码查询、代码问答等,同时可以通过自然语言描述需求,结合当前工程生成简单需求或缺陷的整体修改建议和相关建议代码。
|
安全 调度 Swift
【Swift开发专栏】Swift中的多线程与并发编程
【4月更文挑战第30天】本文探讨Swift中的多线程与并发编程,分为三个部分:基本概念、并发编程模型和最佳实践。介绍了线程、进程、并发与并行、同步与异步的区别。Swift的并发模型包括GCD、OperationQueue及新引入的结构体Task和Actor。编写高效并发代码需注意任务粒度、避免死锁、使用线程安全集合等。Swift 5.5的并发模型简化了异步编程。理解并掌握这些知识能帮助开发者编写高效、安全的并发代码。
556 1
|
机器学习/深度学习 人工智能 算法
体验升级:扫描全能王智能高清滤镜2.0全面测评
**扫描全能王智能高清滤镜2.0测评概览** - **技术亮点:** 结合深度学习与多尺度感知融合,提升图像清晰度。 - **智能处理:** 利用深度学习识别透字、颜色和文字,自适应调整处理策略。 - **测评场景:** - **透字文件**:有效抑制透字噪声,增强文字可读性。 - **有阴影的发票**:去除阴影,清晰呈现内容。 - **曲度较大书籍**:准确扫描曲面,保持文字形状。 - **电脑屏幕文本**:优化屏幕显示文本的扫描质量。 - **图画扫描**:颜色还原准确,保持图像细节。 - **总结展望:** 强大的处理能力,满足多样化文档需求,期待未来功能拓展。
517 6
|
人工智能 API 异构计算
AI智能体研发之路-工程篇(四):大模型推理服务框架Xinference一键部署
AI智能体研发之路-工程篇(四):大模型推理服务框架Xinference一键部署
2504 2
|
人工智能 文字识别 自然语言处理
准确高效的TextIn文档解析:一项开发痛点的解决方案
企业在构建知识库问答系统时面临挑战,尤其是处理扫描文档和手写内容。传统OCR工具和开源方法在准确性和速度上不足。专业长文档解析成为关键,其中TextIn平台的文档解析服务脱颖而出。该服务能快速将PDF转为Markdown,提高处理速度和准确性,尤其适合处理复杂布局的长文档。通过实际测试,TextIn能有效增强LLM问答系统的性能,解决无法正确解析的问题。目前TextIn处于内测阶段,提供每周7000页的免费试用额度,开发者可通过其官网或“合研社”公众号了解更多信息和获取接口文档。
|
JavaScript 开发者
Vue(Vue2+Vue3)——2.使用VsCode搭建Vue开发环境
Vue(Vue2+Vue3)——2.使用VsCode搭建Vue开发环境
|
机器学习/深度学习 算法 搜索推荐
F1值(F1 Score)
F1值(F1 Score)是用于综合评估分类模型性能的指标,它结合了精确率(Precision)和召回率(Recall)。F1值是精确率和召回率的调和平均数,它可以用来衡量模型在保持精确率和召回率之间的平衡时的性能。
9371 1
|
自然语言处理 前端开发 安全
iOS-底层原理 31:LLVM编译流程 & Clang插件开发
iOS-底层原理 31:LLVM编译流程 & Clang插件开发
1061 0
iOS-底层原理 31:LLVM编译流程 & Clang插件开发

热门文章

最新文章