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 }