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 }