1 /** 2 Date: 2015-2016, Joakim Brännström 3 License: MPL-2, Mozilla Public License 2.0 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 */ 6 module cpptooling.analyzer.clang.context; 7 8 import std.typecons : Flag; 9 import logger = std.experimental.logger; 10 11 version (unittest) { 12 import unit_threaded : Name, shouldEqual; 13 } 14 15 @safe: 16 17 /** Convenient context of items needed to practically create a clang AST. 18 * 19 * "Creating a clang AST" means calling $(D makeTranslationUnit). 20 * 21 * Assumes that the scope of a ClangContext is the same as that stated for a 22 * clang Index. Namely that those translation units that are created are the 23 * same as those that would be linked together into an executable or library. 24 * 25 * Items are: 26 * - An index that all translation units use as input. 27 * - A VFS providing access to the files that the translation unites are 28 * derived from. 29 */ 30 struct ClangContext { 31 import clang.Index : Index; 32 import clang.TranslationUnit : TranslationUnit; 33 34 import cpptooling.utility.virtualfilesystem : VirtualFileSystem, FileName, 35 Content; 36 37 import clang.c.Index : CXTranslationUnit_Flags; 38 39 private { 40 Index index; 41 string[] internal_header_arg; 42 string[] syntax_only_arg; 43 } 44 45 /** Access to the virtual filesystem used when instantiating translation 46 * units. 47 * 48 * Note: 49 * NOT using the abbreviation VFS because it is not commonly known. Better 50 * to be specific. 51 */ 52 VirtualFileSystem virtualFileSystem; 53 54 @disable this(); 55 56 // The context is "heavy" therefor disabling moving. 57 @disable this(this); 58 59 /** Create an instance. 60 * 61 * The binary dextool has clang specified headers attached. Those are feed 62 * to the VFS and used when the flag useInternalHeaders is "yes". To make 63 * them accessable a "-I" parameter with their in-memory location is 64 * supplied to all instantiated translation units. 65 * 66 * Params: 67 * useInternalHeaders = load the VFS with in-memory system headers. 68 * prependParamSyntaxOnly = prepend the flag -fsyntax-only to instantiated translation units. 69 */ 70 this(Flag!"useInternalHeaders" useInternalHeaders, 71 Flag!"prependParamSyntaxOnly" prependParamSyntaxOnly) { 72 index = Index(false, false); 73 virtualFileSystem = VirtualFileSystem(); 74 75 if (useInternalHeaders) { 76 import cpptooling.utility.virtualfilesystem : FileName, Content; 77 import clang.Compiler : Compiler; 78 79 Compiler compiler; 80 internal_header_arg = ["-I" ~ compiler.extraIncludePath]; 81 foreach (hdr; compiler.extraHeaders) { 82 virtualFileSystem.openInMemory(cast(FileName) hdr.filename); 83 virtualFileSystem.write(cast(FileName) hdr.filename, cast(Content) hdr.content); 84 } 85 } 86 87 if (prependParamSyntaxOnly) { 88 syntax_only_arg = ["-fsyntax-only"]; 89 } 90 } 91 92 /** Create a translation unit from the context. 93 * 94 * The translation unit is NOT kept by the context. 95 */ 96 auto makeTranslationUnit(in string sourceFilename, in string[] commandLineArgs = null, 97 uint options = CXTranslationUnit_Flags.detailedPreprocessingRecord) @safe { 98 import std.array : join; 99 100 auto prependDefaultFlags(string[] in_cflags) { 101 import std.algorithm : canFind; 102 103 if (in_cflags.canFind(syntax_only_arg)) { 104 return in_cflags; 105 } else { 106 return syntax_only_arg ~ in_cflags; 107 } 108 } 109 110 debug logger.trace("Compiler flags: ", commandLineArgs.join(" ")); 111 112 string[] args = prependDefaultFlags(commandLineArgs ~ internal_header_arg); 113 114 debug logger.trace("Internal compiler flags: ", args.join(" ")); 115 116 // ensure the file exist in the filesys layer. 117 // it has either been added as an in-memory file by the user or it is 118 // read from the filesystem. 119 virtualFileSystem.open(cast(FileName) sourceFilename); 120 121 import cpptooling.utility.virtualfilesystem : toClangFiles; 122 123 auto files = virtualFileSystem.toClangFiles; 124 125 return TranslationUnit.parse(index, sourceFilename, args, files); 126 } 127 } 128 129 @("shall be an instance") 130 unittest { 131 import std.typecons : Yes; 132 133 auto ctx = ClangContext(Yes.useInternalHeaders, Yes.prependParamSyntaxOnly); 134 }