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; 176 import reggae.config: options; 177 import reggae.dub.info: DubObjsDir; 178 import std.array: join; 179 import std.path: buildPath; 180 import std.file: getcwd; 181 182 const isStaticLibrary = 183 dubInfo.targetType == TargetType.library || 184 dubInfo.targetType == TargetType.staticLibrary; 185 const sharedFlags = dubInfo.targetType == TargetType.dynamicLibrary 186 ? "-shared" 187 : ""; 188 const allLinkerFlags = (linkerFlags ~ dubInfo.linkerFlags ~ sharedFlags).join(" "); 189 190 auto allObjs = objs(targetName, 191 dubInfo, 192 includeMain, 193 compilerFlags, 194 compilationMode, 195 extraObjects, 196 startingIndex); 197 198 const name = realName(targetName, dubInfo); 199 auto target = isStaticLibrary 200 ? staticLibraryTarget(name, allObjs)[0] 201 : link(ExeName(name), 202 allObjs, 203 Flags(allLinkerFlags)); 204 205 return dubInfo.postBuildCommands == "" 206 ? target 207 : Target.phony("postBuild", dubInfo.postBuildCommands, target); 208 } 209 210 /** 211 All dub packages object files from the dependencies, but nothing from the 212 main package (the one actually being built). 213 */ 214 Target[] dubDependencies(CompilerFlags compilerFlags = CompilerFlags()) 215 () // runtime args 216 { 217 return dubDependencies!(Configuration("default"), compilerFlags)(); 218 } 219 220 221 ///ditto 222 Target[] dubDependencies(Configuration config, 223 CompilerFlags compilerFlags = CompilerFlags()) 224 () // runtime args 225 { 226 const dubInfo = configToDubInfo[config.value]; 227 const startingIndex = 1; 228 return objs(dubInfo.targetName, 229 dubInfo, 230 No.main, 231 compilerFlags.value, 232 CompilationMode.options, 233 [], // extra objects 234 startingIndex); 235 } 236 237 238 239 /** 240 All dub object files for a configuration 241 */ 242 Target[] dubObjects(Configuration config, 243 CompilerFlags compilerFlags = CompilerFlags(), 244 Flag!"main" includeMain = No.main, 245 CompilationMode compilationMode = CompilationMode.options) 246 () 247 { 248 const dubInfo = configToDubInfo[config.value]; 249 return objs(dubInfo.targetName, 250 dubInfo, 251 includeMain, 252 compilerFlags.value, 253 compilationMode); 254 } 255 256 /** 257 Object files from one dub package 258 */ 259 Target[] dubPackageObjects( 260 DubPackageName dubPackageName, 261 CompilerFlags compilerFlags = CompilerFlags(), 262 CompilationMode compilationMode = CompilationMode.all, 263 Flag!"main" includeMain = No.main, 264 ) 265 () 266 { 267 return dubPackageObjects!( 268 dubPackageName, 269 Configuration("default"), 270 compilerFlags, 271 compilationMode, 272 includeMain 273 ); 274 } 275 276 /** 277 Object files from one dub package 278 */ 279 Target[] dubPackageObjects( 280 DubPackageName dubPackageName, 281 Configuration config = Configuration("default"), 282 CompilerFlags compilerFlags = CompilerFlags(), 283 CompilationMode compilationMode = CompilationMode.all, 284 Flag!"main" includeMain = No.main, 285 ) 286 () 287 { 288 return configToDubInfo[config.value].packageNameToTargets( 289 dubPackageName.value, 290 includeMain, 291 compilerFlags.value, 292 compilationMode, 293 ); 294 } 295 296 297 ImportPaths dubImportPaths(Configuration config = Configuration("default"))() { 298 return ImportPaths(configToDubInfo[config.value].allImportPaths); 299 } 300 301 /** 302 Link a target taking into account the dub linker flags 303 */ 304 Target dubLink(TargetName targetName, 305 Configuration config = Configuration("default"), 306 alias objsFunction = () { Target[] t; return t; }, 307 LinkerFlags linkerFlags = LinkerFlags() 308 ) 309 () 310 { 311 import std.array: join; 312 return link!( 313 ExeName(targetName.value), 314 objsFunction, 315 Flags((linkerFlags.value ~ configToDubInfo[config.value].linkerFlags).join(" ")) 316 ); 317 } 318 319 private Target[] objs(in TargetName targetName, 320 in DubInfo dubInfo, 321 in Flag!"main" includeMain, 322 in string compilerFlags, 323 in CompilationMode compilationMode, 324 Target[] extraObjects = [], 325 in size_t startingIndex = 0) 326 { 327 328 auto dubObjs = dubInfo.toTargets(includeMain, 329 compilerFlags, 330 compilationMode, 331 dubObjsDir(targetName, dubInfo), 332 startingIndex); 333 auto allObjs = dubObjs ~ extraObjects; 334 335 return allObjs; 336 } 337 338 private string realName(in TargetName targetName, in DubInfo dubInfo) { 339 import std.path: buildPath; 340 // otherwise the target wouldn't be top-level in the presence of 341 // postBuildCommands 342 return dubInfo.postBuildCommands == "" 343 ? targetName.value 344 : buildPath("$project", targetName.value); 345 } 346 347 private auto dubObjsDir(in TargetName targetName, in DubInfo dubInfo) { 348 import reggae.config: options; 349 import reggae.dub.info: DubObjsDir; 350 return DubObjsDir(options.dubObjsDir, realName(targetName, dubInfo) ~ ".objs"); 351 } 352 }