1 module reggae.makefile; 2 3 import reggae.build; 4 import reggae.range; 5 import reggae.rules; 6 import std.conv; 7 import std.array; 8 import std.path; 9 import std.algorithm; 10 11 12 struct Makefile { 13 Build build; 14 string projectPath; 15 16 this(Build build) @safe pure { 17 this.build = build; 18 this.projectPath = ""; 19 } 20 21 this(Build build, string projectPath) @safe pure { 22 this.build = build; 23 this.projectPath = projectPath.absolutePath; 24 } 25 26 string fileName() @safe pure nothrow const { 27 return "Makefile"; 28 } 29 30 string simpleOutput() @safe const { 31 32 const outputs = build.targets.map!(a => a.outputs[0]).join(" "); 33 auto ret = text("all: ", outputs, "\n"); 34 35 foreach(topTarget; build.targets) { 36 foreach(t; DepthFirst(topTarget)) { 37 38 mkDir(t); 39 40 ret ~= text(t.outputs.join(" "), ": "); 41 ret ~= t.dependencyFiles(projectPath); 42 immutable implicitFiles = t.implicitFiles(projectPath); 43 if(!implicitFiles.empty) ret ~= " " ~ t.implicitFiles(projectPath); 44 ret ~= " Makefile\n"; 45 ret ~= "\t"; 46 ret ~= command(t); 47 ret ~= "\n"; 48 } 49 } 50 51 return ret; 52 } 53 54 string output() @safe const { 55 import reggae.config; 56 auto ret = simpleOutput; 57 ret ~= "Makefile: " ~ buildFilePath ~ " " ~ reggaePath ~ "\n"; 58 immutable _dflags = dflags == "" ? "" : " --dflags='" ~ dflags ~ "'"; 59 ret ~= "\t" ~ reggaePath ~ " -b make" ~ _dflags ~ " " ~ projectPath ~ "\n"; 60 return ret; 61 } 62 63 private void mkDir(in Target target) @trusted const { 64 foreach(output; target.outputs) { 65 import std.file; 66 if(!output.dirName.exists) mkdirRecurse(output.dirName); 67 } 68 } 69 70 string command(in Target target) @safe const { 71 immutable rawCmdLine = target.inOutCommand(projectPath); 72 if(rawCmdLine.isDefaultCommand) { 73 return command(target, rawCmdLine); 74 } else { 75 return target.command(projectPath); 76 } 77 } 78 79 string command(in Target target, in string rawCmdLine) @safe const { 80 import reggae.config; 81 82 immutable rule = rawCmdLine.getDefaultRule; 83 immutable flags = rawCmdLine.getDefaultRuleParams("flags", []).join(" "); 84 immutable includes = rawCmdLine.getDefaultRuleParams("includes", []).join(" "); 85 immutable depfile = target.outputs[0] ~ ".d"; 86 87 string ccCommand(in string compiler) { 88 immutable command = [compiler, flags, includes, "-MMD", "-MT", target.outputs[0], 89 "-MF", depfile, "-o", target.outputs[0], "-c", 90 target.dependencyFiles(projectPath)].join(" "); 91 return command ~ makeAutoDeps(depfile); 92 } 93 94 if(rule == "_dcompile") { 95 immutable stringImports = rawCmdLine.getDefaultRuleParams("stringImports", []).join(" "); 96 immutable command = [".reggae/dcompile", 97 "--objFile=" ~ target.outputs[0], 98 "--depFile=" ~ depfile, dCompiler, 99 flags, includes, stringImports, 100 target.dependencyFiles(projectPath), 101 ].join(" "); 102 103 return command ~ makeAutoDeps(depfile); 104 105 } else if(rule == "_cppcompile") { 106 return ccCommand(cppCompiler); 107 } else if(rule == "_ccompile") { 108 return ccCommand(cCompiler); 109 } else if(rule == "_dlink") { 110 return [dCompiler, "-of" ~ target.outputs[0], target.dependencyFiles(projectPath)].join(" "); 111 } else { 112 throw new Exception("Unknown Makefile default rule " ~ rule); 113 } 114 } 115 116 private: 117 118 void addRerunBuild(ref string ret) @safe pure nothrow const { 119 import reggae.config; 120 ret ~= "Makefile: " ~ buildFilePath ~ " " ~ reggaePath ~ "\n"; 121 immutable _dflags = dflags == "" ? "" : " --dflags='" ~ dflags ~ "'"; 122 ret ~= "\t" ~ reggaePath ~ " -b make" ~ _dflags ~ " " ~ projectPath ~ "\n"; 123 } 124 } 125 126 127 //For explanation of the crazy Makefile commands, see: 128 //http://stackoverflow.com/questions/8025766/makefile-auto-dependency-generation 129 //http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/ 130 private string makeAutoDeps(in string depfile) @safe pure nothrow { 131 immutable pFile = depfile ~ ".P"; 132 return "\n\t@cp " ~ depfile ~ " " ~ pFile ~ "; \\\n" ~ 133 " sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \\\n" ~ 134 " -e '/^$$/ d' -e 's/$$/ :/' < " ~ depfile ~ " >> " ~ pFile ~"; \\\n" ~ 135 " rm -f " ~ depfile ~ "\n\n" ~ 136 "-include " ~ pFile ~ "\n\n"; 137 }