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.ctestdouble.frontend.xml; 11 12 import logger = std.experimental.logger; 13 14 static import dextool.xml; 15 16 import dextool.compilation_db; 17 import dextool.type; 18 19 import dextool.plugin.ctestdouble.frontend.types; 20 21 alias readRawConfig = dextool.xml.readRawConfig!(XmlConfig, parseRawConfig); 22 23 /** Store the input in a configuration file to make it easy to regenerate the 24 * test double. 25 */ 26 ref AppT makeXmlConfig(AppT)(ref AppT app, CompileCommandFilter compiler_flag_filter, 27 FilterSymbol restrict_sym, FilterSymbol exclude_sym) { 28 import std.algorithm : joiner, copy, map; 29 import std.conv : to; 30 import std.range : chain; 31 import std.xml; 32 import dextool.utility : dextoolVersion; 33 import dextool.xml : makePrelude; 34 35 auto doc = new Document(new Tag("dextool")); 36 doc.tag.attr["version"] = dextoolVersion.get; 37 { 38 auto compiler_tag = new Element("compiler_flag_filter"); 39 compiler_tag.tag.attr["skip_compiler_args"] 40 = compiler_flag_filter.skipCompilerArgs.to!string(); 41 foreach (value; compiler_flag_filter.filter) { 42 auto tag = new Element("exclude"); 43 tag ~= new Text(value); 44 compiler_tag ~= tag; 45 } 46 doc ~= compiler_tag; 47 } 48 49 if (restrict_sym.hasSymbols || exclude_sym.hasSymbols) { 50 auto symbol_tag = new Element("symbol_filter"); 51 foreach (value; chain(restrict_sym.range.map!((a) { 52 auto tag = new Element("restrict"); 53 tag ~= new Text(a.key); 54 return tag; 55 }), exclude_sym.range.map!((a) { 56 auto tag = new Element("exclude"); 57 tag ~= new Text(a.key); 58 return tag; 59 }))) { 60 symbol_tag ~= value; 61 } 62 doc ~= symbol_tag; 63 } 64 65 makePrelude(app); 66 doc.pretty(4).joiner("\n").copy(app); 67 68 return app; 69 } 70 71 XmlConfig parseRawConfig(T)(T xml) @trusted { 72 import std.conv : to, ConvException; 73 import std.xml; 74 75 DextoolVersion version_; 76 int skip_flags = 1; 77 RawCliArguments command; 78 FilterClangFlag[] filter_clang_flags; 79 FilterSymbol restrict_symbols; 80 FilterSymbol exclude_symbols; 81 82 if (auto tag = "version" in xml.tag.attr) { 83 version_ = DextoolVersion(*tag); 84 } 85 86 // dfmt off 87 xml.onStartTag["compiler_flag_filter"] = (ElementParser filter_flags) { 88 if (auto tag = "skip_compiler_args" in xml.tag.attr) { 89 try { 90 skip_flags = (*tag).to!int; 91 } 92 catch (ConvException ex) { 93 logger.info(ex.msg); 94 logger.info(" using fallback '1'"); 95 } 96 } 97 98 xml.onEndTag["exclude"] = (const Element e) { filter_clang_flags ~= FilterClangFlag(e.text()); }; 99 }; 100 xml.onStartTag["symbol_filter"] = (ElementParser filter_sym) { 101 xml.onEndTag["restrict"] = (const Element e) { restrict_symbols.put(e.text()); }; 102 xml.onEndTag["exclude"] = (const Element e) { exclude_symbols.put(e.text()); }; 103 }; 104 // dfmt on 105 xml.parse(); 106 107 return XmlConfig(version_, skip_flags, command, filter_clang_flags, 108 restrict_symbols, exclude_symbols); 109 } 110 111 /** Extracted configuration data from an XML file. 112 * 113 * It is not inteded to be used as is but rather further processed. 114 */ 115 struct XmlConfig { 116 import dextool.type : DextoolVersion, FilterClangFlag; 117 118 DextoolVersion version_; 119 int skipCompilerArgs; 120 RawCliArguments command; 121 FilterClangFlag[] filterClangFlags; 122 123 /// Only a symbol that matches this 124 FilterSymbol restrictSymbols; 125 /// Remove symbols matching this 126 FilterSymbol excludeSymbols; 127 } 128 129 @("Converted a raw xml config without loosing any configuration data or version") 130 unittest { 131 import unit_threaded : shouldEqual; 132 import std.xml; 133 134 string raw = ` 135 <?xml version="1.0" encoding="UTF-8"?> 136 <dextool version="test"> 137 <!-- comment is ignored --> 138 <command>tag is ignored</command> 139 <compiler_flag_filter skip_compiler_args="2"> 140 <exclude>foo</exclude> 141 <exclude>-foo</exclude> 142 <exclude>--foo</exclude> 143 <exclude>-G 0</exclude> 144 </compiler_flag_filter> 145 </dextool>`; 146 147 auto xml = new DocumentParser(raw); 148 auto p = parseRawConfig(xml); 149 150 p.version_.get.shouldEqual("test"); 151 p.command.payload.shouldEqual(string[].init); 152 p.skipCompilerArgs.shouldEqual(2); 153 p.filterClangFlags.shouldEqual([ 154 FilterClangFlag("foo"), FilterClangFlag("-foo"), 155 FilterClangFlag("--foo"), FilterClangFlag("-G 0") 156 ]); 157 } 158 159 /// The raw arguments from the command line. 160 struct RawCliArguments { 161 string[] payload; 162 alias payload this; 163 }