引子
\包瘦身,包瘦身,包瘦身,重要的事情说三遍。 \
最近公司一款iOS APP(本文只讨论使用Objective C开发的iOS安装包)一直在瘦身,我们团队的APP也愈发庞大了。而要解决这个问题,思路主要集中在两个方向,资源和代码。资源主要在于图片,方法包括移除未被引用的图片,只使用一套图片(2x或3x),图片伸缩等;代码层面主要思路包括重构消除冗余,linkmap中selector引用分析等。除此之外,有没有别的路径呢? \
众所周知,代码之间存在调用关系。假设iOS APP的主入口为-[UIApplication main],则所有开发者的源代码(包括第三方库)可分为两类:存在一条调用路径,使得代码可以被主入口最终调用(称此类代码为被最终调用);不存在一条调用路径,使得代码最终不能被主入口调用(称此类代码为未被最终调用)。 \
假设有一个源代码级别的分析工具(或编译器),可以辅助分析代码间的调用关系,这样就使得分析最终被调用代码成为可能,剩下的就是未被最终调用的代码。 \
这种工具目前有成熟可用的吗?答案是肯定的,就是clang插件。除可用于分析未被最终调用代码外,clang还可辅助发现重复代码。 \
LLVM与clang插件
\LLVM工程包含了一组模块化,可复用的编辑器和工具链。同其名字原意(Low Level Virtual Machine)不同的是,LLVM不是一个首字母缩写,而是工程的名字。目前LLVM包含的主要子项目包括: \
- LLVM Core:包含一个现在的源代码/目标设备无关的优化器,一集一个针对很多主流(甚至于一些非主流)的CPU的汇编代码生成支持。 \
- Clang:一个C/C++/Objective-C编译器,致力于提供令人惊讶的快速编译,极其有用的错误和警告信息,提供一个可用于构建很棒的源代码级别的工具. \
- dragonegg: gcc插件,可将GCC的优化和代码生成器替换为LLVM的相应工具。 \
- LLDB:基于LLVM提供的库和Clang构建的优秀的本地调试器。 \
- libc++、libc++ ABI: 符合标准的,高性能的C++标准库实现,以及对C++11的完整支持。 \
- compiler-rt:针对
__fixunsdfdi
和其他目标机器上没有一个核心IR(intermediate representation)对应的短原生指令序列时,提供高度调优过的底层代码生成支持。 \ - OpenMP: Clang中对多平台并行编程的runtime支持。 \
- vmkit:基于LLVM的Java和.NET虚拟机实 \
- polly: 支持高级别的循环和数据本地化优化支持的LLVM框架。 \
- libclc: OpenCL标准库的实现 \
- klee: 基于LLVM编译基础设施的符号化虚拟机 \
- SAFECode:内存安全的C/C++编译器 \
- lld: clang/llvm内置的链接器
作为LLVM提供的编译器前端,clang可将用户的源代码(C/C++/Objective-C)编译成语言/目标设备无关的IR(Intermediate Representation)实现。其可提供良好的插件支持,容许用户在编译时,运行额外的自定义动作。 \
我们的目标是使用clang插件减少包大小。其原理是,针对目标工程,基于clang的插件特性,开发者可以编写插件以分析所有源代码。编译过程中,将插件作为clang的参数载入并生成各种中间文件。编译完成后,还需编写一个工具去分析所有包含源码的方法(包括用户编写,以及引入的第三方库源代码),检查这些方法中哪些最终可被程序主入口调用,剩余即是疑似无用代码。简单的一个复查,移除那些确定无用的代码,重新编译,便可以有效去除无用的代码从而减少包大小。 \
本文相关内容如下: \
- 如何编写一个clang插件并集成到Xcode \
- 如何实现代码级别的包瘦身 \
- 局限与个性化定制 \
- 其他
如何编写一个clang插件并集成到Xcode
\Clone clang源码并编译安装
cd /opt\sudo mkdir llvm\sudo chown `whoami` llvm\cd llvm\export LLVM_HOME=`pwd`\\git clone -b release_39 git@github.com:llvm-mirror/llvm.git llvm\git clone -b release_39 git@github.com:llvm-mirror/clang.git llvm/tools/clang\git clone -b release_39 git@github.com:llvm-mirror/clang-tools-extra.git llvm/tools/clang/tools/extra\git clone -b release_39 git@github.com:llvm-mirror/compiler-rt.git llvm/projects/compiler-rt\\mkdir llvm_build\cd llvm_build\cmake ../llvm -DCMAKE_BUILD_TYPE:STRING=Release\make -j`sysctl -n hw.logicalcpu`\
编写clang插件
\要实现自定义的clang插件(以C++ API为例),应按照以下步骤: \
- 自定义继承自 \
clang::PluginASTAction
(基于consumer的抽象语法树(Abstract Syntax Tree/AST)前端Action抽象基类) \clang::ASTConsumer
(用于客户读取抽象语法树的抽象基类), \
\clang::RecursiveASTVisitor
(前序或后续地深度优先搜索整个抽象语法树,并访问每一个节点的基类)等基类。 - 根据自身需要重载 \
PluginASTAction::CreateASTConsumer
\PluginASTAction::ParseArgs
\ASTConsumer::HandleTranslationUnit
\RecursiveASTVisitor::VisitDecl
\RecursiveASTVisitor::VisitStmt
\等方法,实现自定义的分析逻辑。
\ - 注册插件 \
static FrontendPluginRegistry::Add\u0026lt;MyPlugin\u0026gt; X(\"my-plugin- name\