1 /** 2 Date: 2015-2017, 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 dextool.utility; 7 8 import logger = std.experimental.logger; 9 10 import dextool.compilation_db : CompileCommandDB, CompileCommand, orDefaultDb, fromFiles; 11 12 public import dextool.type : AbsolutePath, DextoolVersion, ExitStatusType; 13 14 version (unittest) { 15 import unit_threaded.assertions : shouldEqual; 16 } 17 18 @safe: 19 20 enum PreferLang : string { 21 none = "", 22 c = "-xc", 23 cpp = "-xc++" 24 } 25 26 pure string[] prependDefaultFlags(const string[] in_cflags, const PreferLang lang) { 27 import std.algorithm : canFind; 28 29 immutable syntax_only = "-fsyntax-only"; 30 if (in_cflags.canFind(syntax_only)) { 31 return prependLangFlagIfMissing(in_cflags, lang); 32 } 33 return syntax_only ~ prependLangFlagIfMissing(in_cflags, lang); 34 } 35 36 ///TODO move to clang module. 37 pure string[] prependLangFlagIfMissing(in string[] in_cflags, const PreferLang lang) { 38 import std.algorithm : findAmong; 39 40 auto v = findAmong(in_cflags, [PreferLang.c, PreferLang.cpp]); 41 42 if (v.length == 0) { 43 return [cast(string) lang] ~ in_cflags; 44 } 45 46 return in_cflags.dup; 47 } 48 49 @system unittest { 50 auto cflags = ["-DBEFORE", "-xc++", "-DAND_A_DEFINE", "-I/3906164"]; 51 cflags.shouldEqual(prependLangFlagIfMissing(cflags, PreferLang.c)); 52 } 53 54 /** Apply the visitor on the clang AST derived from the input_file. 55 * 56 * Params: 57 * input_file = path to a file to analyze 58 * cflags = compiler flags to pass on to clang 59 * visitor = to apply on the clang AST 60 * ctx = $(D ClangContext) 61 * 62 * Returns: if the analyze was performed ok or errors occured 63 */ 64 ExitStatusType analyzeFile(VisitorT, ClangContextT)(const AbsolutePath input_file, 65 const string[] cflags, VisitorT visitor, ref ClangContextT ctx) @trusted { 66 import std.file : exists; 67 68 import libclang_ast.ast : ClangAST; 69 import libclang_ast.check_parse_result : hasParseErrors, logDiagnostic; 70 71 if (!exists(input_file)) { 72 logger.errorf("File '%s' do not exist", input_file); 73 return ExitStatusType.Errors; 74 } 75 76 logger.infof("Analyzing '%s'", input_file); 77 78 auto translation_unit = ctx.makeTranslationUnit(input_file, cflags); 79 if (translation_unit.hasParseErrors) { 80 logDiagnostic(translation_unit); 81 logger.error("Compile error..."); 82 return ExitStatusType.Errors; 83 } 84 85 auto ast = ClangAST!VisitorT(translation_unit.cursor); 86 ast.accept(visitor); 87 88 return ExitStatusType.Ok; 89 } 90 91 // this is deprecated 92 public import dextool.compilation_db : fromArgCompileDb; 93 94 /// Version derived from the git archive. 95 import std..string : strip; 96 97 enum dextoolVersion = DextoolVersion(import("version.txt").strip); 98 99 static assert(dextoolVersion.length > 0, "Failed to import version.txt at compile time"); 100 101 private long dextoolBinaryId_; 102 /// A unique identifier for this binary of dextool. 103 long dextoolBinaryId() @trusted nothrow { 104 import std.file : thisExePath; 105 import std.stdio : File; 106 import my.hash : BuildChecksum64, toLong; 107 108 if (dextoolBinaryId_ != 0) 109 return dextoolBinaryId_; 110 111 // just give up after 10 tries. No good reason. Something is just wrong 112 // then so... and assuming nothing else fataly fails if the version now and 113 // then is "0". 114 for (int i = 0; i < 10; ++i) { 115 try { 116 BuildChecksum64 h; 117 foreach (c; File(thisExePath).byChunk(8196)) { 118 h.put(c); 119 } 120 dextoolBinaryId_ = cast(long) h.finish.toLong; 121 break; 122 } catch (Exception e) { 123 } 124 } 125 126 return dextoolBinaryId_; 127 } 128 129 /// Returns. true if `path` is inside `root`. 130 bool isPathInsideRoot(AbsolutePath root, AbsolutePath path) { 131 import std..string : startsWith; 132 import dextool.utility; 133 134 return (cast(string) path).startsWith(cast(string) root); 135 }