离久的小站

创建XCode插件

2018/09/10 Share

前提

创建一个 XCode 插件,首先先要安装 LLVM 。可以参考上一篇文章 《尝试LLVM》。

正文

首先修改源代码目录 /tools/clang/tools 下的 CMakeLists.txt 文件。

在 CMakeLists.txt 的最后面添加上一行。

在 tools 目录下添加 MyPlugin 文件夹,文件夹里面新增两个文件 CMakeLists.txt 和 MyPlugin.cpp

CMakeLists.txt 文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
add_llvm_loadable_module(MyPlugin 
MyPlugin.cpp
PLUGIN_TOOL clang
)

if(LLVM_ENABLE_PLUGINS AND (WIN32 OR CYGWIN))
target_link_libraries(MyPlugin PRIVATE
clangAST
clangBasic
clangFrontend
clangLex
LLVMSupport
)
endif()

MyPlugin.cpp 文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
using namespace clang;
using namespace std;
using namespace llvm;
namespace MyPlugin
{
class MyASTVisitor: public
RecursiveASTVisitor < MyASTVisitor >
{
private:
ASTContext *context;
public:
void setContext(ASTContext &context)
{
this->context = &context;
}

bool VisitDecl(Decl *decl)
{
if (isa < ObjCInterfaceDecl > (decl)) {
ObjCInterfaceDecl *interDecl = (ObjCInterfaceDecl *)decl;
if (interDecl->getSuperClass()) {
string interName = interDecl->getNameAsString();
string superClassName = interDecl->getSuperClass()->getNameAsString();

cout << "-------- ClassName:" << interName << " superClassName:" << superClassName << endl;
}
}

return true;
}
};

class MyASTConsumer: public ASTConsumer
{
private:
MyASTVisitor visitor;
void HandleTranslationUnit(ASTContext &context)
{
visitor.setContext(context);
visitor.TraverseDecl(context.getTranslationUnitDecl());
}
};
class MyASTAction: public PluginASTAction
{
public:
unique_ptr < ASTConsumer > CreateASTConsumer(CompilerInstance & Compiler, StringRef InFile) {
return unique_ptr < MyASTConsumer > (new MyASTConsumer);
}
bool ParseArgs(const CompilerInstance &CI, const std::vector < std::string >& args)
{
return true;
}
};
}
static clang::FrontendPluginRegistry::Add
< MyPlugin::MyASTAction > X("MyPlugin",
"MyPlugin desc");

再次在 llvm_build 目录下 CMake 构建一下。

1
cmake -G Xcode ../llvm -DCMAKE_BUILD_TYPE:STRING=MinSizeRel

重新打开 LLVM.xcodeproj 工程,会发现多了一个 MyPlugin 的编译目标,选中进行编译。

编译成功之后,就可以得到一个 MyPlugin.dylib ,我们将 MyPlugin.dylib 放在桌面上:

下载XcodeHacking.zip并解压

里面有 HackedBuildSystem.xcspec 和 HackedClang.xcplugin 两个文件,这里可能需要修改一下 HackedClang.xcplugin/Contents/Resources/HackedClang.xcspec 文件,将 ExecPath 的值修改为你编译出来的 Clang 的目录:

到解压的 XcodeHacking 目录,将这两个文件用命令行移动到对应的目录下:

1
2
sudo mv HackedClang.xcplugin `xcode-select -print-path`/../PlugIns/Xcode3Core.ideplugin/Contents/SharedSupport/Developer/Library/Xcode/Plug-ins
sudo mv HackedBuildSystem.xcspec `xcode-select -print-path`/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Specifications

上面两个命令行其实就是把两个文件移动到 XCode 应用程序包里面

然后重启 Xcode,点击 Target 的 Build Settings,修改 Compiler for C/C++/Objective-C 项为 Clang LLVM Trunk(不进行第1步中 hack Xcode 操作的话是不会有这个选项的)

然后修改 OTHER_CFLAGS 选项:

1
-Xclang -load -Xclang 你的 MyPlugin.dylib 路径 -Xclang -add-plugin -Xclang MyPlugin

注意:
如果编译中出现一大堆系统库的 symbol not found 错误的话,可以在上述命令的最后手动指定你的 SDK 目录,加上这句:

1
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk

最后编译你的项目,然后快捷键 Command+5 跳到编译报告,就能看到我们插件的输出了。

CATALOG