草庐IT

iOS前端编译器扩展——Clang

周彬涵 2023-03-28 原文

Part 01、了解Clang   

众所周知,编译器一般分为前端和后端,编译器前端主要负责预处理、词法分析、语法分析、语法检查、生成中间代码等与底层计算机架构无关的工作。

后端以中间代码为输入,首先进行架构无关的代码优化,之后针对不同的机器架构生成不同的机器码,进行汇编链接。

Clang在iOS代码编译中主要用于C/C++、Objective-C的前端编译工作,Clang属于llvm编译链的一部分,是llvm的前端编译器。我们可以通过Clang开放出来的API接口对源码进行自定义处理,如静态代码检查、编译流程控制、代码查找提示补全等功能。Clang工具针对的对象正是AST——语法分析的结果,抽象语法树(abstract syntax tree)。

一个AST的简单例子:


Clang工具可以遍历读取AST上的每一个节点,并对节点对应的代码进行查询、修改操作。Clang插件更能够直接集成进iOS的编译流程中,控制输出自定义的编译告警、错误,控制编译流程。

Part 02、Clang工具的选择  

Clang大体包含三种不同的工具,libClang、Clang插件和libTooling。Clang插件和libTooling代码类似,关于AST的所有信息都通过ASTContext上下文返回,并且对AST有完全的控制权。而libClang不同,它通过封装好的稳定高层C API进行访问,利用Cursor和Token递归遍历,不能对AST进行完全控制。三者的优缺点如下:

1. libClanglibClang是针对Clang的稳定高层C语言封装,当你无需对AST进行完全控制时,libClang是使用最简单最合适的工具,应该首先考虑使用。其次,它只能作为独立工具使用,不能嵌入当前项目的编译流程。

  • 优点:可以使用XCode或Python进行集成开发,拥有稳定的高层API,使用简单;
  • 缺点不能全量控制AST,不能嵌入编译流程。
2. Clang插件Clang插件允许在代码的编译流程中额外插入一些操作,比如在编译的过程中打印特殊字符或者警告,甚至中断编译。

  • 优点:能够嵌入编译流程开启或中断编译,打印自定义告警和错误,并对AST进行全量控制;
  • 缺点:代码编写复杂,集成Clang插件会降低原本的编译速度。
3. libToolinglibTooling是一个基于C的用于编写独立工具的Clang工具,这点类似于libClang,但是它只能用C编写并且功能更强大,对AST能够全量控制。

  • 优点:对工程文件全量的处理,对AST的全量控制;
  • 缺点:不能嵌入编译流程,对Clang的升级较为敏感,API不稳定。
这三个工具,从上到下,兼容性越来越差,对Clang升级变化越来越敏感,使用越来越复杂,但是功能越来越强大。

Part 03、Clang的具体应用 

在和家亲中Clang的应用正在初步展开,我们使用libClang遍历项目中的每一个源文件,找到项目中所有关于图片名称的字符串描述,图片名称往往以固定字符串的形式出现,从而判断在我们的工程中,哪些图片已经被使用而哪些已经没有在用了,进行包的大小优化。

我们使用Clang插件对已在工程中定义的却没有在工程中使用的类、方法进行告警提示。方法是:首先利用Clang插件的VisitObjCInterfaceDecl和VisitObjCMethodDecl方法找出工程中所有的类定义和方法定义,再利用VisitObjCMessageExpr和VisitObjCSelectorExpr找到所有的消息发送,在iOS中方法的调用是通过消息发送的形式进行的,对于那些没有在消息发送列表中出现的类和方法,我们认为这些类和方法未被使用,从而直接在编译的时候进行告警提示。将插件在编译器中集成即可使用。


有关iOS前端编译器扩展——Clang的更多相关文章

  1. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  2. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  3. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  4. c - mkmf 在编译 C 扩展时忽略子文件夹中的文件 - 2

    我想这样组织C源代码:+/||___+ext||||___+native_extension||||___+lib||||||___(Sourcefilesarekeptinhere-maycontainsub-folders)||||___native_extension.c||___native_extension.h||___extconf.rb||___+lib||||___(Rubysourcecode)||___Rakefile我无法使此设置与mkmf一起正常工作。native_extension/lib中的文件(包含在native_extension.c中)将被完全忽略。

  5. ruby - Sinatra set cache_control to static files in public folder编译错误 - 2

    我不知道为什么,但是当我设置这个设置时它无法编译设置:static_cache_control,[:public,:max_age=>300]这是我得到的syntaxerror,unexpectedtASSOC,expecting']'(SyntaxError)set:static_cache_control,[:public,:max_age=>300]^我只想将“过期”header设置为css、javaascript和图像文件。谢谢。 最佳答案 我猜您使用的是Ruby1.8.7。Sinatra文档中显示的语法似乎是在Ruby1.

  6. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  7. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  8. ruby-on-rails - 向 Rails 3 添加 Ruby 扩展方法的最佳实践? - 2

    我有一个要在我的Rails3项目中使用的数组扩展方法。它应该住在哪里?我有一个应用程序/类,我最初把它放在(array_extensions.rb)中,在我的config/application.rb中我加载路径:config.autoload_paths+=%W(#{Rails.root}/应用程序/类)。但是,当我转到railsconsole时,未加载扩展。是否有一个预定义的位置可以放置我的Rails3扩展方法?或者,一种预先定义的方式来添加它们?我知道Rails有自己的数组扩展方法。我应该将我的添加到active_support/core_ext/array/conversion

  9. ruby - 为什么不能使用类IO的实例方法noecho? - 2

    print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上

  10. ruby - 如何在 ruby​​ 中复制目录结构,不包括某些文件扩展名 - 2

    我想编写一个ruby​​脚本来递归复制目录结构,但排除某些文件类型。因此,给定以下目录结构:folder1folder2file1.txtfile2.txtfile3.csfile4.htmlfolder2folder3file4.dll我想复制这个结构,但不包含.txt和.cs文件。因此,生成的目录结构应如下所示:folder1folder2file4.htmlfolder2folder3file4.dll 最佳答案 您可以使用查找模块。这是一个代码片段:require"find"ignored_extensions=[".cs"

随机推荐