1 module app; 2 3 import std.algorithm; 4 import std.array; 5 import std.getopt; 6 import std.json; 7 import std.process; 8 import std.stdio; 9 10 enum dbdVersion = "1.0.1"; 11 12 struct Pack 13 { 14 string name; 15 string ver; 16 string config; 17 string[] overrideConfig; 18 19 @property string dubId() 20 { 21 assert(name); 22 if (ver) 23 return name ~ "@" ~ ver; 24 else 25 return name; 26 } 27 } 28 29 struct DubConfig 30 { 31 string exe; 32 string compiler; 33 string arch; 34 string build; 35 36 string[] makeDescribeCmd(Pack pack) 37 { 38 return makeComplexCmd("describe", pack); 39 } 40 41 string[] makeBuildCmd(Pack pack) 42 { 43 return makeComplexCmd("build", pack); 44 } 45 46 string[] makeFetchCmd(Pack pack) 47 { 48 return [exe, "fetch", pack.dubId]; 49 } 50 51 string[] makeComplexCmd(string cmdName, Pack pack) 52 { 53 auto cmd = [ 54 exe, cmdName, pack.dubId 55 ]; 56 if (pack.config) 57 cmd ~= ["--config", pack.config]; 58 59 foreach (oc; pack.overrideConfig) 60 cmd ~= ["--override-config", oc]; 61 62 if (compiler) 63 cmd ~= ["--compiler", compiler]; 64 65 if (arch) 66 cmd ~= ["--arch", arch]; 67 68 if (build) 69 cmd ~= ["--build", build]; 70 71 return cmd; 72 } 73 74 } 75 76 int usage(string dbdExe, Option[] opts, int code = 0, string errorMsg = null) 77 { 78 auto file = code == 0 ? stdout : stderr; 79 80 if (errorMsg) 81 { 82 file.writefln("Error: %s", errorMsg); 83 file.writeln(""); 84 } 85 86 if (code == 0) 87 { 88 file.writefln("dub-build-deep v%s - An utility to build DUB sub-dependencies", dbdVersion); 89 file.writefln(""); 90 } 91 92 file.writefln("Usage:"); 93 file.writefln(" %s [options] (package name)[@version]", dbdExe); 94 file.writeln(); 95 file.writeln("Most options are the same than expected by DUB describe."); 96 97 defaultGetoptFormatter(file.lockingTextWriter(), null, opts); 98 99 return code; 100 } 101 102 int main(string[] args) 103 { 104 Pack pack; 105 106 DubConfig dub; 107 dub.exe = "dub"; 108 109 auto helpInfo = getopt(args, 110 "config", "Specify a DUB configuration", &pack.config, 111 "override-config", "Specify a DUB configuration for a sub-dependency", &pack.overrideConfig, 112 "dub", "Specify a DUB executable", &dub.exe, 113 "compiler", "D compiler to be used", &dub.compiler, 114 "arch", "Architecture to target", &dub.arch, 115 "build", "The build type (debug, release, ...)", &dub.build, 116 ); 117 118 if (helpInfo.helpWanted) 119 { 120 return usage(args[0], helpInfo.options); 121 } 122 123 if (args.length < 2) 124 { 125 return usage(args[0], helpInfo.options, 1, "(package name] was not provided"); 126 } 127 128 if (args.length > 2) 129 { 130 return usage(args[0], helpInfo.options, 1, "Too many arguments (only one package accepted)"); 131 } 132 133 const spec = args[1].split("@"); 134 if (spec.length > 2) 135 { 136 stderr.writeln("Invalid package specifier: ", args[1]); 137 return 1; 138 } 139 pack.name = spec[0]; 140 if (spec.length == 2) 141 { 142 pack.ver = spec[1]; 143 } 144 145 const describeCmd = dub.makeDescribeCmd(pack); 146 147 writeln("running ", escapeShellCommand(describeCmd)); 148 auto describe = execute(describeCmd); 149 150 if (describe.status != 0 && describe.output.canFind("locally")) 151 { 152 writefln("Warning: %s does not appear to be present locally. Will try to fetch...", pack.dubId); 153 const fetchCmd = dub.makeFetchCmd(pack); 154 writefln("running %s", escapeShellCommand(fetchCmd)); 155 auto fetch = execute(fetchCmd); 156 if (fetch.status != 0) 157 { 158 stderr.writefln("Error: `dub fetch` returned %s:", fetch.status); 159 stderr.writeln(fetch.output); 160 return fetch.status; 161 } 162 163 writeln("running ", escapeShellCommand(describeCmd)); 164 describe = execute(describeCmd); 165 } 166 167 168 if (describe.status != 0) 169 { 170 stderr.writeln("Error: describe command returned %s:", describe.status); 171 stderr.writeln(describe.output); 172 return describe.status; 173 } 174 175 auto json = parseJSON(describe.output); 176 177 foreach (jp; json["packages"].array) 178 { 179 if (!jp["active"].boolean) 180 continue; 181 182 const targetType = jp["targetType"].str; 183 if (targetType == "none" || targetType == "sourceLibrary") 184 continue; 185 186 Pack p; 187 p.name = jp["name"].str; 188 p.ver = jp["version"].str; 189 p.config = jp["configuration"].str; 190 191 const res = buildDubPackage(p, dub); 192 193 if (res) 194 return res; 195 } 196 197 return 0; 198 } 199 200 int buildDubPackage(Pack pack, DubConfig dub) 201 { 202 const buildCmd = dub.makeBuildCmd(pack); 203 writeln("running ", escapeShellCommand(buildCmd)); 204 auto res = execute(buildCmd); 205 if (res.status != 0) 206 { 207 stderr.writeln(res.output); 208 } 209 return res.status; 210 }