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