1 /** 2 High-level rules for compiling D files. For a D-only application with 3 no dub dependencies, $(D executable) should suffice. If the app depends 4 on dub packages, consult the reggae.rules.dub module instead. 5 */ 6 7 module reggae.rules.d; 8 9 import reggae.types; 10 import reggae.build; 11 import reggae.sorting; 12 import reggae.dependencies: dMainDepSrcs; 13 import reggae.rules.common; 14 import std.algorithm; 15 import std.array; 16 17 //objectFile, objectFiles and link are the only default rules 18 //They work by serialising the rule to use piggy-backing on Target's string 19 //command attribute. It's horrible, but it works with the original decision 20 //of using strings as commands. Should be changed to be a sum type where 21 //a string represents a shell command and other variants call D code. 22 23 //generate object file(s) for a D package. By default generates one per package, 24 //if reggae.config.perModule is true, generates one per module 25 Target[] objectFiles(in string[] srcFiles, in string flags = "", 26 in string[] importPaths = [], in string[] stringImportPaths = [], 27 in string projDir = "$project") @safe pure { 28 import reggae.config; 29 auto func = perModule ? &objectFilesPerModule : &objectFilesPerPackage; 30 return func(srcFiles, flags, importPaths, stringImportPaths, projDir); 31 } 32 33 Target[] objectFilesPerPackage(in string[] srcFiles, in string flags = "", 34 in string[] importPaths = [], in string[] stringImportPaths = [], 35 in string projDir = "$project") @trusted pure { 36 37 if(srcFiles.empty) return []; 38 const command = compileCommand(srcFiles[0], flags, importPaths, stringImportPaths, projDir); 39 return srcFiles.byPackage.map!(a => Target(a[0].packagePath.objFileName, 40 command, 41 a.map!(a => Target(a)).array)).array; 42 } 43 44 Target[] objectFilesPerModule(in string[] srcFiles, in string flags = "", 45 in string[] importPaths = [], in string[] stringImportPaths = [], 46 in string projDir = "$project") @trusted pure { 47 48 return srcFiles.map!(a => objectFile(a, flags, importPaths, stringImportPaths, projDir)).array; 49 } 50 51 52 /** 53 Currently only works for D. This convenience rule builds a D executable, automatically 54 calculating which files must be compiled in a similar way to rdmd. 55 All paths are relative to projectPath. 56 This template function is provided as a wrapper around the regular runtime version 57 below so it can be aliased without trying to call it at runtime. Basically, it's a 58 way to use the runtime executable without having define a function in reggaefile.d, 59 i.e.: 60 $(D 61 alias myApp = executable!(...); 62 mixin build!(myApp); 63 ) 64 vs. 65 $(D 66 Build myBuld() { return executable(..); } 67 ) 68 */ 69 Target executable(App app, 70 Flags flags = Flags(), 71 ImportPaths importPaths = ImportPaths(), 72 StringImportPaths stringImportPaths = StringImportPaths(), 73 alias linkWithFunction = () { return cast(Target[])[];}) 74 () { 75 auto linkWith = linkWithFunction(); 76 return executable(app, flags, importPaths, stringImportPaths, linkWith); 77 } 78 79 80 //regular runtime version of executable 81 //all paths relative to projectPath 82 //@trusted because of .array 83 Target executable(in App app, in Flags flags, 84 in ImportPaths importPaths, 85 in StringImportPaths stringImportPaths, 86 in Target[] linkWith) @trusted { 87 88 auto mainObj = objectFile(app.srcFileName, flags.value, importPaths.value, stringImportPaths.value); 89 const output = runDCompiler(buildPath(projectPath, app.srcFileName), flags.value, 90 importPaths.value, stringImportPaths.value); 91 92 const files = dMainDepSrcs(output).map!(a => a.removeProjectPath).array; 93 const dependencies = [mainObj] ~ objectFiles(files, flags.value, 94 importPaths.value, stringImportPaths.value); 95 96 return link(app.exeFileName, dependencies ~ linkWith); 97 } 98 99 100 //@trusted because of splitter 101 private auto runDCompiler(in string srcFileName, in string flags, 102 in string[] importPaths, in string[] stringImportPaths) @trusted { 103 104 import std.process: execute; 105 import std.exception: enforce; 106 import std.conv:text; 107 108 immutable compiler = "dmd"; 109 const compArgs = [compiler] ~ flags.splitter.array ~ 110 importPaths.map!(a => "-I" ~ buildPath(projectPath, a)).array ~ 111 stringImportPaths.map!(a => "-J" ~ buildPath(projectPath, a)).array ~ 112 ["-o-", "-v", "-c", srcFileName]; 113 const compRes = execute(compArgs); 114 enforce(compRes.status == 0, text("executable could not run ", compArgs.join(" "), ":\n", compRes.output)); 115 return compRes.output; 116 }