1 module reggae.backend.make;
2 
3 import reggae.build;
4 import reggae.range;
5 import reggae.rules;
6 import reggae.config;
7 
8 import std.conv;
9 import std.array;
10 import std.path;
11 import std.algorithm;
12 
13 
14 struct Makefile {
15     Build build;
16     string projectPath;
17 
18     this(Build build) @safe pure {
19         this.build = build;
20         this.projectPath = "";
21     }
22 
23     this(Build build, string projectPath) @safe pure {
24         this.build = build;
25         this.projectPath = projectPath;
26     }
27 
28     string fileName() @safe pure nothrow const {
29         return "Makefile";
30     }
31 
32     //only the main targets
33     string simpleOutput() @safe const {
34 
35         const outputs = build.targets.map!(a => a.outputs[0]).join(" ");
36         auto ret = text("all: ", outputs, "\n");
37 
38         foreach(topTarget; build.targets) {
39             foreach(t; DepthFirst(topTarget)) {
40 
41                 mkDir(t);
42 
43                 ret ~= text(t.outputs.join(" "), ": ");
44                 ret ~= t.dependencyFilesString(projectPath);
45                 immutable implicitFiles = t.implicitFilesString(projectPath);
46                 if(!implicitFiles.empty) ret ~= " " ~ t.implicitFilesString(projectPath);
47                 ret ~= " Makefile\n";
48 
49                 ret ~= "\t" ~ command(t) ~ "\n";
50             }
51         }
52 
53         return ret;
54     }
55 
56     //includes rerunning reggae
57     string output() @safe const {
58         auto ret = simpleOutput;
59         ret ~= "Makefile: " ~ buildFilePath ~ " " ~ reggaePath ~ "\n";
60         immutable _dflags = dflags == "" ? "" : " --dflags='" ~ dflags ~ "'";
61         ret ~= "\t" ~ reggaePath ~ " -b make" ~ _dflags ~ " " ~ projectPath ~ "\n";
62 
63         return ret;
64     }
65 
66     private void mkDir(in Target target) @trusted const {
67         foreach(output; target.outputs) {
68             import std.file;
69             if(!output.dirName.exists) mkdirRecurse(output.dirName);
70         }
71     }
72 
73     //the only reason this is needed is to add auto dependency
74     //tracking
75     string command(in Target target) @safe const {
76         immutable cmd = target.shellCommand(projectPath);
77         immutable depfile = target.outputs[0] ~ ".dep";
78 
79         immutable rawCmdLine = target.rawCmdString(projectPath);
80 
81         if(rawCmdLine.isDefaultCommand) {
82             immutable rule = rawCmdLine.getDefaultRule;
83             return rule.canFind("compile") ? cmd ~ makeAutoDeps(depfile) : cmd;
84         } else {
85             return cmd;
86         }
87     }
88 }
89 
90 
91 //For explanation of the crazy Makefile commands, see:
92 //http://stackoverflow.com/questions/8025766/makefile-auto-dependency-generation
93 //http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/
94 private string makeAutoDeps(in string depfile) @safe pure nothrow {
95     immutable pFile = depfile ~ ".P";
96     return "\n\t@cp " ~ depfile ~ " " ~ pFile ~ "; \\\n" ~
97         "    sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \\\n" ~
98         "        -e '/^$$/ d' -e 's/$$/ :/' < " ~ depfile ~ " >> " ~ pFile ~"; \\\n" ~
99         "    rm -f " ~ depfile ~ "\n\n" ~
100         "-include " ~ pFile ~ "\n\n";
101 }