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 blob_model : BlobVfs, Uri, Blob; 35 36 import clang.c.Index : CXTranslationUnit_Flags; 37 38 private { 39 Index index; 40 string[] internal_header_arg; 41 string[] syntax_only_arg; 42 } 43 44 /** Access to the virtual filesystem used when instantiating translation 45 * units. 46 */ 47 BlobVfs vfs; 48 49 @disable this(); 50 51 // The context is "heavy" therefor disabling moving. 52 @disable this(this); 53 54 /** Create an instance. 55 * 56 * The binary dextool has clang specified headers attached. Those are feed 57 * to the VFS and used when the flag useInternalHeaders is "yes". To make 58 * them accessable a "-I" parameter with their in-memory location is 59 * supplied to all instantiated translation units. 60 * 61 * TODO from llvm-6.0 -fsyntax-only is default and ignored. The 62 * functionality to prepend with -fsyntax-only should thus be removed. 63 * 64 * Params: 65 * useInternalHeaders = load the VFS with in-memory system headers. 66 * prependParamSyntaxOnly = prepend the flag -fsyntax-only to instantiated translation units. 67 */ 68 this(Flag!"useInternalHeaders" useInternalHeaders, 69 Flag!"prependParamSyntaxOnly" prependParamSyntaxOnly) @trusted { 70 this.index = Index(false, false); 71 this.vfs = new BlobVfs; 72 73 if (useInternalHeaders) { 74 import clang.Compiler : Compiler; 75 76 Compiler compiler; 77 this.internal_header_arg = compiler.extraIncludeFlags; 78 foreach (hdr; compiler.extraHeaders) { 79 auto f = vfs.open(new Blob(Uri(hdr.filename), hdr.content)); 80 } 81 } 82 83 if (prependParamSyntaxOnly) { 84 this.syntax_only_arg = ["-fsyntax-only"]; 85 } 86 } 87 88 /** Create a translation unit from the context. 89 * 90 * The translation unit is NOT kept by the context. 91 */ 92 auto makeTranslationUnit(in string sourceFilename, in string[] commandLineArgs = null, 93 uint options = CXTranslationUnit_Flags.detailedPreprocessingRecord) @safe { 94 import std.array : join; 95 96 auto prependDefaultFlags(string[] in_cflags) { 97 import std.algorithm : canFind; 98 99 if (in_cflags.canFind(syntax_only_arg)) { 100 return in_cflags; 101 } else { 102 return syntax_only_arg ~ in_cflags; 103 } 104 } 105 106 debug logger.trace(sourceFilename); 107 debug logger.trace("Compiler flags: ", commandLineArgs.join(" ")); 108 109 string[] args = prependDefaultFlags(commandLineArgs ~ internal_header_arg); 110 111 debug logger.trace("Internal compiler flags: ", args.join(" ")); 112 113 const uri = Uri(sourceFilename); 114 115 // ensure the file exist in the filesys layer. 116 // it has either been added as an in-memory file by the user or it is 117 // read from the filesystem. 118 if (!vfs.exists(uri)) { 119 vfs.openFromFile(uri); 120 } 121 122 import cpptooling.utility.virtualfilesystem : toClangFiles; 123 124 auto files = vfs.toClangFiles; 125 126 return TranslationUnit.parse(index, sourceFilename, args, files); 127 } 128 } 129 130 @("shall be an instance") 131 @system unittest { 132 import std.typecons : Yes; 133 134 auto ctx = ClangContext(Yes.useInternalHeaders, Yes.prependParamSyntaxOnly); 135 }