前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >LLVM(二)——Clang插件

LLVM(二)——Clang插件

作者头像
拉维
发布于 2021-04-16 08:37:06
发布于 2021-04-16 08:37:06
1.7K00
代码可运行
举报
文章被收录于专栏:iOS小生活iOS小生活
运行总次数:0
代码可运行

LLVM的下载

由于国内的网络限制,我们需要借助镜像来下载LLVM的源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
https://mirror.tuna.tsinghua.edu.cn/help/llvm/

执行如下命令下载LLVM项目的源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/llvm.git

这一步真的很磨人,我下载了一上午才搞定?,如果你在这一步一直下载不下来,那么试着切换个其他的网络,并且多试几次?

LLVM项目的源码下载完成之后,cd到其tools目录下,下载Clang子项目:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cd llvm/tools
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/clang.git

然后,在LLVM的projects目录下,下载compiler-rt,libcxx,libcxxabi:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cd ../projects
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/compiler-rt.git
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/libcxx.git
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/libcxxabi.git

然后还需要在Clang的tools文件夹下安装extra工具:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cd ../tools/clang/tools
git clone https://mirrors.tuna.tsinghua.edu.cn/git/llvm/clang-tools-extra.git

LLVM的编译

由于最新的LLVM 只支持cmake来编译了,所以我们还需要安装cmake。

安装cmake

首先查看brew是否有安装cmake,如果有那么就跳过下面安装cmake的步骤:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
brew list

通过brew安装cmake的命令如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
brew install cmake

编译LLVM

接下来我通过Xcode来编译一下LLVM。

来到llvm所在的文件目录下,新建一个文件夹,并且cd进去:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mkdir build_xcode
cd build_xcode

然后执行如下命令来将llvm编译成Xcode项目:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cmake -G Xcode ../llvm

这个编译的过程也是比较耗时间的,请耐心等待。

编译完成之后,build_xcode文件夹下就有一个Xcode工程了:

这里有个坑点需要说一下,打开CMakeCache.txt文件,我们可以看到很多的路径,这些路径都是绝对路径,因此如果build_xcode文件夹移动了位置或者这个路径下的任何文件夹出现了变动,那么build_xcode文件夹里面的Xcode工程将会运行失败。所以,如果路径出现了错误,那么就将llvm重新编译成Xcode项目即可。

接下来我们就使用Xcode来编译Clang。打开上面的这个Xcode工程:

注意,这里选择手动管理,不要选择自动创建哈。因为自动创建会创建很多用不到的东西,占用内存比较多,所以我们就手动添加需要的clang和libclang即可:

之后就是在Xcode里面分别对libclang和clang这两个scheme进行编译即可。

由于他们依赖的东西很多,所以这个编译过程是很慢的哦,亲测平均每个都需要一个小时左右?。

要注意哦,一定要预留出足够的磁盘空间哦!不然就会因为磁盘空间不足导致编译失败~

编译完成之后就会生成对应的mach-o可执行文件。

创建插件

先来说一个小技巧,当你的工程文件夹展开得非常多的时候,你想把它收缩起来,此时不需要一个一个点,你就把光标点进任何一个文件或者文件夹,然后command + A全选,然后单独取消最顶层的Xcode那一个层级(即反选),然后按一下左移键,这个时候所有的文件夹就都收起来了,清晰明了。

接下来我们就开始创建自己的插件了。

Clang的插件都是放在其tools文件夹下面的,所以我也在tools文件夹下面创建一个我自己的插件文件夹,暂且命名为NormanPlugin吧:

clang的tools文件夹下面有一个CMakeLists.txt文件,clang用到的所有插件都会记录在该文件中,所以我们自己定义的NormanPlugin插件也需要在CMakeLists.txt中添加一下:

翻阅各个插件可以知道,每个插件的文件夹下面都会有一个CMakeLists.txt文件,咱也创建一个:

CMakeLists.txt的内容如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
add_llvm_library( NormanPlugin MODULE BUILDTREE_ONLY
  NormanPlugin.cpp
)

然后我在NormanPlugin文件夹下面创建一个NormanPlugin.cpp文件:

这里的NormanPlugin.cpp中写的就是插件源码。

接下来我利用cmake来重新编译生成一下LLVM的xcode 项目,完成以后打开Xcode项目,就可以在targets中找到NormanPlugin,并且可以把它添加进scheme来了:

然后我们就可以在Loadable modules目录下找到对应的源码文件,编写对应的插件代码了:

编写插件代码

我们实现这么一个功能:声明NSString类型的属性的时候,属性修饰符如果不是copy就报出警告⚠️

整体的设计思路如下:

clang的整个编译过程都有对应的API暴露出来,也就是说,可以通过继承一些类然后重载对应的方法来达到回调指定节点的目的。这里就是分析语法分析生成AST的过程中的相关内容。

NormanPlugin.cpp文件中的整个内容如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <iostream>
#include "clang/AST/AST.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/FrontendPluginRegistry.h"

using namespace clang;
using namespace std;
using namespace llvm;
using namespace clang::ast_matchers;

namespace NormanPlugin {

    // 4,分析相关的节点
    class NormanMatchCallback: public MatchFinder::MatchCallback {
    private:
        // 4.3 编译器实例对象
        CompilerInstance &CI;

        // 4.4 判断是否是自己写的文件(值检查自己创建的文件,不检查系统的文件)
        bool isUserSourceCode(const string filename) {
            if (filename.empty()) return false;

            // 非Xcode中的源码都认为是用户源码
            if (filename.find("/Applications/Xcode.app/") == 0) return false;

            return true;
        }

        // 4.5 判断是否应该用copy修饰,这里定的是不可变的字符串、字典、数组都应使用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:

        // 4.1 构造器函数
        NormanMatchCallback(CompilerInstance &CI):CI(CI){}

        // 4.2 节点的具体分析
        void run(const MatchFinder::MatchResult &Result) {
            // 4.2.1 通过MatchResult结果获取到要研究的节点(这里研究的是属性节点)
            const ObjCPropertyDecl *propertyDecl = Result.Nodes.getNodeAs<ObjCPropertyDecl>("objcPropertyDecl");
            // 4.2.2 获取文件名称
            string filename = CI.getSourceManager().getFilename(propertyDecl->getSourceRange().getBegin()).str();

            // 4.2.3 如果节点有值,并且是用户自定义的文件(即非系统文件)
            if (propertyDecl && isUserSourceCode(filename)) {
                string typeStr = propertyDecl->getType().getAsString(); // 拿到属性的类型
                ObjCPropertyDecl::PropertyAttributeKind attrKind = propertyDecl->getPropertyAttributes(); // 拿到节点的描述信息

                // 如果应该使用copy但是却没有使用,那么就报出警告
                if (isShouldUseCopy(typeStr) && !(attrKind & ObjCPropertyDecl::OBJC_PR_copy)) {
                    cout<<typeStr<<"应该用copy修饰而没用Copy,发出警告!"<<endl;
                    DiagnosticsEngine &diag = CI.getDiagnostics(); // 诊断引擎
                    // 在编译器中发出警告信息
                    // Report函数的第一个参数是警告报出的位置,第二个参数是警告信息
                    // getCustomDiagID函数的第一个参数是警告级别,第2个参数是警告的文案信息
                    diag.Report(propertyDecl->getBeginLoc(), diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0这个地方推荐用Copy"))<<typeStr;
                }
            }
        }
    };

    // 3,自定义继承自ASTConsumer的NormanConsumer
    // ASTConsumer是专门用来解析AST抽象语法树里面的节点的
    class NormanConsumer: public ASTConsumer {
    private:
        MatchFinder matcher; // MatchFinder是AST语法树各节点的过滤器(过滤你所要研究的节点)
        NormanMatchCallback callback; // 在callback里面对相关节点进行分析研究
    public:
        // 3.1 构造器方法
        NormanConsumer(CompilerInstance &CI):callback(CI) {
            // 添加一个MatchFinder去匹配objcPropertyDecl节点(因为我要研究的是属性,所以需要匹配属性节点)
            // 在NormanMatchCallback的run方法里面去研究相关的节点
            matcher.addMatcher(objcPropertyDecl().bind("objcPropertyDecl"), &callback);
        }

        // 3.2 解析完一个顶级的声明就会来到这里执行(所谓顶级,指的就是最外层)
        bool HandleTopLevelDecl(DeclGroupRef D) override {
//            cout<<"开始解析顶级节点!"<<endl;
            return true;
        }

        // 3.3 在整个文件都解析完后被调用
        void HandleTranslationUnit(ASTContext &context) override {
            cout<<"解析完毕了!"<<endl;
            matcher.matchAST(context); // 生成AST之后将其给到matcher,然后matcher就会对AST进行分析
        }
    };

    // 1,继承PluginASTAction实现我们自定义的Action
    class NormanASTAction: public PluginASTAction { // PluginASTAction用于分析抽象语法树时采取的动作
    public:
        // 1.1
        bool ParseArgs(const CompilerInstance &CI,const vector<string> &arg){
            return true;
        }

        // 1.2 绑定ASTConsumer
        unique_ptr <ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
            // 注意哦,这里的CI是编译器实例对象,文件路径的检查、编译器警告的发送都是通过它来完成
            return unique_ptr <NormanConsumer> (new NormanConsumer(CI));
        }

    };

}

// 2,注册NormanPlugin插件
static FrontendPluginRegistry::Add<NormanPlugin::NormanASTAction> X("NormanPlugin", "This is the description of the plugin");

测试插件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
自己编译的?????clang????文件路径 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.2.sdk/(自己电脑上对应SDK的路径) -Xclang -load -Xclang 插件??(.dylib)路径?? -Xclang -add-plugin -Xclang 插件名??? -c 源码路径????

查找【自己编译的?????clang????文件路径】

在llvm的xcode工程中查找clang,然后show in finder,然后直接拖入终端

查找【插件??(.dylib)路径】

在llvm的xcode工程中查找插件名,然后show in finder,然后直接拖入终端

查找【源码路径????】

随便一个源码拖入终端即可

测试结果

最终的终端代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/Users/liwei/LLVM/build_xcode/Debug/bin/clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/ -Xclang -load -Xclang /Users/liwei/LLVM/build_xcode/Debug/lib/NormanPlugin.dylib -Xclang -add-plugin -Xclang NormanPlugin -c /Users/liwei/Desktop/Test/Test/ViewController.m

源代码如下:

测试结果如下:

可以看到,出问题的代码及其位置都被检测出来了。

Xcode集成插件

加载插件

打开你的测试工程,然后在Build Settings -> Other C Flags中添加如下内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 -Xclang -load -Xclang Clang插件动态库路径(.dylib)????? -Xclang -add-plugin -Xclang Clang插件名称NormanPlugin

注意,【Clang插件动态库路径(.dylib)】可以是绝对路径,也可以是相对路径,相对路径相对的是当前工程的根目录。这里我使用的是绝对路径,但是当我们真正在项目中去使用的时候,使用相对路径会更好一些。

此时,如果你编译一下,Xcode会报一个警告:

这是因为Clang插件需要使用对应的版本去加载,如果版本不一致的话就会导致编译错误,因此我们还需要去进行编译器相关的设置。

设置编译器

在Building Settings中新增两项用户自定义的设置:

分别是CC和CXX:

CC对应的是自己编译的clang的绝对路径

CXX对应的是自己编译的clang++的绝对路径

接下来在Building Settings中搜索index,将Enable Index-Wihle-Building Functionality的Default改为NO。

以上配置都改完之后,再运行测试工程,没有使用copy修饰的NSString就会报出警告了:

以上。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-03-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 iOS小生活 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
SSM三大框架整合详细总结(Spring+SpringMVC+MyBatis)
使用 SSM ( Spring 、 SpringMVC 和 Mybatis )已经很久了,项目在技术上已经没有什么难点了,基于现有的技术就可以实现想要的功能,当然肯定有很多可以改进的地方。之前没有记录 SSM 整合的过程, 这次刚刚好基于自己的一个小项目重新搭建了一次,而且比项目搭建的要更好一些。以前解决问题的过程和方法并没有及时记录,以后在自己的小项目中遇到我再整理分享一下。 这次 , 先说说 三大框架整合过程 。个人认为使用框架并不是很难,关键要理解其思想,这对于我们提高编程水平很有帮助。不过,如果用都不会,谈思想就变成纸上谈兵了!!! 先技术,再思想。实践出真知。 
MonroeCode
2018/01/10
4K0
SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)
最近在学习Spring+SpringMVC+MyBatis的整合。以下是参考网上的资料自己实践操作的详细步骤。
Java架构师必看
2021/12/19
2.4K0
SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)
猿蜕变17——一文掌握SSM框架搭建姿势
看过之前的蜕变系列文章,相信你对SpringMVC 、Spring、 Mybatis有了一些应用上的感性认识。但是都还是单个使用,并没有放到一起来使用,今天我们讲讲怎样将这三个框架结合起来使用,学会搭建属于自己的开发框架——也就是俗称的SSM框架。
山旮旯的胖子
2020/07/28
5640
猿蜕变17——一文掌握SSM框架搭建姿势
SSM框架原理,作用及使用方法
SSM框架是spring MVC ,spring和mybatis框架的整合,是标准的MVC模式,将整个系统划分为表现层,controller层,service层,DAO层四层
全栈程序员站长
2022/09/14
7900
Maven搭建SpringMVC+Mybatis项目详解
最近比较闲,复习搭建一下项目,这次主要使用spring+SpringMVC+Mybatis。项目持久层使用Mybatis3,控制层使用SpringMVC4.1,使用Spring4.1管理控制器,数据库连接池使用druid数据源,该项数据库暂使用MySQL。(如果是Oracle需修改pom.xml)
yaohong
2019/09/11
6550
超详细图解从0搭建SSM框架【intellij idea】
文章链接:http://blog.csdn.net/w8897282/article/details/71215591
Java团长
2018/08/06
5K0
eclipse 创建maven 项目 动态web工程完整示例 maven 整合springmvc整合mybatis
如果搜索不到内容,或者有报错信息(index downloads are disabled ,search results may be incomplete)
noteless
2018/09/11
9180
eclipse 创建maven 项目 动态web工程完整示例  maven 整合springmvc整合mybatis
Spring+SpringMvc+Mybatis框架集成搭建教程二(依赖配置及框架整合)
(3).在项目的resources文件夹下新建log4j.properties文件
阿豪聊干货
2018/08/09
5400
Spring+SpringMvc+Mybatis框架集成搭建教程二(依赖配置及框架整合)
SSM整合redis
在学习Redis的时候,大家应该知道,JAVA操作redis通常使用的是Jedis,通过java代码来操作redis的数据存储读取等操作,用过的人应该知道,Jedis客户端已经足够简单和轻量级了,但是呢,在此同时,Spring也为Redis提供了支持,就是在Spring-data模块中的Spring-Data-Redis(SDR),它一部分是基于Jedis客户端的API封装,另一部分是对Spring容器的整合。
全栈程序员站长
2022/08/12
6380
SSM整合redis
【SpringMVC】010-SSM框架整合
WEB/INF的lib下,除了导入jstl.jar包,还要导入standard.jar包。另外,解压standard.jar包,把.tld文件放在WEB/INF下;
訾博ZiBo
2025/01/06
640
【SpringMVC】010-SSM框架整合
整合ssm项目开发入门
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
多凡
2019/11/01
3910
SSM框架——Spring+SpringMVC+Mybatis的搭建教程
一:概述 SSM框架在项目开发中经常使用到,相比于SSH框架,它在仅几年的开发中运用的更加广泛。
全栈程序员站长
2022/02/27
7190
SSM框架——Spring+SpringMVC+Mybatis的搭建教程
玩转 SSH(六):SpringMVC + MyBatis 架构搭建(注解版)
一、创建 SSMVCAnnoDemo 项目 点击菜单,选择“File -> New Project” 创建新项目。选择使用 archetype 中的 maven-webapp 模版创建。 输入对应的项
陈树义
2018/04/13
1K0
玩转 SSH(六):SpringMVC + MyBatis 架构搭建(注解版)
SSM 框架整合
原始整合方式 数据表 ```sql create database ssm character set utf8; use ssm; create table account( id in
ruochen
2022/03/17
4.2K0
使用idea2017搭建SSM框架
我这里列出的是搭建完了之后所有的目录和文件,诸位先把目录文件建起来,然后我在给出文件内容
Java学习
2018/07/25
1.1K0
使用idea2017搭建SSM框架
SSM 框架整合
SSM 框架是 (Spring+SpringMVC+MyBatis)的缩写,这个框架是继 SSH(Spring+Struts+Hibernate)之后,目前比较主流的 Java EE 企业级框架,适合搭建各种中小型的企业级应用系统。由于之前也没有Java Web相关的工作经验以及后台系统开发经验,更别说应用框架来开发应用系统了。通过各方面的了解,SSM框架作为轻量级应用框架,速度快,效率高,最主要的还是学习成本更低。这样我们能更快地学习并应用到我们的项目当中!所以,追踪器的项目选择了 SSM这套框架作为我们的后台开发框架!以下,便详细的说明各个框架的优缺点以及 框架的具体搭建步骤
AI码真香
2022/09/13
1.5K0
SSM 框架整合
超详细教程教你如何简易整合SSM框架(源码已上传到GitHub)
先创建Web项目,因为表现层及持久层都是和业务层进行交流,所以我们通过Spring整合SpringMVC,再通过Spring整合Mybatis。
Swingz
2020/12/18
1.5K0
超详细教程教你如何简易整合SSM框架(源码已上传到GitHub)
Maven+SSM框架实现简单的增删改查
spring 使用基本的 JavaBean 来完成以前只可能由 EJB 完成的事情。然而, Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java 应用都可以从 Spring 中受益。 简单来说, Spring 是一个轻量级的控制反转(IoC )和面向切面( AOP )的容器框架。
yaohong
2019/09/11
1.3K0
Maven+SSM框架实现简单的增删改查
基于maven+ssm的增删改查之spring+springmvc+mybatis环境搭建
com.gong.curd.serviceImpl:用于存放service接口的实现类
西西嘛呦
2020/08/26
4490
基于maven+ssm的增删改查之spring+springmvc+mybatis环境搭建
从分析我抓取的60w知乎网民来学习如何在SSM项目中使用Echarts
去年在接触Java爬虫的时候,接触到了一个关于知乎的爬虫。个人觉得写的非常好,当时抓取的效率和成功率还是特别特别高,现在可能知乎反扒做的更好,这个开源知乎爬虫没之前抓取的那么顺利了。我记得当时在我的i7+8g的机器上爬了将近两天,大概爬取了60多w的数据。当然,实际抓取的用户数据数量肯定比这个多,只是持久化过程不同步而已,也就是抓取的好几个用户可能只有一个存入数据库中。
用户2164320
2018/07/24
2.2K0
从分析我抓取的60w知乎网民来学习如何在SSM项目中使用Echarts
相关推荐
SSM三大框架整合详细总结(Spring+SpringMVC+MyBatis)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档