1 module unit_threaded.dub; 2 3 import unit_threaded.runtime: Options; 4 import std.json: JSONValue; 5 6 7 struct DubPackage { 8 string name; 9 string path; 10 string mainSourceFile; 11 string targetFileName; 12 string[] flags; 13 string[] importPaths; 14 string[] stringImportPaths; 15 string[] files; 16 string targetType; 17 string[] versions; 18 string[] dependencies; 19 string[] libs; 20 bool active; 21 } 22 23 struct DubInfo { 24 DubPackage[] packages; 25 } 26 27 DubInfo getDubInfo(string jsonString) @trusted { 28 import std.json: parseJSON; 29 import std.algorithm: map, filter; 30 import std.array: array; 31 32 auto json = parseJSON(jsonString); 33 auto packages = json.byKey("packages").array; 34 return DubInfo(packages. 35 map!(a => DubPackage(a.byKey("name").str, 36 a.byKey("path").str, 37 a.getOptional("mainSourceFile"), 38 a.getOptional("targetFileName"), 39 a.byKey("dflags").jsonValueToStrings, 40 a.byKey("importPaths").jsonValueToStrings, 41 a.byKey("stringImportPaths").jsonValueToStrings, 42 a.byKey("files").jsonValueToFiles, 43 a.getOptional("targetType"), 44 a.getOptionalList("versions"), 45 a.getOptionalList("dependencies"), 46 a.getOptionalList("libs"), 47 a.byOptionalKey("active", true), //true for backwards compatibility 48 )). 49 filter!(a => a.active). 50 array); 51 } 52 53 private string[] jsonValueToFiles(JSONValue files) @trusted { 54 import std.algorithm: map, filter; 55 import std.array: array; 56 57 return files.array. 58 filter!(a => ("type" in a && a.byKey("type").str == "source") || 59 ("role" in a && a.byKey("role").str == "source") || 60 ("type" !in a && "role" !in a)). 61 map!(a => a.byKey("path").str). 62 array; 63 } 64 65 private string[] jsonValueToStrings(JSONValue json) @trusted { 66 import std.algorithm: map, filter; 67 import std.array: array; 68 69 return json.array.map!(a => a.str).array; 70 } 71 72 73 private auto byKey(JSONValue json, in string key) @trusted { 74 return json.object[key]; 75 } 76 77 private auto byOptionalKey(JSONValue json, in string key, bool def) { 78 import std.conv: to; 79 auto value = json.object; 80 return key in value ? value[key].boolean : def; 81 } 82 83 //std.json has no conversion to bool 84 private bool boolean(JSONValue json) @trusted { 85 import std.exception: enforce; 86 import std.json: JSONException, JSON_TYPE; 87 enforce!JSONException(json.type == JSON_TYPE.TRUE || json.type == JSON_TYPE.FALSE, 88 "JSONValue is not a boolean"); 89 return json.type == JSON_TYPE.TRUE; 90 } 91 92 private string getOptional(JSONValue json, in string key) @trusted { 93 auto aa = json.object; 94 return key in aa ? aa[key].str : ""; 95 } 96 97 private string[] getOptionalList(JSONValue json, in string key) @trusted { 98 auto aa = json.object; 99 return key in aa ? aa[key].jsonValueToStrings : []; 100 } 101 102 103 DubInfo getDubInfo(in bool verbose) { 104 import core.exception: RangeError; 105 import std.conv: text; 106 import std.algorithm: find; 107 import std.stdio: writeln; 108 import std.exception: enforce; 109 import std.process: execute; 110 import std.array: join; 111 112 if(verbose) 113 writeln("Running dub describe"); 114 115 immutable args = ["dub", "describe", "-c", "unittest"]; 116 immutable res = execute(args); 117 enforce(res.status == 0, text("Could not execute ", args.join(" "), ":\n", res.output)); 118 try { 119 return getDubInfo(res.output.find("{")); 120 } catch(RangeError e) { 121 throw new Exception(text("Could not parse the output of dub describe:\n", res.output, "\n", e.toString)); 122 } 123 } 124 125 bool isDubProject() { 126 import std.file; 127 return "dub.sdl".exists || "dub.json".exists || "package.json".exists; 128 } 129 130 131 // set import paths from dub information 132 void dubify(ref Options options) { 133 134 import std.path: buildPath; 135 import std.algorithm: map, reduce; 136 import std.array: array; 137 138 if(!isDubProject) return; 139 140 auto dubInfo = getDubInfo(options.verbose); 141 options.includes = dubInfo.packages. 142 map!(a => a.importPaths.map!(b => buildPath(a.path, b)).array). 143 reduce!((a, b) => a ~ b).array; 144 options.files = dubInfo.packages[0].files; 145 }