1 /** 2 High-level rules for building dub projects. The rules in this module 3 only replicate what dub does itself. This allows a reggaefile.d to 4 reuse the information that dub already knows about. 5 */ 6 7 module reggae.rules.dub; 8 9 import reggae.config; 10 11 enum CompilationMode { 12 module_, /// compile per module 13 package_, /// compile per package 14 all, /// compile all source files 15 options, /// whatever the command-line option was 16 } 17 18 struct DubPackageName { 19 string value; 20 } 21 22 static if(isDubProject) { 23 24 import reggae.dub.info; 25 import reggae.types; 26 import reggae.build; 27 import reggae.rules.common; 28 import std.traits; 29 import std.typecons; 30 31 /** 32 Builds the main dub target (equivalent of "dub build") 33 */ 34 Target dubDefaultTarget(CompilerFlags compilerFlags = CompilerFlags(), 35 LinkerFlags linkerFlags = LinkerFlags(), 36 CompilationMode compilationMode = CompilationMode.options) 37 () 38 { 39 import std.string: split; 40 41 enum config = "default"; 42 enum dubInfo = configToDubInfo[config]; 43 enum targetName = dubInfo.targetName; 44 enum linkerFlags = dubInfo.mainLinkerFlags ~ linkerFlags.value.split(" "); 45 return dubTarget( 46 targetName, 47 dubInfo, 48 compilerFlags.value, 49 linkerFlags, 50 Yes.main, 51 compilationMode, 52 ); 53 } 54 55 56 /** 57 A target corresponding to `dub test` 58 */ 59 Target dubTestTarget(CompilerFlags compilerFlags = CompilerFlags(), 60 LinkerFlags linkerFlags = LinkerFlags()) 61 () 62 { 63 import std.typecons: No, Yes; 64 65 66 static if (__VERSION__ < 2079 || (__VERSION__ >= 2081 && __VERSION__ < 2084)) { 67 // these dmd versions have a bug pertaining to separate compilation and __traits(getUnitTests), 68 // we default here to compiling all-at-once for the unittest build 69 enum compilationMode = CompilationMode.all; 70 } 71 else 72 enum compilationMode = CompilationMode.options; 73 74 return dubTestTarget!(compilerFlags, linkerFlags, compilationMode)(); 75 } 76 77 /** 78 A target corresponding to `dub test` 79 */ 80 Target dubTestTarget(CompilerFlags compilerFlags = CompilerFlags(), 81 LinkerFlags linkerFlags = LinkerFlags(), 82 CompilationMode compilationMode) 83 () 84 { 85 import reggae.dub.info: TargetType, targetName; 86 import std.string: split; 87 import std.exception : enforce; 88 import std.conv: text; 89 90 const config = "unittest" in configToDubInfo ? "unittest" : "default"; 91 auto actualCompilerFlags = compilerFlags.value; 92 if("unittest" !in configToDubInfo) actualCompilerFlags ~= " -unittest"; 93 const dubInfo = configToDubInfo[config]; 94 enforce(dubInfo.packages.length, text("No dub packages found for config '", config, "'")); 95 const hasMain = dubInfo.packages[0].mainSourceFile != ""; 96 const string[] emptyStrings; 97 const extraLinkerFlags = hasMain ? emptyStrings : ["-main"]; 98 const actualLinkerFlags = extraLinkerFlags ~ linkerFlags.value.split(" "); 99 const defaultTargetHasName = configToDubInfo["default"].packages.length > 0; 100 const sameNameAsDefaultTarget = 101 defaultTargetHasName 102 && dubInfo.targetName == configToDubInfo["default"].targetName; 103 const name = sameNameAsDefaultTarget 104 // don't emit two targets with the same name 105 ? targetName(TargetType.executable, "ut") 106 : dubInfo.targetName; 107 108 return dubTarget(name, 109 dubInfo, 110 actualCompilerFlags, 111 actualLinkerFlags, 112 Yes.main, 113 compilationMode); 114 } 115 116 /** 117 Builds a particular dub configuration (executable, unittest, etc.) 118 */ 119 Target dubConfigurationTarget(Configuration config = Configuration("default"), 120 CompilerFlags compilerFlags = CompilerFlags(), 121 LinkerFlags linkerFlags = LinkerFlags(), 122 Flag!"main" includeMain = Yes.main, 123 CompilationMode compilationMode = CompilationMode.options, 124 alias objsFunction = () { Target[] t; return t; }, 125 ) 126 () if(isCallable!objsFunction) 127 { 128 import std.string: split; 129 130 const dubInfo = configToDubInfo[config.value]; 131 return dubTarget(dubInfo.targetName, 132 dubInfo, 133 compilerFlags.value, 134 linkerFlags.value.split(" "), 135 includeMain, 136 compilationMode, 137 objsFunction()); 138 } 139 140 Target dubTarget( 141 TargetName targetName, 142 Configuration config, 143 CompilerFlags compilerFlags = CompilerFlags(), 144 LinkerFlags linkerFlags = LinkerFlags(), 145 Flag!"main" includeMain = Yes.main, 146 CompilationMode compilationMode = CompilationMode.options, 147 alias objsFunction = () { Target[] t; return t; }, 148 ) 149 () 150 { 151 import std.array: split; 152 return dubTarget(targetName, 153 configToDubInfo[config.value], 154 compilerFlags.value, 155 linkerFlags.value.split(" "), 156 includeMain, 157 compilationMode, 158 objsFunction(), 159 ); 160 } 161 162 163 Target dubTarget( 164 in TargetName targetName, 165 in DubInfo dubInfo, 166 in string compilerFlags, 167 in string[] linkerFlags = [], 168 in Flag!"main" includeMain = Yes.main, 169 in CompilationMode compilationMode = CompilationMode.options, 170 Target[] extraObjects = [], 171 in size_t startingIndex = 0, 172 ) 173 { 174 175 import reggae.rules.common: staticLibraryTarget, link; 176 import reggae.dub.info: DubObjsDir; 177 import std.array: join; 178 import std.path: buildPath; 179 import std.file: getcwd; 180 181 const isStaticLibrary = 182 dubInfo.targetType == TargetType.library || 183 dubInfo.targetType == TargetType.staticLibrary; 184 const sharedFlags = dubInfo.targetType == TargetType.dynamicLibrary 185 ? "-shared" 186 : ""; 187 const allLinkerFlags = (linkerFlags ~ dubInfo.linkerFlags ~ sharedFlags).join(" "); 188 189 auto allObjs = objs(targetName, 190 dubInfo, 191 includeMain, 192 compilerFlags, 193 compilationMode, 194 extraObjects, 195 startingIndex); 196 197 const name = realName(targetName, dubInfo); 198 199 auto target = isStaticLibrary 200 ? staticLibraryTarget(name, allObjs)[0] 201 : dubInfo.targetType == TargetType.none 202 ? Target.phony(name, "", allObjs) 203 : link(ExeName(name), 204 allObjs, 205 Flags(allLinkerFlags)); 206 207 return dubInfo.postBuildCommands == "" 208 ? target 209 : Target.phony("postBuild", dubInfo.postBuildCommands, target); 210 } 211 212 /** 213 All dub packages object files from the dependencies, but nothing from the 214 main package (the one actually being built). 215 */ 216 Target[] dubDependencies(CompilerFlags compilerFlags = CompilerFlags()) 217 () // runtime args 218 { 219 return dubDependencies!(Configuration("default"), compilerFlags)(); 220 } 221 222 223 ///ditto 224 Target[] dubDependencies(Configuration config, 225 CompilerFlags compilerFlags = CompilerFlags()) 226 () // runtime args 227 { 228 const dubInfo = configToDubInfo[config.value]; 229 const startingIndex = 1; 230 return objs(dubInfo.targetName, 231 dubInfo, 232 No.main, 233 compilerFlags.value, 234 CompilationMode.options, 235 [], // extra objects 236 startingIndex); 237 } 238 239 240 241 /** 242 All dub object files for a configuration 243 */ 244 Target[] dubObjects(Configuration config, 245 CompilerFlags compilerFlags = CompilerFlags(), 246 Flag!"main" includeMain = No.main, 247 CompilationMode compilationMode = CompilationMode.options) 248 () 249 { 250 const dubInfo = configToDubInfo[config.value]; 251 return objs(dubInfo.targetName, 252 dubInfo, 253 includeMain, 254 compilerFlags.value, 255 compilationMode); 256 } 257 258 /** 259 Object files from one dub package 260 */ 261 Target[] dubPackageObjects( 262 DubPackageName dubPackageName, 263 CompilerFlags compilerFlags = CompilerFlags(), 264 CompilationMode compilationMode = CompilationMode.all, 265 Flag!"main" includeMain = No.main, 266 ) 267 () 268 { 269 return dubPackageObjects!( 270 dubPackageName, 271 Configuration("default"), 272 compilerFlags, 273 compilationMode, 274 includeMain 275 ); 276 } 277 278 /** 279 Object files from one dub package 280 */ 281 Target[] dubPackageObjects( 282 DubPackageName dubPackageName, 283 Configuration config = Configuration("default"), 284 CompilerFlags compilerFlags = CompilerFlags(), 285 CompilationMode compilationMode = CompilationMode.all, 286 Flag!"main" includeMain = No.main, 287 ) 288 () 289 { 290 return configToDubInfo[config.value].packageNameToTargets( 291 dubPackageName.value, 292 includeMain, 293 compilerFlags.value, 294 compilationMode, 295 ); 296 } 297 298 299 ImportPaths dubImportPaths(Configuration config = Configuration("default"))() { 300 return ImportPaths(configToDubInfo[config.value].allImportPaths); 301 } 302 303 /** 304 Link a target taking into account the dub linker flags 305 */ 306 Target dubLink(TargetName targetName, 307 Configuration config = Configuration("default"), 308 alias objsFunction = () { Target[] t; return t; }, 309 LinkerFlags linkerFlags = LinkerFlags() 310 ) 311 () 312 { 313 import std.array: join; 314 return link!( 315 ExeName(targetName.value), 316 objsFunction, 317 Flags((linkerFlags.value ~ configToDubInfo[config.value].linkerFlags).join(" ")) 318 ); 319 } 320 321 private Target[] objs(in TargetName targetName, 322 in DubInfo dubInfo, 323 in Flag!"main" includeMain, 324 in string compilerFlags, 325 in CompilationMode compilationMode, 326 Target[] extraObjects = [], 327 in size_t startingIndex = 0) 328 { 329 330 auto dubObjs = dubInfo.toTargets(includeMain, 331 compilerFlags, 332 compilationMode, 333 dubObjsDir(targetName, dubInfo), 334 startingIndex); 335 auto allObjs = dubObjs ~ extraObjects; 336 337 return allObjs; 338 } 339 340 private string realName(in TargetName targetName, in DubInfo dubInfo) { 341 import std.path: buildPath; 342 // otherwise the target wouldn't be top-level in the presence of 343 // postBuildCommands 344 auto ret = dubInfo.postBuildCommands == "" 345 ? targetName.value 346 : buildPath("$project", targetName.value); 347 if(ret == "") ret = "placeholder"; 348 return ret; 349 } 350 351 private auto dubObjsDir(in TargetName targetName, in DubInfo dubInfo) { 352 import reggae.config: options; 353 import reggae.dub.info: DubObjsDir; 354 return DubObjsDir(options.dubObjsDir, realName(targetName, dubInfo) ~ ".objs"); 355 } 356 }