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