1 /** 2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved. 3 License: MPL-2 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 This Source Code Form is subject to the terms of the Mozilla Public License, 7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain 8 one at http://mozilla.org/MPL/2.0/. 9 */ 10 module dextool.plugin.intercept.frontend.raw_args; 11 12 import std.typecons : Nullable; 13 14 import logger = std.experimental.logger; 15 16 import dextool.compilation_db; 17 import dextool.type; 18 import dextool.utility; 19 20 import dextool.plugin.types; 21 22 import dextool.plugin.intercept.type; 23 24 struct RawConfiguration { 25 import std.getopt : getopt, GetoptResult; 26 27 Nullable!XmlConfig xmlConfig; 28 29 string[] testDoubleInclude; 30 string[] inFiles; 31 string[] cflags; 32 string[] compileDb; 33 string header; 34 string headerFile; 35 string mainFileName = "intercept"; 36 string prefix = "intercept_"; 37 string stripInclude; 38 string out_; 39 string config; 40 bool help; 41 bool shortPluginHelp; 42 43 private GetoptResult help_info; 44 45 void parse(string[] args) { 46 static import std.getopt; 47 48 try { 49 // dfmt off 50 help_info = getopt(args, std.getopt.config.keepEndOfOptions, 51 "short-plugin-help", "short description of the plugin", &shortPluginHelp, 52 "main-fname", "Used as part of filename for generated files [default: intercept]", &mainFileName, 53 "out", "directory for generated files [default: ./]", &out_, 54 "compile-db", "Retrieve compilation parameters from the file", &compileDb, 55 "prefix", "Prefix all function calls to the intercepted target [default: intercept_]", &prefix, 56 "strip-incl", "A regex used to strip the include paths", &stripInclude, 57 "header", "Prepend generated files with the string", &header, 58 "header-file", "Prepend generated files with the header read from the file", &headerFile, 59 "td-include", "User supplied includes used instead of those found", &testDoubleInclude, 60 "in", "Input file to parse (at least one)", &inFiles, 61 "config", "Use configuration file", &config); 62 // dfmt on 63 help = help_info.helpWanted; 64 } 65 catch (std.getopt.GetOptException ex) { 66 logger.error(ex.msg); 67 help = true; 68 } 69 70 // default arguments 71 if (stripInclude.length == 0) { 72 stripInclude = r".*/(.*)"; 73 logger.trace("--strip-incl: using default regex to strip include path (basename)"); 74 } 75 76 if (config.length != 0) { 77 xmlConfig = readRawConfig(FileName(config)); 78 if (xmlConfig.isNull) { 79 help = true; 80 } 81 } 82 83 import std.algorithm : find; 84 import std.array : array; 85 import std.range : drop; 86 87 // at this point args contain "what is left". What is interesting then is those after "--". 88 cflags = args.find("--").drop(1).array(); 89 } 90 91 void printHelp() { 92 import std.getopt : defaultGetoptPrinter; 93 import std.stdio : writeln; 94 95 defaultGetoptPrinter("Usage: dextool intercept [options] [--in=] [-- CFLAGS...]", 96 help_info.options); 97 98 writeln(" 99 REGEX 100 The regex syntax is found at http://dlang.org/phobos/std_regex.html 101 102 Information about --strip-incl. 103 Default regexp is: .*/(.*) 104 105 To allow the user to selectively extract parts of the include path dextool 106 applies the regex and then concatenates all the matcher groups found. It is 107 turned into the replacement include path. 108 109 Important to remember then is that this approach requires that at least one 110 matcher group exists. 111 "); 112 } 113 } 114 115 /// Symbols to intercept. 116 @safe struct Symbols { 117 InterceptSymbol[SymbolName] syms; 118 alias syms this; 119 120 bool contains(string symbol) { 121 if (SymbolName(symbol) in syms) 122 return true; 123 return false; 124 } 125 126 bool hasSymbols() { 127 return syms.length != 0; 128 } 129 130 void put(InterceptSymbol data) { 131 syms[data.funcName] = data; 132 } 133 } 134 135 /// A symbol to intercept. 136 @safe struct InterceptSymbol { 137 SymbolName funcName; 138 string prefix; 139 } 140 141 /** Extracted configuration data from an XML file. 142 * 143 * It is not inteded to be used as is but rather further processed. 144 */ 145 struct XmlConfig { 146 import dextool.type : DextoolVersion, RawCliArguments, FilterClangFlag; 147 148 DextoolVersion version_; 149 int skipCompilerArgs; 150 RawCliArguments command; 151 FilterClangFlag[] filterClangFlags; 152 153 /// Symbols to intercept. 154 Symbols symbols; 155 } 156 157 auto parseRawConfig(T)(T xml) @trusted { 158 import std.conv : to, ConvException; 159 import std.xml; 160 import dextool.utility : DextoolVersion; 161 import dextool.type : RawCliArguments, FilterClangFlag; 162 163 DextoolVersion version_; 164 int skip_flags = 1; 165 RawCliArguments command; 166 FilterClangFlag[] filter_clang_flags; 167 Symbols syms; 168 169 if (auto tag = "version" in xml.tag.attr) { 170 version_ = *tag; 171 } 172 173 // dfmt off 174 xml.onStartTag["compiler_flag_filter"] = (ElementParser filter_flags) { 175 if (auto tag = "skip_compiler_args" in xml.tag.attr) { 176 try { 177 skip_flags = (*tag).to!int; 178 } 179 catch (ConvException ex) { 180 logger.info(ex.msg); 181 logger.info(" using fallback '1'"); 182 } 183 } 184 185 xml.onEndTag["exclude"] = (const Element e) { filter_clang_flags ~= FilterClangFlag(e.text()); }; 186 }; 187 xml.onStartTag["intercept"] = (ElementParser filter_sym) { 188 xml.onEndTag["func"] = (const Element e) { 189 if (auto pref = "prefix" in e.tag.attr) { 190 auto sym = InterceptSymbol(SymbolName(e.text), *pref); 191 syms.put(sym); 192 } else { 193 logger.warningf("xml-config: missing the attribute prefix for intercept func '%s'", e.text); 194 } 195 }; 196 }; 197 // dfmt on 198 xml.parse(); 199 200 return XmlConfig(version_, skip_flags, command, filter_clang_flags, syms); 201 } 202 203 static import dextool.xml; 204 205 alias readRawConfig = dextool.xml.readRawConfig!(XmlConfig, parseRawConfig);