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 }