1 module reggae.dcompile;
2 
3 import std.stdio;
4 import std.exception;
5 import std.process;
6 import std.conv;
7 import std.algorithm;
8 import std.getopt;
9 import std.array;
10 
11 
12 version(ReggaeTest) {}
13 else {
14     int main(string[] args) {
15         try {
16             dcompile(args);
17             return 0;
18         } catch(Exception ex) {
19             stderr.writeln(ex.msg);
20             return 1;
21         }
22     }
23 }
24 
25 /**
26 Only exists in order to get dependencies for each compilation step.
27  */
28 private void dcompile(string[] args) {
29 
30     string depFile, objFile;
31     auto helpInfo = getopt(
32         args,
33         std.getopt.config.passThrough,
34         "depFile", "The dependency file to write", &depFile,
35         "objFile", "The object file to output", &objFile,
36     );
37 
38     enforce(args.length >= 2, "Usage: dcompile --objFile <objFile> --depFile <depFile> <compiler> <compiler args>");
39     enforce(!depFile.empty && !objFile.empty, "The --depFile and --objFile 'options' are mandatory");
40 
41     const compArgs = compilerArgs(args, objFile);
42     const fewerArgs = compArgs[0..$-1]; //non-verbose
43     const compRes = execute(compArgs);
44     enforce(compRes.status == 0,
45             text("Could not compile with args:\n", fewerArgs.join(" "), "\n",
46                  execute(fewerArgs).output));
47 
48     auto file = File(depFile, "w");
49     file.write(dependenciesToFile(objFile, dMainDependencies(compRes.output)).join("\n"));
50     file.writeln;
51 }
52 
53 
54 private string[] compilerArgs(string[] args, in string objFile) @safe pure {
55     auto compArgs = args[1 .. $] ~ ["-of" ~ objFile, "-c", "-v"];
56 
57     switch(args[1]) {
58         default:
59             return compArgs;
60         case "gdc":
61             return mapToGdcOptions(compArgs);
62         case "ldc":
63         case "ldc2":
64             return mapToLdcOptions(compArgs);
65     }
66 }
67 
68 //takes a dmd command line and maps arguments to gdc ones
69 private string[] mapToGdcOptions(in string[] compArgs) @safe pure {
70     string[string] options = ["-v": "-fd-verbose", "-O": "-O2", "-debug": "-fdebug", "-of": "-o"];
71 
72     string doMap(string a) {
73         foreach(k, v; options) {
74             if(a.startsWith(k)) a = a.replace(k, v);
75         }
76         return a;
77     }
78 
79     return compArgs.map!doMap.array;
80 }
81 
82 
83 //takes a dmd command line and maps arguments to gdc ones
84 private string[] mapToLdcOptions(in string[] compArgs) @safe pure {
85     string[string] options = ["-O": "-O2", "-debug": "-d-debug"];
86 
87     string doMap(string a) {
88         foreach(k, v; options) {
89             if(a.startsWith(k)) a = a.replace(k, v);
90         }
91         return a;
92     }
93 
94     return compArgs.map!doMap.array;
95 }
96 
97 
98 /**
99  * Given the output of compiling a file, return
100  * the list of D files to compile to link the executable
101  * Includes all dependencies, not just source files to
102  * compile.
103  */
104 string[] dMainDependencies(in string output) @safe {
105     import reggae.dependencies: dMainDepSrcs;
106     import std.regex: regex, matchFirst;
107     import std.string: splitLines;
108 
109     string[] dependencies = dMainDepSrcs(output);
110     auto fileReg = regex(`^file +([^\t]+)\t+\((.+)\)$`);
111 
112     foreach(line; output.splitLines) {
113         auto fileMatch = line.matchFirst(fileReg);
114         if(fileMatch) dependencies ~= fileMatch.captures[2];
115     }
116 
117     return dependencies;
118 }
119 
120 
121 string[] dependenciesToFile(in string objFile, in string[] deps) @safe pure nothrow {
122     import std.array: join;
123     return [
124         objFile ~ ": \\",
125         deps.join(" "),
126     ];
127 }