1 /** 2 3 This module implements the binary that is used to generate the build 4 in the case of the make, ninja and tup backends, i.e. it translates 5 D code into the respective output. 6 7 For the binary target this module implements the binary that actually 8 performs the build 9 10 */ 11 12 module reggae.buildgen; 13 14 import reggae.build; 15 import reggae.options; 16 import reggae.types; 17 import reggae.backend; 18 import reggae.reflect; 19 import reggae.path: buildPath; 20 21 import std.stdio; 22 import std.file: timeLastModified; 23 24 /** 25 Creates a build generator out of a module and a list of top-level targets. 26 This will define a function with the signature $(D Build buildFunc()) in 27 the calling module and a $(D main) entry point function for a command-line 28 executable. 29 */ 30 mixin template buildGen(string buildModule, targets...) { 31 mixin buildImpl!targets; 32 mixin BuildGenMain!buildModule; 33 } 34 35 mixin template BuildGenMain(string buildModule = "reggaefile") { 36 import std.stdio; 37 38 // args is empty except for the binary backend, 39 // in which case it's used for runtime options 40 int main(string[] args) { 41 try { 42 import reggae.config: options; 43 doBuildFor!(buildModule)(options, args); //the user's build description 44 } catch(Exception ex) { 45 stderr.writeln(ex.msg); 46 return 1; 47 } 48 49 return 0; 50 } 51 } 52 53 void doBuildFor(alias module_ = "reggaefile")(in Options options, string[] args = []) { 54 auto build = getBuildObject!module_(options); 55 if(!options.noCompilationDB) writeCompilationDB(build, options); 56 doBuild(build, options, args); 57 } 58 59 // calls the build function or loads it from the cache and returns 60 // the Build object 61 Build getBuildObject(alias module_)(in Options options) { 62 import std.file; 63 64 immutable cacheFileName = buildPath(".reggae", "cache"); 65 if(!options.cacheBuildInfo || 66 !cacheFileName.exists || 67 thisExePath.timeLastModified > cacheFileName.timeLastModified) { 68 const buildFunc = getBuild!(module_); //get the function to call by CT reflection 69 auto build = buildFunc(); //actually call the function to get the build description 70 71 if(options.cacheBuildInfo) { 72 auto file = File(cacheFileName, "w"); 73 file.rawWrite(build.toBytes(options)); 74 } 75 76 return build; 77 } else { 78 auto file = File(cacheFileName); 79 auto buffer = new ubyte[cast(uint)file.size]; 80 return Build.fromBytes(file.rawRead(buffer)); 81 } 82 } 83 84 void doBuild(Build build, in Options options, string[] args = []) { 85 options.export_ ? exportBuild(build, options) : doOneBuild(build, options, args); 86 } 87 88 89 private void doOneBuild(Build build, in Options options, string[] args = []) { 90 final switch(options.backend) with(Backend) { 91 92 version(minimal) { 93 import std.conv; 94 95 case make: 96 case ninja: 97 case tup: 98 throw new Exception(text("Support for ", options.backend, " not compiled in")); 99 } else { 100 101 case make: 102 writeBuild!Makefile(build, options); 103 break; 104 105 case ninja: 106 writeBuild!Ninja(build, options); 107 break; 108 109 case tup: 110 writeBuild!Tup(build, options); 111 break; 112 } 113 114 case binary: 115 Binary(build, options).run(args); 116 break; 117 118 case none: 119 throw new Exception("A backend must be specified with -b/--backend"); 120 } 121 } 122 123 private void exportBuild(Build build, in Options options) { 124 import std.exception; 125 import std.meta; 126 127 enforce(options.backend == Backend.none, "Cannot specify a backend and export at the same time"); 128 129 version(minimal) 130 throw new Exception("export not supported in minimal version"); 131 else 132 foreach(backend; AliasSeq!(Makefile, Ninja, Tup)) 133 writeBuild!backend(build, options); 134 } 135 136 private void writeBuild(T)(Build build, in Options options) { 137 version(minimal) 138 throw new Exception(T.stringof ~ " backend support not compiled in"); 139 else 140 T(build, options).writeBuild; 141 } 142 143 144 void writeCompilationDB(Build build, in Options options) { 145 import std.file; 146 import std.conv; 147 import std.algorithm; 148 import std..string; 149 import std.path: dirSeparator; 150 151 auto file = File(buildPath(options.workingDir, "compile_commands.json"), "w"); 152 file.writeln("["); 153 154 enum objPathPrefix = "objs" ~ dirSeparator; 155 156 immutable cwd = getcwd; 157 string entry(Target target) { 158 auto command = target 159 .shellCommand(options) 160 .replace(`"`, `\"`) 161 .split(" ") 162 .map!(a => a.startsWith(objPathPrefix) ? buildPath(options.workingDir, a) : a) 163 .join(" ") 164 ; 165 return 166 " {\n" ~ 167 text(` "directory": "`, cwd, `"`) ~ ",\n" ~ 168 text(` "command": "`, command, `"`) ~ ",\n" ~ 169 text(` "file": "`, target.dependenciesInProjectPath(options.projectPath).join(" "), `"`) ~ "\n" ~ 170 " }"; 171 } 172 173 file.write(build.range.map!(a => entry(a)).join(",\n")); 174 file.writeln; 175 file.writeln("]"); 176 }