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 Target[] dlangObjects( 20 alias sourcesFunc = Sources!(), 21 CompilerFlags compilerFlags = CompilerFlags(), 22 ImportPaths importPaths = ImportPaths(), 23 StringImportPaths stringImportPaths = StringImportPaths(), 24 ProjectDir projectDir = ProjectDir(), 25 ) 26 () 27 { 28 return dlangObjectFiles( 29 sourcesToFileNames!sourcesFunc, 30 compilerFlags.value, 31 importPaths.value, 32 stringImportPaths.value, 33 projectDir.value, 34 ); 35 } 36 37 38 Target[] dlangObjectsPerPackage( 39 alias sourcesFunc = Sources!(), 40 CompilerFlags compilerFlags = CompilerFlags(), 41 ImportPaths importPaths = ImportPaths(), 42 StringImportPaths stringImportPaths = StringImportPaths(), 43 ProjectDir projectDir = ProjectDir(), 44 ) 45 () 46 { 47 return dlangObjectFilesPerPackage( 48 sourcesToFileNames!sourcesFunc, 49 compilerFlags.value, 50 importPaths.value, 51 stringImportPaths.value, 52 [], // implicits 53 projectDir.value, 54 ); 55 } 56 57 Target[] dlangObjectsPerModule( 58 alias sourcesFunc = Sources!(), 59 CompilerFlags compilerFlags = CompilerFlags(), 60 ImportPaths importPaths = ImportPaths(), 61 StringImportPaths stringImportPaths = StringImportPaths(), 62 ProjectDir projectDir = ProjectDir(), 63 ) 64 () 65 { 66 return dlangObjectFilesPerModule( 67 sourcesToFileNames!sourcesFunc, 68 compilerFlags.value, 69 importPaths.value, 70 stringImportPaths.value, 71 [], // implicits 72 projectDir.value, 73 ); 74 } 75 76 77 78 /** 79 Generate object file(s) for D sources. 80 Depending on command-line options compiles all files together, per package, or per module. 81 */ 82 Target[] dlangObjectFiles(in string[] srcFiles, 83 in string flags = "", 84 in string[] importPaths = [], 85 in string[] stringImportPaths = [], 86 Target[] implicits = [], 87 in string projDir = "$project") 88 @safe 89 { 90 91 import reggae.config: options; 92 93 auto func = options.perModule 94 ? &dlangObjectFilesPerModule 95 : options.allAtOnce 96 ? &dlangObjectFilesTogether 97 : &dlangObjectFilesPerPackage; 98 99 return func(srcFiles, flags, importPaths, stringImportPaths, implicits, projDir); 100 } 101 102 /// Generate object files for D sources, compiling the whole package together. 103 Target[] dlangObjectFilesPerPackage(in string[] srcFiles, 104 in string flags = "", 105 in string[] importPaths = [], 106 in string[] stringImportPaths = [], 107 Target[] implicits = [], 108 in string projDir = "$project") 109 @trusted pure 110 { 111 112 if(srcFiles.empty) return []; 113 auto command(in string[] files) { 114 return compileCommand(files[0].packagePath ~ ".d", 115 flags, 116 importPaths, 117 stringImportPaths, 118 projDir); 119 } 120 return srcFiles.byPackage.map!(a => Target(a[0].packagePath.objFileName, 121 command(a), 122 a.map!(a => Target(a)).array, 123 implicits)).array; 124 } 125 126 /// Generate object files for D sources, compiling each module separately 127 Target[] dlangObjectFilesPerModule(in string[] srcFiles, 128 in string flags = "", 129 in string[] importPaths = [], 130 in string[] stringImportPaths = [], 131 Target[] implicits = [], 132 in string projDir = "$project") 133 @trusted pure 134 { 135 return srcFiles.map!(a => objectFile(const SourceFile(a), 136 const Flags(flags), 137 const ImportPaths(importPaths), 138 const StringImportPaths(stringImportPaths), 139 implicits, 140 projDir)).array; 141 } 142 143 /// Generate object files for D sources, compiling all of them together 144 Target[] dlangObjectFilesTogether(in string[] srcFiles, 145 in string flags = "", 146 in string[] importPaths = [], 147 in string[] stringImportPaths = [], 148 Target[] implicits = [], 149 in string projDir = "$project") 150 @trusted pure 151 { 152 153 if(srcFiles.empty) return []; 154 auto command = compileCommand(srcFiles[0], flags, importPaths, stringImportPaths, projDir); 155 return [Target(srcFiles[0].packagePath.objFileName, command, srcFiles.map!(a => Target(a)).array, implicits)]; 156 } 157 158 159 /** 160 Currently only works for D. This convenience rule builds a D scriptlike, automatically 161 calculating which files must be compiled in a similar way to rdmd. 162 All paths are relative to projectPath. 163 This template function is provided as a wrapper around the regular runtime version 164 below so it can be aliased without trying to call it at runtime. Basically, it's a 165 way to use the runtime scriptlike without having define a function in reggaefile.d, 166 i.e.: 167 $(D 168 alias myApp = scriptlike!(...); 169 mixin build!(myApp); 170 ) 171 vs. 172 $(D 173 Build myBuld() { return scriptlike(..); } 174 ) 175 */ 176 Target scriptlike(App app, 177 Flags flags = Flags(), 178 ImportPaths importPaths = ImportPaths(), 179 StringImportPaths stringImportPaths = StringImportPaths(), 180 alias linkWithFunction = () { return cast(Target[])[];}) 181 () @trusted 182 { 183 auto linkWith = linkWithFunction(); 184 import reggae.config: options; 185 return scriptlike(options.projectPath, app, flags, importPaths, stringImportPaths, linkWith); 186 } 187 188 189 //regular runtime version of scriptlike 190 //all paths relative to projectPath 191 //@trusted because of .array 192 Target scriptlike(in string projectPath, 193 in App app, in Flags flags, 194 in ImportPaths importPaths, 195 in StringImportPaths stringImportPaths, 196 Target[] linkWith) 197 @trusted 198 { 199 200 import std.path; 201 202 if(getLanguage(app.srcFileName.value) != Language.D) 203 throw new Exception("'scriptlike' rule only works with D files"); 204 205 auto mainObj = objectFile(SourceFile(app.srcFileName.value), flags, importPaths, stringImportPaths); 206 const output = runDCompiler(projectPath, buildPath(projectPath, app.srcFileName.value), flags.value, 207 importPaths.value, stringImportPaths.value); 208 209 const files = dMainDepSrcs(output).map!(a => a.removeProjectPath).array; 210 auto dependencies = [mainObj] ~ dlangObjectFiles(files, flags.value, 211 importPaths.value, stringImportPaths.value); 212 213 return link(ExeName(app.exeFileName.value), dependencies ~ linkWith); 214 } 215 216 217 //@trusted because of splitter 218 private auto runDCompiler(in string projectPath, 219 in string srcFileName, 220 in string flags, 221 in string[] importPaths, 222 in string[] stringImportPaths) @trusted { 223 224 import std.process: execute; 225 import std.exception: enforce; 226 import std.conv:text; 227 import std.path: buildPath; 228 229 immutable compiler = "dmd"; 230 const compArgs = [compiler] ~ flags.splitter.array ~ 231 importPaths.map!(a => "-I" ~ buildPath(projectPath, a)).array ~ 232 stringImportPaths.map!(a => "-J" ~ buildPath(projectPath, a)).array ~ 233 ["-o-", "-v", "-c", srcFileName]; 234 const compRes = execute(compArgs); 235 enforce(compRes.status == 0, 236 text("scriptlike could not run ", compArgs.join(" "), ":\n", compRes.output)); 237 return compRes.output; 238 }