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 } else { 33 return syntax_only ~ prependLangFlagIfMissing(in_cflags, lang); 34 } 35 } 36 37 ///TODO move to clang module. 38 pure string[] prependLangFlagIfMissing(in string[] in_cflags, const PreferLang lang) { 39 import std.algorithm : findAmong; 40 41 auto v = findAmong(in_cflags, [PreferLang.c, PreferLang.cpp]); 42 43 if (v.length == 0) { 44 return [cast(string) lang] ~ in_cflags; 45 } 46 47 return in_cflags.dup; 48 } 49 50 @system unittest { 51 auto cflags = ["-DBEFORE", "-xc++", "-DAND_A_DEFINE", "-I/3906164"]; 52 cflags.shouldEqual(prependLangFlagIfMissing(cflags, PreferLang.c)); 53 } 54 55 /** Apply the visitor on the clang AST derived from the input_file. 56 * 57 * Params: 58 * input_file = path to a file to analyze 59 * cflags = compiler flags to pass on to clang 60 * visitor = to apply on the clang AST 61 * ctx = $(D ClangContext) 62 * 63 * Returns: if the analyze was performed ok or errors occured 64 */ 65 ExitStatusType analyzeFile(VisitorT, ClangContextT)(const AbsolutePath input_file, 66 const string[] cflags, VisitorT visitor, ref ClangContextT ctx) @trusted { 67 import std.file : exists; 68 69 import cpptooling.analyzer.clang.ast : ClangAST; 70 import cpptooling.analyzer.clang.check_parse_result : hasParseErrors, logDiagnostic; 71 72 if (!exists(input_file)) { 73 logger.errorf("File '%s' do not exist", input_file); 74 return ExitStatusType.Errors; 75 } 76 77 logger.infof("Analyzing '%s'", input_file); 78 79 auto translation_unit = ctx.makeTranslationUnit(input_file, cflags); 80 if (translation_unit.hasParseErrors) { 81 logDiagnostic(translation_unit); 82 logger.error("Compile error..."); 83 return ExitStatusType.Errors; 84 } 85 86 auto ast = ClangAST!VisitorT(translation_unit.cursor); 87 ast.accept(visitor); 88 89 return ExitStatusType.Ok; 90 } 91 92 // this is deprecated 93 public import dextool.compilation_db : fromArgCompileDb; 94 95 /// Version derived from the git archive. 96 import std.string : strip; 97 98 enum dextoolVersion = DextoolVersion(import("version.txt").strip); 99 100 static assert(dextoolVersion.length > 0, "Failed to import version.txt at compile time"); 101 102 /// Returns. true if `path` is inside `root`. 103 bool isPathInsideRoot(AbsolutePath root, AbsolutePath path) { 104 import std.string : startsWith; 105 import dextool.utility; 106 107 return (cast(string) path).startsWith(cast(string) root); 108 }