1 2 /** 3 High-level rules for compiling D files. For a D-only application with 4 no dub dependencies, $(D scriptlike) should suffice. If the app depends 5 on dub packages, consult the reggae.rules.dub module instead. 6 */ 7 8 module reggae.rules.d; 9 10 import reggae.types; 11 import reggae.build; 12 import reggae.sorting; 13 import reggae.dependencies: dMainDepSrcs; 14 import reggae.rules.common; 15 import std.algorithm; 16 import std.array; 17 18 19 20 //generate object file(s) for a D package. By default generates one per package, 21 //if reggae.config.perModule is true, generates one per module 22 Target[] dlangPackageObjectFiles(in string[] srcFiles, in string flags = "", 23 in string[] importPaths = [], in string[] stringImportPaths = [], 24 in string projDir = "$project") @safe { 25 26 import reggae.config: options; 27 28 auto func = options.perModule 29 ? &dlangPackageObjectFilesPerModule 30 : options.allAtOnce 31 ? &dlangPackageObjectFilesTogether 32 : &dlangPackageObjectFilesPerPackage; 33 34 return func(srcFiles, flags, importPaths, stringImportPaths, projDir); 35 } 36 37 Target[] dlangPackageObjectFilesPerPackage(in string[] srcFiles, 38 in string flags = "", 39 in string[] importPaths = [], 40 in string[] stringImportPaths = [], 41 in string projDir = "$project") 42 @trusted pure { 43 44 if(srcFiles.empty) return []; 45 auto command(in string[] files) { 46 return compileCommand(files[0].packagePath ~ ".d", 47 flags, 48 importPaths, 49 stringImportPaths, 50 projDir); 51 } 52 return srcFiles.byPackage.map!(a => Target(a[0].packagePath.objFileName, 53 command(a), 54 a.map!(a => Target(a)).array)).array; 55 } 56 57 Target[] dlangPackageObjectFilesPerModule(in string[] srcFiles, in string flags = "", 58 in string[] importPaths = [], in string[] stringImportPaths = [], 59 in string projDir = "$project") @trusted pure { 60 return srcFiles.map!(a => objectFile(const SourceFile(a), 61 const Flags(flags), 62 const ImportPaths(importPaths), 63 const StringImportPaths(stringImportPaths), 64 projDir)).array; 65 } 66 67 // compiles all source files in one go 68 Target[] dlangPackageObjectFilesTogether(in string[] srcFiles, in string flags = "", 69 in string[] importPaths = [], in string[] stringImportPaths = [], 70 in string projDir = "$project") @trusted pure { 71 72 if(srcFiles.empty) return []; 73 auto command = compileCommand(srcFiles[0], flags, importPaths, stringImportPaths, projDir); 74 return [Target(srcFiles[0].packagePath.objFileName, command, srcFiles.map!(a => Target(a)).array)]; 75 } 76 77 78 /** 79 Currently only works for D. This convenience rule builds a D scriptlike, automatically 80 calculating which files must be compiled in a similar way to rdmd. 81 All paths are relative to projectPath. 82 This template function is provided as a wrapper around the regular runtime version 83 below so it can be aliased without trying to call it at runtime. Basically, it's a 84 way to use the runtime scriptlike without having define a function in reggaefile.d, 85 i.e.: 86 $(D 87 alias myApp = scriptlike!(...); 88 mixin build!(myApp); 89 ) 90 vs. 91 $(D 92 Build myBuld() { return scriptlike(..); } 93 ) 94 */ 95 Target scriptlike(App app, 96 Flags flags = Flags(), 97 ImportPaths importPaths = ImportPaths(), 98 StringImportPaths stringImportPaths = StringImportPaths(), 99 alias linkWithFunction = () { return cast(Target[])[];}) 100 () @trusted { 101 auto linkWith = linkWithFunction(); 102 import reggae.config: options; 103 return scriptlike(options.projectPath, app, flags, importPaths, stringImportPaths, linkWith); 104 } 105 106 107 //regular runtime version of scriptlike 108 //all paths relative to projectPath 109 //@trusted because of .array 110 Target scriptlike(in string projectPath, 111 in App app, in Flags flags, 112 in ImportPaths importPaths, 113 in StringImportPaths stringImportPaths, 114 Target[] linkWith) @trusted { 115 116 import std.path; 117 118 if(getLanguage(app.srcFileName.value) != Language.D) 119 throw new Exception("'scriptlike' rule only works with D files"); 120 121 auto mainObj = objectFile(SourceFile(app.srcFileName.value), flags, importPaths, stringImportPaths); 122 const output = runDCompiler(projectPath, buildPath(projectPath, app.srcFileName.value), flags.value, 123 importPaths.value, stringImportPaths.value); 124 125 const files = dMainDepSrcs(output).map!(a => a.removeProjectPath).array; 126 auto dependencies = [mainObj] ~ dlangPackageObjectFiles(files, flags.value, 127 importPaths.value, stringImportPaths.value); 128 129 return link(ExeName(app.exeFileName.value), dependencies ~ linkWith); 130 } 131 132 133 //@trusted because of splitter 134 private auto runDCompiler(in string projectPath, 135 in string srcFileName, 136 in string flags, 137 in string[] importPaths, 138 in string[] stringImportPaths) @trusted { 139 140 import std.process: execute; 141 import std.exception: enforce; 142 import std.conv:text; 143 144 immutable compiler = "dmd"; 145 const compArgs = [compiler] ~ flags.splitter.array ~ 146 importPaths.map!(a => "-I" ~ buildPath(projectPath, a)).array ~ 147 stringImportPaths.map!(a => "-J" ~ buildPath(projectPath, a)).array ~ 148 ["-o-", "-v", "-c", srcFileName]; 149 const compRes = execute(compArgs); 150 enforce(compRes.status == 0, text("scriptlike could not run ", compArgs.join(" "), ":\n", compRes.output)); 151 return compRes.output; 152 }