1 /** 2 Date: 2015-2021, 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 libclang_ast.context; 7 8 import std.typecons : Flag; 9 import logger = std.experimental.logger; 10 11 import clang.c.Index : CXUnsavedFile; 12 13 public import my.path : Path; 14 public import blob_model : BlobVfs; 15 16 version (unittest) { 17 import unit_threaded : Name, shouldEqual; 18 } 19 20 @safe: 21 22 /** Convenient context of items needed to practically create a clang AST. 23 * 24 * "Creating a clang AST" means calling $(D makeTranslationUnit). 25 * 26 * Assumes that the scope of a ClangContext is the same as that stated for a 27 * clang Index. Namely that those translation units that are created are the 28 * same as those that would be linked together into an executable or library. 29 * 30 * Items are: 31 * - An index that all translation units use as input. 32 * - A VFS providing access to the files that the translation unites are 33 * derived from. 34 */ 35 struct ClangContext { 36 import clang.Index : Index; 37 import clang.TranslationUnit : TranslationUnit; 38 39 import blob_model : BlobVfs, Uri, Blob; 40 41 import clang.c.Index : CXTranslationUnit_Flags; 42 43 private { 44 Index index; 45 string[] internal_header_arg; 46 string[] syntax_only_arg; 47 } 48 49 /** Access to the virtual filesystem used when instantiating translation 50 * units. 51 */ 52 BlobVfs vfs; 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 * TODO from llvm-6.0 -fsyntax-only is default and ignored. The 67 * functionality to prepend with -fsyntax-only should thus be removed. 68 * 69 * Params: 70 * useInternalHeaders = load the VFS with in-memory system headers. 71 * prependParamSyntaxOnly = prepend the flag -fsyntax-only to instantiated translation units. 72 */ 73 this(Flag!"useInternalHeaders" useInternalHeaders, 74 Flag!"prependParamSyntaxOnly" prependParamSyntaxOnly) @trusted { 75 this.index = Index(false, false); 76 this.vfs = new BlobVfs; 77 78 if (useInternalHeaders) { 79 import clang.Compiler : Compiler; 80 81 Compiler compiler; 82 this.internal_header_arg = compiler.extraIncludeFlags; 83 foreach (hdr; compiler.extraHeaders) { 84 auto f = vfs.open(new Blob(Uri(hdr.filename), hdr.content)); 85 } 86 } 87 88 if (prependParamSyntaxOnly) { 89 this.syntax_only_arg = ["-fsyntax-only"]; 90 } 91 } 92 93 /** Create a translation unit from the context. 94 * 95 * The translation unit is NOT kept by the context. 96 */ 97 auto makeTranslationUnit(in string sourceFilename, in string[] commandLineArgs = null, 98 uint options = CXTranslationUnit_Flags.detailedPreprocessingRecord) @safe { 99 import std.array : join; 100 101 auto prependDefaultFlags(string[] in_cflags) { 102 import std.algorithm : canFind; 103 104 if (in_cflags.canFind(syntax_only_arg)) { 105 return in_cflags; 106 } else { 107 return syntax_only_arg ~ in_cflags; 108 } 109 } 110 111 debug logger.trace(sourceFilename); 112 debug logger.trace("Compiler flags: ", commandLineArgs.join(" ")); 113 114 string[] args = prependDefaultFlags(commandLineArgs ~ internal_header_arg); 115 116 debug logger.trace("Internal compiler flags: ", args.join(" ")); 117 118 const uri = Uri(sourceFilename); 119 120 // ensure the file exist in the filesys layer. 121 // it has either been added as an in-memory file by the user or it is 122 // read from the filesystem. 123 if (!vfs.exists(uri)) { 124 vfs.openFromFile(uri); 125 } 126 127 auto files = vfs.toClangFiles; 128 129 return TranslationUnit.parse(index, sourceFilename, args, files); 130 } 131 } 132 133 @("shall be an instance") 134 @system unittest { 135 import std.typecons : Yes; 136 137 auto ctx = ClangContext(Yes.useInternalHeaders, Yes.prependParamSyntaxOnly); 138 } 139 140 /** Convert to an array that can be passed on to clang to use as in-memory source code. 141 * 142 * Trusted: operates on files handled by a VirtualFileSystem that ensues that 143 * they exists. The VFS has taken care of validating the files. 144 */ 145 CXUnsavedFile[] toClangFiles(ref BlobVfs vfs) @trusted { 146 import std.algorithm : map; 147 import std.array : array; 148 import std..string : toStringz; 149 150 return vfs.uris.map!((a) { 151 auto s = vfs.get(a).content[]; 152 auto fname = (cast(string) a).toStringz; 153 return CXUnsavedFile(fname, cast(char*) s.ptr, s.length); 154 }).array(); 155 }