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