in iOS ~ read.

Clang - Use Xcode to make first Clang plugin

How to build clang~

//make directory
cd /opt  
sudo mkdir llvm  
sudo chown `whoami` llvm  
cd llvm  
export LLVM_HOME=`pwd`  
//downlaod the source code,  here i use the https urls, you can also use ssh:
git clone -b release_60 https://github.com/llvm-mirror/llvm.git llvm  
git clone -b release_60 https://github.com/llvm-mirror/clang.git llvm/tools/clang  
git clone -b release_60 https://github.com/llvm-mirror/clang-tools-extra.git llvm/tools/clang/tools/extra  
git clone -b release_60 https://github.com/llvm-mirror/compiler-rt.git llvm/projects/compiler-rt  
  • Pay Attention: if you have problems about permission you can do as followed
  • And then you can continue download~
  • Build the source code
mkdir llvm_build  
cd llvm_build  
cmake -G Xcode ../llvm -DCMAKE_BUILD_TYPE:STRING=MinSizeRel  
  • Open the generate project LLVM, choose Automatically Create Schemes and build the Clang and libClang Scheme, it will wait for a long time(near an hour)

Write your First Plugin

  • 1.Input the code as follow in terminal
cd /opt/llvm/llvm/tools/clang/tools  
mkdir MyPlugin  
  • if you have permission problem, you can do Pay Attention as above.
  • 2.Modify the CMakeLists.txt file under /opt/llvm/llvm/tools/clang/tools path, add add_clang_subdirectory(MyPlugin) code at the bottom of CMakeLists.txt, the bottom of file will be like this:
# We support checking out the clang-tools-extra repository into the 'extra'
# subdirectory. It contains tools developed as part of the Clang/LLVM project
# on top of the Clang tooling platform. We keep them in a separate repository
# to keep the primary Clang repository small and focused.
# It also may be included by LLVM_EXTERNAL_CLANG_TOOLS_EXTRA_SOURCE_DIR.
add_llvm_external_project(clang-tools-extra extra)

# libclang may require clang-tidy in clang-tools-extra.
add_clang_subdirectory(MyPlugin)  
  • 3.Create CMakeLists.txt and MyPlugin.cpp files under MyPlugin directory.
//CMakeLists.txt's content
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's Content
#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");
  • Then, build again
cd /opt/llvm/llvm_build  
cmake -G Xcode ../llvm -DCMAKE_BUILD_TYPE:STRING=MinSizeRel  
  • Reopen the LLVM.xcodeproj, you can find the new scheme MyPlugin, try building and find the output MyPlugin.dylib

Use MyPlugin

  • Create test Code:
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end
  • Move the MyPlugin.dylib, ViewController.h and .m files into the same direcory.
  • use the command to test
/opt/llvm/llvm_build/Debug/bin/clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator11.2.sdk -Xclang -load -Xclang ./MyPlugin.dylib -Xclang -add-plugin -Xclang MyPlugin -c ./ViewController.m
  • You can see the output as followed:

How to use plugin in the Xcode

  • Hack Xcode, donwload XcodeHacking.zip, you will have HackedBuildSystem.xcspec and HackedClang.xcplugin files, you must modify the HackedClang.xcspec file under HackedClang.xcplugin/Contents/Resources/ path, modify the ExecPath to "/opt/llvm/llvm_build/Debug/bin/clang":
//The line will be like this:
ExecPath = "/opt/llvm/llvm_build/Debug/bin/clang";  
  • Move files
//cd to the XcodeHacking directory
cd (XcodeHacking directory)  
//move two files
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  
  • Restart Xcode, modify Compiler for C/C++/Objective-C
  • modify OTHER_CFLAGS
-Xclang -load -Xclang /Users/usernName/Desktop/(Your Directory/)MyPlugin.dylib -Xclang -add-plugin -Xclang MyPlugin
  • Attention: if you have problem about symbol not found you can use this command instead of above:
-Xclang -load -Xclang /Users/usernName/Desktop/(Your Directory/)MyPlugin.dylib -Xclang -add-plugin -Xclang MyPlugin -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator11.2.sdk
  • Build Success:
comments powered by Disqus