1 /** 2 The main entry point for the reggae tool. Its tasks are: 3 $(UL 4 $(LI Verify that a $(D reggafile.d) exists in the selected directory) 5 $(LI Generate a $(D reggaefile.d) for dub projects) 6 $(LI Write out the reggae library files and $(D config.d)) 7 $(LI Compile the build description with the reggae library files to produce $(D buildgen)) 8 $(LI Produce $(D dcompile), a binary to call the D compiler to obtain dependencies during compilation) 9 $(LI Call the produced $(D buildgen) binary) 10 ) 11 */ 12 13 14 module reggae.reggae; 15 16 import std.stdio; 17 import std.process: execute; 18 import std.array: array, join, empty; 19 import std.path: absolutePath, buildPath, relativePath; 20 import std.typetuple; 21 import std.file: exists; 22 import std.conv: text; 23 import std.exception: enforce; 24 import std.conv: to; 25 import std.algorithm; 26 27 import reggae.options; 28 import reggae.ctaa; 29 30 31 version(minimal) { 32 //empty stubs for minimal version of reggae 33 void maybeCreateReggaefile(T...)(T) {} 34 void writeDubConfig(T...)(T) {} 35 } else { 36 import reggae.dub.interop; 37 } 38 39 mixin template reggaeGen(targets...) { 40 mixin buildImpl!targets; 41 mixin ReggaeMain; 42 } 43 44 mixin template ReggaeMain() { 45 import reggae.options: getOptions; 46 import std.stdio: stderr; 47 48 int main(string[] args) { 49 try { 50 run(getOptions(args)); 51 } catch(Exception ex) { 52 stderr.writeln(ex.msg); 53 return 1; 54 } 55 56 return 0; 57 } 58 } 59 60 void run(in Options options) { 61 if(options.help) return; 62 enforce(options.projectPath != "", "A project path must be specified"); 63 64 maybeCreateReggaefile(options); 65 createBuild(options); 66 } 67 68 69 enum coreFiles = [ 70 "buildgen_main.d", "buildgen.d", 71 "build.d", 72 "backend/binary.d", 73 "package.d", "range.d", "reflect.d", 74 "dependencies.d", "types.d", "dcompile.d", 75 "ctaa.d", "sorting.d", 76 "rules/package.d", 77 "rules/defaults.d", "rules/common.d", 78 "rules/d.d", 79 "core/package.d", "core/rules/package.d", 80 ]; 81 enum otherFiles = [ 82 "backend/ninja.d", "backend/make.d", 83 "dub/info.d", "rules/dub.d", 84 "rules/cpp.d", "rules/c.d", 85 ]; 86 87 //all files that need to be written out and compiled 88 private string[] fileNames() @safe pure nothrow { 89 version(minimal) return coreFiles; 90 else return coreFiles ~ otherFiles; 91 } 92 93 94 private void createBuild(in Options options) { 95 96 immutable reggaefilePath = getReggaefilePath(options); 97 enforce(reggaefilePath.exists, text("Could not find ", reggaefilePath)); 98 99 //write out the library source files to be compiled with the user's 100 //build description 101 writeSrcFiles(options); 102 103 //compile the binaries (the build generator and dcompile) 104 immutable buildGenName = compileBinaries(options); 105 106 //actually run the build generator 107 writeln("[Reggae] Running the created binary to generate the build"); 108 immutable retRunBuildgen = execute([buildPath(".", buildGenName)]); 109 enforce(retRunBuildgen.status == 0, 110 text("Couldn't execute the produced ", buildGenName, " binary:\n", retRunBuildgen.output)); 111 112 writeln(retRunBuildgen.output); 113 } 114 115 private immutable hiddenDir = ".reggae"; 116 117 118 private auto compileBinaries(in Options options) { 119 immutable buildGenName = getBuildGenName(options); 120 const compileBuildGenCmd = getCompileBuildGenCmd(options); 121 122 immutable dcompileName = buildPath(hiddenDir, "dcompile"); 123 immutable dcompileCmd = ["dmd", 124 "-I.reggae/src", 125 "-of" ~ dcompileName, 126 reggaeSrcFileName("dcompile.d"), 127 reggaeSrcFileName("dependencies.d")]; 128 129 130 static struct Binary { string name; const(string)[] cmd; } 131 132 const binaries = [Binary(buildGenName, compileBuildGenCmd), Binary(dcompileName, dcompileCmd)]; 133 import std.parallelism; 134 135 foreach(bin; binaries.parallel) { 136 writeln("[Reggae] Compiling metabuild binary ", bin.name); 137 immutable res = execute(bin.cmd); 138 enforce(res.status == 0, text("Couldn't execute ", bin.cmd.join(" "), ":\n", res.output, 139 "\n", "bin.name: ", bin.name, ", bin.cmd: ", bin.cmd.join(" "))); 140 } 141 142 return buildGenName; 143 } 144 145 string[] getCompileBuildGenCmd(in Options options) @safe nothrow { 146 const reggaeSrcs = ("config.d" ~ fileNames). 147 filter!(a => a != "dcompile.d"). 148 map!(a => a.reggaeSrcFileName).array; 149 150 immutable buildBinFlags = options.backend == Backend.binary 151 ? ["-O", "-release", "-inline"] 152 : []; 153 immutable commonBefore = ["dmd", 154 "-I" ~ options.projectPath, 155 "-of" ~ getBuildGenName(options)]; 156 const commonAfter = buildBinFlags ~ reggaeSrcs ~ getReggaefilePath(options); 157 version(minimal) return commonBefore ~ "-version=minimal" ~ commonAfter; 158 else return commonBefore ~ commonAfter; 159 } 160 161 string getBuildGenName(in Options options) @safe pure nothrow { 162 return options.backend == Backend.binary ? "build" : buildPath(hiddenDir, "buildgen"); 163 } 164 165 immutable reggaeSrcDirName = buildPath(".reggae", "src", "reggae"); 166 167 private string filesTupleString() @safe pure nothrow { 168 return "TypeTuple!(" ~ fileNames.map!(a => `"` ~ a ~ `"`).join(",") ~ ")"; 169 } 170 171 template FileNames() { 172 mixin("alias FileNames = " ~ filesTupleString ~ ";"); 173 } 174 175 176 private void writeSrcFiles(in Options options) { 177 import std.file: mkdirRecurse; 178 if(!reggaeSrcDirName.exists) { 179 mkdirRecurse(reggaeSrcDirName); 180 mkdirRecurse(buildPath(reggaeSrcDirName, "dub")); 181 mkdirRecurse(buildPath(reggaeSrcDirName, "rules")); 182 mkdirRecurse(buildPath(reggaeSrcDirName, "backend")); 183 mkdirRecurse(buildPath(reggaeSrcDirName, "core", "rules")); 184 } 185 186 187 //this foreach has to happen at compile time due 188 //to the string import below. 189 foreach(fileName; FileNames!()) { 190 auto file = File(reggaeSrcFileName(fileName), "w"); 191 file.write(import(fileName)); 192 } 193 194 writeConfig(options); 195 } 196 197 198 private void writeConfig(in Options options) { 199 auto file = File(reggaeSrcFileName("config.d"), "w"); 200 201 file.writeln(q{ 202 module reggae.config; 203 import reggae.ctaa; 204 import reggae.types: Backend; 205 206 }); 207 208 file.writeln("enum projectPath = `", options.projectPath, "`;"); 209 file.writeln("enum backend = Backend.", options.backend, ";"); 210 file.writeln("enum dflags = `", options.dflags, "`;"); 211 file.writeln("enum reggaePath = `", options.reggaePath, "`;"); 212 file.writeln("enum buildFilePath = `", options.getReggaefilePath.absolutePath, "`;"); 213 file.writeln("enum cCompiler = `", options.cCompiler, "`;"); 214 file.writeln("enum cppCompiler = `", options.cppCompiler, "`;"); 215 file.writeln("enum dCompiler = `", options.dCompiler, "`;"); 216 file.writeln("enum perModule = ", options.perModule, ";"); 217 218 file.writeln("enum userVars = AssocList!(string, string)(["); 219 foreach(key, value; options.userVars) { 220 file.writeln("assocEntry(`", key, "`, `", value, "`), "); 221 } 222 file.writeln("]);"); 223 224 writeDubConfig(options, file); 225 } 226 227 228 229 private string reggaeSrcFileName(in string fileName) @safe pure nothrow { 230 return buildPath(reggaeSrcDirName, fileName); 231 } 232 233 private string getReggaefilePath(in Options options) @safe nothrow { 234 immutable regular = projectBuildFile(options); 235 if(regular.exists) return regular; 236 immutable path = options.isDubProject ? "" : options.projectPath; 237 return buildPath(path, "reggaefile.d"); 238 }