Clang Preprocessor 类的创建
参考:
Create a working compiler with the LLVM framework, Part 2
How to parse C programs with Clang: A tutorial
[Clang,libClang] exercise1 : FileManager
注意:此篇笔记各类之构造函数源码来自 llvm/clang doxygen 5.0.0 svn,与现行版本(此时为 3.9.1)有一定出入,其中主要是现行版本有些参数是 llvm::IntrusiveRefCntPtr(llvm 实现的 smart pointer)而不是 std::shared_ptr
一、Preprocessor 的构造函数
前端由许多部分组成,其中第一部分通常是一个 lexer,clang 中 Preprocessor 类是 lexer 的 main interface。出于性能考虑,clang 没有独立的预处理器程序,而是在 lexing 的过程中进行预处理。
Preprocessor 的构造函数如下所示:
1 Preprocessor::Preprocessor( 2 std::shared_ptr<PreprocessorOptions> PPOpts, // constructor: PreprocessorOptions() 3 DiagnosticsEngine& diags, 4 LangOptions& opts, // constructor: LangOptions() 5 // Keep track of the various options that can be enabled, 6 // which controls the dialect of C or C++ that is accepted 7 SourceManager& SM, 8 HeaderSearch& Headers, 9 ModuleLoader& TheModuleLoader, 10 IdentifierInfoLookup* IILookup = nullptr, 11 bool OwnsHeaderSearch = false, 12 TranslationUnitKind TUKind = TU_Complete 13 )
DiagnosticsEngine : 用来给用户报告错误和警告信息。构造函数如下:
1 DiagnosticsEngine::DiagnosticsEngine( 2 IntrusiveRefCntPtr<DiagnosticIDs> Diags, // constructor: DiagnosticIDs() 3 // used for handling and querying diagnostic IDs 4 DiagnosticOptions* DiagOpts, // constructor: DiagnosticOptions() 5 // Options for controlling the compiler diagnostics engine 6 DiagnosticConsumer* client = nullptr, 7 bool ShouldOwnClient = true 8 )
其中 DiagnosticConsumer 是一个抽象接口,由前端的 clients 实现,用来 formats and prints fully processed diagnostics。clang 内置一个 TextDiagnosticsConsumer 类,将错误和警告信息写到 console 上,clang binary 用的 DiagnosticConsumer 也是这个类。TextDiagnosticsConsumer 的构造函数如下:
1 TextDiagnosticPrinter::TextDiagnosticPrinter( 2 raw_ostream& os, // llvm::outs() returns a reference to a raw_ostream for standard output 3 DiagnosticOptions* diags, 4 bool OwnsOutputStream = false // within destructor:(OS is the member, initialized with os) 5 // if (OwnsOutputStream) delete &OS 6 )
SourceManager :handles loading and caching of source files into memory。构造函数如下:
1 SourceManager::SourceManager(vim 2 DiagnosticsEngine& Diag, 3 FileManager& FileMgr, 4 bool UserFilesAreVolatile = false 5 )
FileManager :实现了对文件系统查找、文件系统缓存、目录查找管理的支持。构造函数如下:
1 FileManager::FileManager( 2 const FileSystemOptions& FileSystemOpts, // use default constructor 3 IntrusiveRefCntPtr<vfs::FileSystem> FS = nullptr 4 )
HeaderSearch :Encapsulates the information needed to find the file referenced by a #include or #include_next, (sub-)framework lookup, etc。构造函数如下:
1 HeaderSearch::HeaderSearch( 2 std::shared_ptr<HeaderSearchOptions> HSOpts, // constructor: HeaderSearchOptions::HeaderSearchOptions(StringRef _Sysroot = "/") 3 SourceManager& SourceMgr, 4 DiagnosticsEngine& Diags, 5 const LangOptions& LangOpts, 6 const TargetInfo* Target 7 )
TargetInfo :Exposes information about the current target。 其构造函数为 protected,因此需要调用工厂函数 static TargetInfo* TargetInfo::CreateTargetInfo(DiagnosticsEngine &Diags, const std::shared_ptr<TargetOptions>& Opts) ,其中 TargetOptions 类包含 target 的相关信息,如 CPU、ABI 等。类中有一个属性 Triple 用以定义 target 的架构。Triple 是一个 string,形如 i386-apple-darwin,通过 llvm::sys::getDefaultTargetTriple() 可以获得编译 llvm 的机器的 host triple。
ModuleLoader:描述了 module loader 的抽象接口。Module loader 负责解析一个 module name(如“std”),将其与实际的 module file 联系起来,并加载该 module。CompilerInstance 便是一个实现了该接口的 module loader。
二、通过 CompilerInstance 创建 Preprocessor
比起手写 Preprocessor,CompilerInstance 更加实用一些。CompilerInstance 主要有两个作用:(1)管理运行编译器所必须的各个对象,如 preprocessor、target information、AST context 等;(2)提供创建和操作常用 Clang 对象的有用方法。下面是其类定义的一部分:
1 class CompilerInstance : public ModuleLoader { 2 /// The options used in this compiler instance. 3 std::shared_ptr<CompilerInvocation> Invocation; 4 /// The diagnostics engine instance. 5 IntrusiveRefCntPtr<DiagnosticsEngine> Diagnostics; 6 /// The target being compiled for. 7 IntrusiveRefCntPtr<TargetInfo> Target; 8 /// The file manager. 9 IntrusiveRefCntPtr<FileManager> FileMgr; 10 /// The source manager. 11 IntrusiveRefCntPtr<SourceManager> SourceMgr; 12 /// The preprocessor. 13 std::shared_ptr<Preprocessor> PP; 14 /// The AST context. 15 IntrusiveRefCntPtr<ASTContext> Context; 16 /// An optional sema source that will be attached to sema. 17 IntrusiveRefCntPtr<ExternalSemaSource> ExternalSemaSrc; 18 /// The AST consumer. 19 std::unique_ptr<ASTConsumer> Consumer; 20 /// The semantic analysis object. 21 std::unique_ptr<Sema> TheSema; 22 /// ... 23 };
下列代码通过 CompilerInstance 来创建 Preprocessor:
1 #include <memory> 2 3 #include "clang/Basic/LangOptions.h" 4 #include "clang/Basic/TargetInfo.h" 5 #include "clang/Frontend/CompilerInstance.h" 6 7 int main() { 8 clang::CompilerInstance ci; 9 10 ci.createDiagnostics(); 11 12 std::shared_ptr<clang::TargetOptions> pTargetOptions = 13 std::make_shared<clang::TargetOptions>(); 14 pTargetOptions->Triple = llvm::sys::getDefaultTargetTriple(); 15 clang::TargetInfo *pTargetInfo = 16 clang::TargetInfo::CreateTargetInfo(ci.getDiagnostics(), pTargetOptions); 17 ci.setTarget(pTargetInfo); 18 19 ci.createFileManager(); 20 ci.createSourceManager(ci.getFileManager()); 21 ci.createPreprocessor(clang::TU_Complete); 22 23 return 0; 24 }
首先创建 DiagnosticsEngine(通过 createDiagnostics()),然后创建并设置 TargetInfo,然后依次创建 FileManager(通过 createFileManager()),SourceManager(通过 createSourceManager (FileManager &FileMgr)),最后创建 Preprocessor(createPreprocessor(TranslationUnitKind))。
三、FileManager 与 SourceManager
FileManager:
posted on 2017-02-23 17:43 westwindrest 阅读(753) 评论(0) 编辑 收藏 举报