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(const SourceFile(a), 49 const Flags(flags), 50 const ImportPaths(importPaths), 51 const StringImportPaths(stringImportPaths), 52 projDir)).array; 53 } 54 55 56 /** 57 Currently only works for D. This convenience rule builds a D executable, automatically 58 calculating which files must be compiled in a similar way to rdmd. 59 All paths are relative to projectPath. 60 This template function is provided as a wrapper around the regular runtime version 61 below so it can be aliased without trying to call it at runtime. Basically, it's a 62 way to use the runtime executable without having define a function in reggaefile.d, 63 i.e.: 64 $(D 65 alias myApp = executable!(...); 66 mixin build!(myApp); 67 ) 68 vs. 69 $(D 70 Build myBuld() { return executable(..); } 71 ) 72 */ 73 Target executable(App app, 74 Flags flags = Flags(), 75 ImportPaths importPaths = ImportPaths(), 76 StringImportPaths stringImportPaths = StringImportPaths(), 77 alias linkWithFunction = () { return cast(Target[])[];}) 78 () { 79 auto linkWith = linkWithFunction(); 80 return executable(app, flags, importPaths, stringImportPaths, linkWith); 81 } 82 83 84 //regular runtime version of executable 85 //all paths relative to projectPath 86 //@trusted because of .array 87 Target executable(in App app, in Flags flags, 88 in ImportPaths importPaths, 89 in StringImportPaths stringImportPaths, 90 in Target[] linkWith) @trusted { 91 92 if(getLanguage(app.srcFileName) != Language.D) 93 throw new Exception("'executable' rule only works with D files"); 94 95 auto mainObj = objectFile(SourceFile(app.srcFileName), flags, importPaths, stringImportPaths); 96 const output = runDCompiler(buildPath(projectPath, app.srcFileName), flags.value, 97 importPaths.value, stringImportPaths.value); 98 99 const files = dMainDepSrcs(output).map!(a => a.removeProjectPath).array; 100 const dependencies = [mainObj] ~ objectFiles(files, flags.value, 101 importPaths.value, stringImportPaths.value); 102 103 return link(ExeName(app.exeFileName), dependencies ~ linkWith); 104 } 105 106 107 //@trusted because of splitter 108 private auto runDCompiler(in string srcFileName, in string flags, 109 in string[] importPaths, in string[] stringImportPaths) @trusted { 110 111 import std.process: execute; 112 import std.exception: enforce; 113 import std.conv:text; 114 115 immutable compiler = "dmd"; 116 const compArgs = [compiler] ~ flags.splitter.array ~ 117 importPaths.map!(a => "-I" ~ buildPath(projectPath, a)).array ~ 118 stringImportPaths.map!(a => "-J" ~ buildPath(projectPath, a)).array ~ 119 ["-o-", "-v", "-c", srcFileName]; 120 const compRes = execute(compArgs); 121 enforce(compRes.status == 0, text("executable could not run ", compArgs.join(" "), ":\n", compRes.output)); 122 return compRes.output; 123 }