1 module reggae.json_build; 2 3 4 import reggae.build; 5 import reggae.ctaa; 6 import reggae.rules.common; 7 8 import std.json; 9 import std.algorithm; 10 import std.array; 11 import std.conv; 12 13 enum JsonTargetType { 14 fixed, 15 dynamic, 16 } 17 18 enum JsonCommandType { 19 shell, 20 link, 21 } 22 23 24 enum JsonDependencyType { 25 fixed, 26 dynamic, 27 } 28 29 30 enum JsonDepsFuncName { 31 objectFiles, 32 staticLibrary, 33 } 34 35 36 Build jsonToBuild(in string projectPath, in string jsonString) { 37 auto json = parseJSON(jsonString); 38 Target[] targets; 39 foreach(target; json.array) { 40 targets ~= jsonToTarget(projectPath, target); 41 } 42 return Build(targets); 43 } 44 45 46 47 private Target jsonToTarget(in string projectPath, in JSONValue json) { 48 if(json.object["type"].str.to!JsonTargetType == JsonTargetType.dynamic) 49 return callTargetFunc(projectPath, json); 50 51 const dependencies = getDeps(projectPath, json.object["dependencies"]); 52 const implicits = getDeps(projectPath, json.object["implicits"]); 53 54 if(isLeaf(json)) 55 return Target(json.object["outputs"].array.map!(a => a.str).array, 56 "", 57 []); 58 59 return Target(json.object["outputs"].array.map!(a => a.str).array, 60 jsonToCommand(json.object["command"]), 61 dependencies, 62 implicits); 63 } 64 65 private bool isLeaf(in JSONValue json) pure { 66 return json.object["dependencies"].object["type"].str.to!JsonDependencyType == JsonDependencyType.fixed && 67 json.object["dependencies"].object["targets"].array.empty && 68 json.object["implicits"].object["type"].str.to!JsonDependencyType == JsonDependencyType.fixed && 69 json.object["implicits"].object["targets"].array.empty; 70 } 71 72 73 private Command jsonToCommand(in JSONValue json) pure { 74 immutable type = json.object["type"].str.to!JsonCommandType; 75 final switch(type) with(JsonCommandType) { 76 case shell: 77 return Command(json.object["cmd"].str); 78 case link: 79 return Command(CommandType.link, 80 assocList([assocEntry("flags", json.object["flags"].str.splitter.array)])); 81 } 82 } 83 84 85 private Target[] getDeps(in string projectPath, in JSONValue json) { 86 immutable type = json.object["type"].str.to!JsonDependencyType; 87 return type == JsonDependencyType.fixed 88 ? json.object["targets"].array.map!(a => jsonToTarget(projectPath, a)).array 89 : callDepsFunc(projectPath, json); 90 91 } 92 93 private Target[] callDepsFunc(in string projectPath, in JSONValue json) { 94 immutable func = json.object["func"].str.to!JsonDepsFuncName; 95 final switch(func) { 96 case JsonDepsFuncName.objectFiles: 97 return objectFiles(projectPath, 98 strings(json, "src_dirs"), 99 strings(json, "exclude_dirs"), 100 strings(json, "src_files"), 101 strings(json, "exclude_files"), 102 stringVal(json, "flags"), 103 strings(json, "includes"), 104 strings(json, "string_imports")); 105 case JsonDepsFuncName.staticLibrary: 106 return staticLibrary(projectPath, 107 stringVal(json, "name"), 108 strings(json, "src_dirs"), 109 strings(json, "exclude_dirs"), 110 strings(json, "src_files"), 111 strings(json, "exclude_files"), 112 stringVal(json, "flags"), 113 strings(json, "includes"), 114 strings(json, "string_imports")); 115 116 } 117 } 118 119 private const(string)[] strings(in JSONValue json, in string key) { 120 return json.object[key].array.map!(a => a.str).array; 121 } 122 123 private const(string) stringVal(in JSONValue json, in string key) { 124 return json.object[key].str; 125 } 126 127 128 private Target callTargetFunc(in string projectPath, in JSONValue json) { 129 import std.exception; 130 import reggae.rules.d; 131 import reggae.types; 132 133 enforce(json.object["func"].str == "scriptlike", 134 "Only scriptlike is supported for Targets"); 135 136 return scriptlike(projectPath, 137 App(SourceFileName(stringVal(json, "src_name")), 138 BinaryFileName(stringVal(json, "exe_name"))), 139 Flags(stringVal(json, "flags")), 140 const ImportPaths(strings(json, "includes")), 141 const StringImportPaths(strings(json, "string_imports")), 142 getDeps(projectPath, json["link_with"])); 143 }