前面一篇文章 LLVM & Clang 入門 講了如何編寫一個 Clang 外掛,然後將外掛編譯成一個.dylib
的動態連結庫。整合到 Xcode 中就可以看到效果(正確的結果)。
在得到正確結果的過程中,必不可少的一步就是Debug
,沒有任何程式是一蹴而就的,除非你printf
一個"Hello, World!"
,說不定你的world
還寫成了word
。
在使用Plugin
的模式下我們是不能打斷點進行 Debug 的,但是我們可以在程式碼中加日誌,然後在終端中執行命令看日誌進行 Debug。這種低效率(Low)的方式是你想要的嗎?顯然不是。
我們只需要把.dylib
動態庫變成可執行檔案
就能打斷點 debug。LibTooling 或許是一個不錯的選擇。使用 LibTooling 的話,我們只需要改動很少部分的程式碼就可以。
LibTooling 簡介:
LibTooling 是一個獨立的庫,它允許使用者很方便地搭建屬於你自己的編譯器前端工具,它基於 C++ 介面,提供給使用者強大全面的 AST 解析和控制能力,同時由於它與 Clang 的核心過於接近導致它的版本相容能力比 libclang 差得多,Clang 的變動很容易影響到 LibTooling。libTooling 還提供了完整的引數解析方案,可以很方便的構建一個獨立的命令列工具。
建立 LibTooling 專案及程式碼調整
為了方便,我們直接建立一個可執行的LibTooling
專案,我們可以建立一個名為QTPluginTooling
的專案。
-
建立過程跟 建立外掛 步驟差不多,前面 3 步都是一樣的,只需要把
QTPlugin
替換為QTPluginTooling
就可以。 -
只是在第 4 步略有不同,
QTPluginTooling
目錄下的CMakeLists.txt
的檔案內容為set(LLVM_LINK_COMPONENTS Support ) add_clang_executable(QTPluginTooling QTPluginTooling.cpp ) target_link_libraries(QTPluginTooling PRIVATE clangAST clangBasic clangDriver clangFormat clangLex clangParse clangSema clangFrontend clangTooling clangToolingCore clangRewrite clangRewriteFrontend ) if (UNIX) set(CLANGXX__LING_OR_COPY create_symlink) else() set(CLANGXX_LINK_OR_COPY copy) endif() 複製程式碼
-
在
llvm_xcode
目錄下執行$ cmake -G Xcode ../llvm
,重新生成一下Xcode
專案。Tooling
專案在 Xcode 的Clang executables
目錄下可以找到。 -
將之前 Plugin 的程式碼複製過來,新增三個標頭檔案
#include "clang/Tooling/CommonOptionsParser.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Tooling/Tooling.h" 複製程式碼
-
新增一個名稱空間
using namespace clang::tooling; 複製程式碼
-
將
QTASTAction
的繼承改為繼承至ASTFrontendAction
。 -
將
FrontendPluginRegistry
註冊外掛的方式註釋。更改為main()
函式方式static llvm::cl::OptionCategory OptsCategory("QTPlugin"); int main(int argc, const char **argv) { CommonOptionsParser op(argc, argv, OptsCategory); ClangTool Tool(op.getCompilations(), op.getSourcePathList()); return Tool.run(newFrontendActionFactory<QTPlugin::QTASTAction>().get()); } 複製程式碼
最後整個檔案的內容可以在 QTPluginTooling.cpp 看到。
輸入源
如果這時候就run
的話則會直接退出。這是因為沒有“輸入源”。我們可以在QTPluginTooling
的Scheme
加入。
/Users/laiyoung_/Desktop/Plugin/ViewController.m
--
-isysroot
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk
-isystem
-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/10.0.0/include
-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1
-I/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include
-F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks
複製程式碼
引數解釋:
上面在
--
後面的引數,是傳遞給CI
的Compilation DataBase
的,而不是這個命令列工具本身的。比如我們的ViewController.m
,因為有#import <UIKit/UIKit.h>
這麼一條語句,以及繼承了UIViewController
,那麼語法分析器(Sema)讀到這裡的時候就需要知道UIViewController
的定義是從哪裡來的,換句話說就是它需要找到定義UIViewController
的地方。怎麼找呢?通過指定的-I
、-F
這些引數指定的目錄來尋找。--
後面的引數,可以理解為如果你要編譯ViewController.m
需要什麼引數,那麼這個後面就要傳遞什麼引數給我們的QTPlugin
,否則就會看到Console
裡打出找不到xxx
定義或者xxx.h
檔案的錯誤。當然因為一般的編譯指令,會有-c
引數指定原始檔,但是--
後面並不需要,因為我們在--
前面就指定了。--
這種傳參的方式還有另外一種方法,使用-extra-arg="xxxx"
的方式指定編譯引數,這樣就不需要--
了。
-extra-arg="-Ixxxxxx"
-extra-arg="-Fxxxxxx"
-extra-arg="-isysroot xxxxxx"
xxxxxx表示的路徑
複製程式碼
最終效果:
參考文章:
如有內容錯誤,歡迎 issue 指正。
轉載請註明出處!