美文网首页
ios编译以及clang插件

ios编译以及clang插件

作者: 会跑的鱼_09 | 来源:发表于2020-11-25 00:07 被阅读0次

ios的编译器组成分为前端和后端,clang是前端,llvm是后端,swift语言的前端编译器是swift。前端编译器把代码最终编译成IR中间代码,然后后端进行优化生成汇编,以及目标文件,最后经过链接生成可执行文件。

文件编译过程

有如下代码:

#import <stdio.h>
#define C 30
int test(int a,int b){
    return a + b + C;
}

int main(int argc, const char * argv[]) {
    int a = test(1, 2);
    printf("%d",a);
    return 0;
}
//查看编译阶段
clang -ccc-print-phases main.m

+- 0: input, "main.m", objective-c
+- 1: preprocessor, {0}, objective-c-cpp-output
+- 2: compiler, {1}, ir
+- 3: backend, {2}, assembler
+- 4: assembler, {3}, object
+- 5: linker, {4}, image
6: bind-arch, "x86_64", {5}, image

各阶段编译器命令如下:

//预编译
clang -E main.m
//词法分析
clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m
//语法分析
clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
//找不到头文件时
clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/ iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.2.sdk -fmodules -fsyntax-only -Xclang -ast-dump main.m

//生成中间代码
clang -S -fobjc-arc -emit-llvm main.m -o main.ll
//生成bitCode
clang -emit-llvm -c main.ll -o main.bc

//用bc或者ll生成汇编代码
clang -S -fobjc-arc main.bc -o main.s
clang -S -fobjc-arc main.ll -o main.s
//增加优化参数
clang -Os -S -fobjc-arc main.m -o main.s
//生成目标文件
clang -fmodules -c main.s -o main.o
//查看目标文件内容
xcrun nm -nm main.o
//生成可执行文件
clang main.o -o main

源码编译llvm工程

一、下载源码

首先到llvm-github上下载最新版本的llvm11源代码(因为我的电脑系统是11,最好对应上,否则编译有问题),有下面几个源码包需求下载:
llvm-11.0.0.src.tar.xz
clang-11.0.0.src.tar.xz
clang-tools-extra-11.0.0.src.tar.xz
compiler-rt-11.0.0.src.tar.xz
libcxx-11.0.0.src.tar.xz
libcxxabi-11.0.0.src.tar.xz

二、准备环境

下载后解压,然后执行以下动作:
1.把clang放到llvm项目的tools目录下。
2.把clang-tools-extra放到clang项目的tools目录下
3.把compiler-rt、libcxx、libcxxabi都放到llvm项目的projects目录下
4.安装cmake,brew install cmake

三、编译出xcode工程

在llvm项目的同级目录下建一个编译目录build_xcode,然后编译xcode工程

mkdir build_xcode
cd build_xcode
cmake -G Xcode ../llvm
四、编译clang

编译完后会在build_xcode目录下生成一个xcode工程出来,双击LLVM.xcodeproj(注意此时选择Manually Manage Scheme,因为自动导入时间会比较长,也用不到)。然后手动添加clang和clangTooling这两个scheme。最后先编译clangTooling,再编译clang。这个过程会非常漫长,编译后build_xcode这个文件夹也会变得非常大,大概20G左右。

clang插件开发

经过上面的编译后就可以进行clang插件开发了,cmake的过程是增量的,所以后面的编译就非常快了。

新建插件

1.在llvm/tools/clang/tools下建一个文件夹HKPlugin,然后修改llvm/tools/clang/tools目录下的CMakeLists.txt文件,新增 add_clang_subdirectory(HKPlugin)
2.在TTPlugin目录下新建HKPlugin.cpp和CMakeLists.txt文件,并在CMakeLists.txt中增加

add_llvm_library( HKPlugin MODULE BUILDTREE_ONLY
  HKPlugin.cpp
)

3.接下来复用cmake重新生成一下Xcode项目,在build_xcode中 cmake -G Xcode ../llvm,最后在xcode项目的Loadable modules目录下就可以看到HKPlugin目录了

编写插件代码

插件开发代码也可以参考Loadable modules目录下的LLVMHello插件。

namespace HKPlugin {
    class HKMatchCallback: public MatchFinder::MatchCallback{
    private:
        CompilerInstance &CI;
        
        bool isUserSourceCode(const string fileName){
            if (fileName.empty()) return false;
            //非xcode中的源码都认为是用户的
            if (fileName.find("/Applications/Xcode.app/") == 0) return false;
            return true;
        }
        
        //判断是否应该用copy修饰
        bool isShouldUseCopy(const string typeStr){
            if (typeStr.find("NSString") != string::npos ||
                typeStr.find("NSArray") != string::npos ||
                typeStr.find("NSDictionary") != string::npos) {
                return true;
            }
            return false;
        }
        
    public:
        HKMatchCallback(CompilerInstance &CI):CI(CI){}
        
        //真正的回调!!
        void run(const MatchFinder::MatchResult &Result) {
            //通过Result获取到节点.
         const ObjCPropertyDecl * propertyDecl =   Result.Nodes.getNodeAs<ObjCPropertyDecl>("objcPropertyDecl");
            //获取文件名称
                          string fileName = CI.getSourceManager().getFilename(propertyDecl->getSourceRange().getBegin()).str();
            
            //判断节点有值并且是用户文件
            if (propertyDecl && isUserSourceCode(fileName)) {
                //拿到节点的类型!
                string typeStr = propertyDecl->getType().getAsString();
                //拿到节点的描述信息
                ObjCPropertyDecl::PropertyAttributeKind attrKind = propertyDecl->getPropertyAttributes();
                
                //判断应该使用copy但是没有使用copy
                if (isShouldUseCopy(typeStr) && !(attrKind & ObjCPropertyDecl::OBJC_PR_copy)) {
//                    cout<<typeStr<<"应该使用;copy修饰!但是你没有!"<<endl;
                    //诊断引擎
                    DiagnosticsEngine &diag = CI.getDiagnostics();
                    //Report 报告
                    diag.Report(propertyDecl->getBeginLoc(),diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0这个地方推荐使用copy!!"))<<typeStr;
                    
                }
            }
        }
    };
    

    //自定义的HKConsumer
    class HKConsumer: public ASTConsumer{
    private:
        //AST节点的查找过滤器!
        MatchFinder matcher;
        HKMatchCallback callback;
    public:
        
        HKConsumer(CompilerInstance &CI):callback(CI){
            //添加一个MatchFinder去匹配objcPropertyDecl节点
            //回调在HKMatchCallback里面run方法!
            matcher.addMatcher(objcPropertyDecl().bind("objcPropertyDecl"), &callback);
        }
        
        //解析完一个顶级的声明就回调一次!
        bool HandleTopLevelDecl(DeclGroupRef D) {
//            cout<<"正在解析...."<<endl;
            return true;
        }
        
        //整个文件都解析完成的回调!
        void HandleTranslationUnit(ASTContext &Ctx) {
//            cout<<"文件解析完毕!"<<endl;
            matcher.matchAST(Ctx);
        }
        
    };

    //1.继承PluginASTAction 实现我们自定义的Action
    class HKASTAction:public PluginASTAction{
    public:
        bool ParseArgs(const CompilerInstance &CI, const std::vector<std::string> &arg) {
            return true;
        }
        
        unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
            return unique_ptr<HKConsumer>(new HKConsumer(CI));
        }
    };
}
//2.注册插件
static FrontendPluginRegistry::Add<HKPlugin::HKASTAction> HK("HKPlugin","this is HKPlugin");

整个插件的开发逻辑就是在clang编译器的抽象语法树生成过程中进行一些自定义的动作,也就是增加了一些回调方法。然后利用一些编译器内置的api进行ui展示。

编译过程中如果想测试插件情况可以通过以下命令:

生成的clang编译器路径 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.2.sdk/ -Xclang -load -Xclang 生成的HKPlugin.dylib路径 -Xclang -add-plugin
-Xclang HKPlugin -c 某个源码路径

相关文章

网友评论

      本文标题:ios编译以及clang插件

      本文链接:https://www.haomeiwen.com/subject/vgkgiktx.html