1 /* 2 ******************************************************************************************* 3 * Dgame (a D game framework) - Copyright (c) Randy Schütt 4 * 5 * This software is provided 'as-is', without any express or implied warranty. 6 * In no event will the authors be held liable for any damages arising from 7 * the use of this software. 8 * 9 * Permission is granted to anyone to use this software for any purpose, 10 * including commercial applications, and to alter it and redistribute it 11 * freely, subject to the following restrictions: 12 * 13 * 1. The origin of this software must not be misrepresented; you must not claim 14 * that you wrote the original software. If you use this software in a product, 15 * an acknowledgment in the product documentation would be appreciated but is 16 * not required. 17 * 18 * 2. Altered source versions must be plainly marked as such, and must not be 19 * misrepresented as being the original software. 20 * 21 * 3. This notice may not be removed or altered from any source distribution. 22 ******************************************************************************************* 23 */ 24 module build; 25 26 import std.stdio; 27 import std.path : dirName, buildNormalizedPath, absolutePath; 28 import std.process : system, ErrnoException; 29 import std.file : mkdir, exists, read, dirEntries, SpanMode; 30 import std.array : endsWith; 31 import std..string : format, toUpper, chop, splitLines, strip; 32 import std.exception : enforce; 33 34 version(DigitalMars) { 35 enum DMD = true; 36 enum GDC = false; 37 enum LDC = false; 38 } else version(GNU) { 39 enum DMD = false; 40 enum GDC = true; 41 enum LDC = false; 42 } else version(LDC) { 43 enum DMD = false; 44 enum GDC = false; 45 enum LDC = true; 46 } 47 48 version(Windows) { 49 enum Windows = true; 50 enum Posix = false; 51 } else version(Posix) { 52 enum Windows = false; 53 enum Posix = true; 54 } else 55 static assert(false, "Unknown operating system."); 56 57 static immutable string Project = "Dgame"; 58 static immutable string SrcDgame = "../"; 59 static immutable string LibDir = SrcDgame ~ "lib"; 60 61 // Compiler configuration 62 version(DigitalMars) { 63 pragma(msg, "Using the Digital Mars DMD compiler."); 64 65 debug { 66 static immutable string Release = "-debug"; 67 } else { 68 static immutable string Release = "-release -inline"; 69 } 70 71 static immutable string CompilerOptions = "-lib -O " ~ Release ~ " -wi"; 72 73 string buildCompileString(string files, string libName) { 74 return format("dmd %s -of%s/%s %s -I%s -I../../", CompilerOptions, outdir, libName, files, derelictImportDir); 75 } 76 } else version(GNU) { 77 pragma(msg, "Using the GNU GDC compiler."); 78 79 static immutable string CompilerOptions = "-c -s -O3 -Wall"; 80 81 string buildCompileString(string files, string libName) { 82 return format("gdc %s -o %s/%s %s -I%s -I../../", CompilerOptions, outdir, libName, files, derelictImportDir); 83 } 84 } else version(LDC) { 85 pragma(msg, "Using the LDC compiler."); 86 87 static immutable string CompilerOptions = "-lib -O -release -enable-inlining -w -wi"; 88 89 string buildCompileString(string files, string libName) { 90 return format("ldc2 %s -of%s/%s %s -I%s", CompilerOptions, outdir, libName, files, derelictImportDir); 91 } 92 } else 93 static assert(false, "Unknown compiler."); 94 95 static if (Windows && DMD) { 96 static immutable string Prefix = ""; 97 static immutable string Extension = ".lib"; 98 } else static if (Posix || GDC || LDC) { 99 static immutable string Prefix = "lib"; 100 static immutable string Extension = ".a"; 101 } else 102 static assert(false, "Unknown operating system and compiler."); 103 104 struct Package { 105 const string name; 106 string path; 107 } 108 109 final abstract class Pack { 110 public: 111 static const Internal = Package("Internal", SrcDgame ~ "Internal/"); 112 static const Audio = Package("Audio", SrcDgame ~ "Audio/"); 113 static const Graphics = Package("Graphics", SrcDgame ~ "Graphics/"); 114 static const Math = Package("Math", SrcDgame ~ "Math/"); 115 static const System = Package("System", SrcDgame ~ "System/"); 116 static const Window = Package("Window", SrcDgame ~ "Window/"); 117 } 118 119 // Map package names to source paths. 120 Package[string] pathMap; 121 122 string buildPath; 123 string derelictImportDir; 124 125 debug { 126 string outdir = LibDir ~ "/Debug"; 127 } else { 128 string outdir = LibDir ~ "/Release"; 129 } 130 131 static this() { 132 if (!LibDir.exists()) 133 mkdir(LibDir); 134 135 if (!outdir.exists()) 136 mkdir(outdir); 137 138 // Initializes the source path map. 139 pathMap = [ 140 Pack.Internal.name.toUpper() : Pack.Internal, 141 Pack.Audio.name.toUpper() : Pack.Audio, 142 Pack.Graphics.name.toUpper() : Pack.Graphics, 143 Pack.Math.name.toUpper() : Pack.Math, 144 Pack.System.name.toUpper() : Pack.System, 145 Pack.Window.name.toUpper() : Pack.Window, 146 ]; 147 } 148 149 enum DerelictDirname = "derelict"; 150 151 void main(string[] args) { 152 // Determine the path to this executable so that imports and source files can be found 153 // no matter what the working directory. 154 buildPath = args[0].dirName(); 155 156 string derelictPath = args[0].absolutePath().dirName() ~ "/../../" ~ DerelictDirname; 157 derelictPath = derelictPath.buildNormalizedPath(); 158 159 writeln("Assume '", derelictPath, "' as derelict path."); 160 writeln("Verify...\n"); 161 162 if (.exists(derelictPath)) 163 derelictImportDir = derelictPath; 164 else { 165 writeln("Assume, that the derelict path is in 'external.txt'."); 166 writeln("Verify...\n"); 167 168 if (.exists(buildPath ~ "/external.txt")) { 169 foreach(derelictLine; (cast(string) .read(buildPath ~ "/external.txt")).splitLines()) { 170 derelictLine = derelictLine.strip(); 171 if(derelictLine.length > 0 && derelictLine[0] != '#' && .exists(derelictLine)) { 172 derelictImportDir = derelictLine; 173 break; 174 } 175 } 176 } 177 178 if (derelictImportDir.length == 0 || !.exists(derelictImportDir)) { 179 do { 180 writeln("Derelict import path not found."); 181 writeln("You can enter the full path in 'external.txt'."); 182 writeln("But for now, please enter the full path here or press q for quit:"); 183 184 derelictImportDir = readln().chop(); 185 186 if (derelictImportDir[0] == 'q') 187 return; 188 189 if (.exists(derelictImportDir)) { 190 if (derelictImportDir.endsWith(DerelictDirname)) 191 break; 192 193 derelictImportDir ~= DerelictDirname; 194 if (.exists(derelictImportDir)) 195 break; 196 } 197 } while (true); 198 } 199 } 200 201 if (derelictImportDir.endsWith(DerelictDirname)) 202 derelictImportDir = derelictImportDir.dirName(); 203 204 if (buildPath != "./") { 205 outdir = buildNormalizedPath(buildPath, outdir); 206 207 // fix up the package paths 208 foreach (ref Package pack; pathMap) { 209 pack.path = buildNormalizedPath(buildPath, pack.path); 210 } 211 } 212 213 if (args.length == 1) 214 buildAll(); 215 else 216 buildSome(args[1 .. $]); 217 } 218 219 // Build all of the Derelict libraries. 220 void buildAll() { 221 writeln("Building all packages."); 222 try { 223 foreach (ref Package pack; pathMap) { 224 buildPackage(pack); 225 } 226 227 writeln("\nAll builds complete\n"); 228 } 229 // Eat any ErrnoException. The compiler will print the right thing on a failed build, no need 230 // to clutter the output with exception info. 231 catch (ErrnoException e) { 232 writeln("\nBuild Failed!\n"); 233 } 234 } 235 236 // Build only the packages specified on the command line. 237 void buildSome(string[] args) { 238 try { 239 // If any of the args matches a key in the pathMap, build 240 // that package. 241 foreach (s; args) { 242 auto key = s.toUpper(); 243 244 Package* p = key in pathMap; 245 if (!p) 246 writefln("Unknown package '%s'", s); 247 else 248 buildPackage(*p); 249 } 250 251 writeln("\nSelected builds complete\n"); 252 } catch (ErrnoException e) { 253 writeln("\nBuild Failed!\n"); 254 } 255 } 256 257 void buildPackage(ref const Package pack) { 258 writeln(); 259 writefln("Building %s/%s", Project, pack.name); 260 writeln(); 261 262 // Build up a string of all .d files in the package directory. 263 string joined; 264 foreach (string s; dirEntries(pack.path, SpanMode.breadth)) { 265 if (s.endsWith(".d")) { 266 writeln(s); 267 joined ~= " " ~ s; 268 } 269 } 270 271 writeln(); 272 273 string libName = format("%s%s%s%s", Prefix, Project, pack.name, Extension); 274 string arg = buildCompileString(joined, libName); 275 writeln(arg); 276 277 (system(arg) == 0).enforce(new ErrnoException("Build failure")); 278 279 writeln("Build succeeded."); 280 }