1 /** 2 Build settings definitions. 3 4 Copyright: © 2013-2014 rejectedsoftware e.K. 5 License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 6 Authors: Sönke Ludwig 7 */ 8 module dub.compilers.buildsettings; 9 10 import dub.internal.vibecompat.inet.path; 11 12 import std.array : array; 13 import std.algorithm : filter, any; 14 import std.path : globMatch; 15 import std.typecons : BitFlags; 16 import std.algorithm.iteration : uniq; 17 import std.range : chain; 18 19 /// BuildPlatform specific settings, like needed libraries or additional 20 /// include paths. 21 struct BuildSettings { 22 import dub.internal.vibecompat.data.serialization : byName; 23 24 TargetType targetType; 25 string targetPath; 26 string targetName; 27 string workingDirectory; 28 string mainSourceFile; 29 string[] dflags; 30 string[] lflags; 31 string[] libs; 32 string[] linkerFiles; 33 string[] sourceFiles; 34 string[] injectSourceFiles; 35 string[] copyFiles; 36 string[] extraDependencyFiles; 37 string[] versions; 38 string[] debugVersions; 39 string[] versionFilters; 40 string[] debugVersionFilters; 41 string[] importPaths; 42 string[] stringImportPaths; 43 string[] importFiles; 44 string[] stringImportFiles; 45 string[] preGenerateCommands; 46 string[] postGenerateCommands; 47 string[] preBuildCommands; 48 string[] postBuildCommands; 49 string[] preRunCommands; 50 string[] postRunCommands; 51 string[string] environments; 52 string[string] buildEnvironments; 53 string[string] runEnvironments; 54 string[string] preGenerateEnvironments; 55 string[string] postGenerateEnvironments; 56 string[string] preBuildEnvironments; 57 string[string] postBuildEnvironments; 58 string[string] preRunEnvironments; 59 string[string] postRunEnvironments; 60 @byName BuildRequirements requirements; 61 @byName BuildOptions options; 62 63 BuildSettings dup() 64 const { 65 import std.traits: FieldNameTuple; 66 import std.algorithm: map; 67 import std.typecons: tuple; 68 import std.array: assocArray; 69 BuildSettings ret; 70 foreach (m; FieldNameTuple!BuildSettings) { 71 static if (is(typeof(__traits(getMember, ret, m) = __traits(getMember, this, m).dup))) 72 __traits(getMember, ret, m) = __traits(getMember, this, m).dup; 73 else static if (is(typeof(add(__traits(getMember, ret, m), __traits(getMember, this, m))))) 74 add(__traits(getMember, ret, m), __traits(getMember, this, m)); 75 else static if (is(typeof(__traits(getMember, ret, m) = __traits(getMember, this, m)))) 76 __traits(getMember, ret, m) = __traits(getMember, this, m); 77 else static assert(0, "Cannot duplicate BuildSettings." ~ m); 78 } 79 assert(ret.targetType == targetType); 80 assert(ret.targetName == targetName); 81 assert(ret.importPaths == importPaths); 82 return ret; 83 } 84 85 void add(in BuildSettings bs) 86 { 87 addDFlags(bs.dflags); 88 addLFlags(bs.lflags); 89 addLibs(bs.libs); 90 addLinkerFiles(bs.linkerFiles); 91 addSourceFiles(bs.sourceFiles); 92 addCopyFiles(bs.copyFiles); 93 addExtraDependencyFiles(bs.extraDependencyFiles); 94 addVersions(bs.versions); 95 addDebugVersions(bs.debugVersions); 96 addVersionFilters(bs.versionFilters); 97 addDebugVersionFilters(bs.debugVersionFilters); 98 addImportPaths(bs.importPaths); 99 addStringImportPaths(bs.stringImportPaths); 100 addImportFiles(bs.importFiles); 101 addStringImportFiles(bs.stringImportFiles); 102 addPreGenerateCommands(bs.preGenerateCommands); 103 addPostGenerateCommands(bs.postGenerateCommands); 104 addPreBuildCommands(bs.preBuildCommands); 105 addPostBuildCommands(bs.postBuildCommands); 106 addPreRunCommands(bs.preRunCommands); 107 addPostRunCommands(bs.postRunCommands); 108 } 109 110 void addDFlags(in string[] value...) { dflags = chain(dflags, value.dup).uniq.array; } 111 void prependDFlags(in string[] value...) { prepend(dflags, value); } 112 void removeDFlags(in string[] value...) { remove(dflags, value); } 113 void addLFlags(in string[] value...) { lflags ~= value; } 114 void addLibs(in string[] value...) { add(libs, value); } 115 void addLinkerFiles(in string[] value...) { add(linkerFiles, value); } 116 void addSourceFiles(in string[] value...) { add(sourceFiles, value); } 117 void prependSourceFiles(in string[] value...) { prepend(sourceFiles, value); } 118 void removeSourceFiles(in string[] value...) { removePaths(sourceFiles, value); } 119 void addInjectSourceFiles(in string[] value...) { add(injectSourceFiles, value); } 120 void addCopyFiles(in string[] value...) { add(copyFiles, value); } 121 void addExtraDependencyFiles(in string[] value...) { add(extraDependencyFiles, value); } 122 void addVersions(in string[] value...) { add(versions, value); } 123 void addDebugVersions(in string[] value...) { add(debugVersions, value); } 124 void addVersionFilters(in string[] value...) { add(versionFilters, value); } 125 void addDebugVersionFilters(in string[] value...) { add(debugVersionFilters, value); } 126 void addImportPaths(in string[] value...) { add(importPaths, value); } 127 void addStringImportPaths(in string[] value...) { add(stringImportPaths, value); } 128 void prependStringImportPaths(in string[] value...) { prepend(stringImportPaths, value); } 129 void addImportFiles(in string[] value...) { add(importFiles, value); } 130 void addStringImportFiles(in string[] value...) { addSI(stringImportFiles, value); } 131 void addPreGenerateCommands(in string[] value...) { add(preGenerateCommands, value, false); } 132 void addPostGenerateCommands(in string[] value...) { add(postGenerateCommands, value, false); } 133 void addPreBuildCommands(in string[] value...) { add(preBuildCommands, value, false); } 134 void addPostBuildCommands(in string[] value...) { add(postBuildCommands, value, false); } 135 void addPreRunCommands(in string[] value...) { add(preRunCommands, value, false); } 136 void addPostRunCommands(in string[] value...) { add(postRunCommands, value, false); } 137 void addEnvironments(in string[string] value) { add(environments, value); } 138 void updateEnvironments(in string[string] value) { update(environments, value); } 139 void addBuildEnvironments(in string[string] value) { add(buildEnvironments, value); } 140 void updateBuildEnvironments(in string[string] value) { update(buildEnvironments, value); } 141 void addRunEnvironments(in string[string] value) { add(runEnvironments, value); } 142 void updateRunEnvironments(in string[string] value) { update(runEnvironments, value); } 143 void addPreGenerateEnvironments(in string[string] value) { add(preGenerateEnvironments, value); } 144 void updatePreGenerateEnvironments(in string[string] value) { update(preGenerateEnvironments, value); } 145 void addPostGenerateEnvironments(in string[string] value) { add(postGenerateEnvironments, value); } 146 void updatePostGenerateEnvironments(in string[string] value) { update(postGenerateEnvironments, value); } 147 void addPreBuildEnvironments(in string[string] value) { add(preBuildEnvironments, value); } 148 void updatePreBuildEnvironments(in string[string] value) { update(preBuildEnvironments, value); } 149 void addPostBuildEnvironments(in string[string] value) { add(postBuildEnvironments, value); } 150 void updatePostBuildEnvironments(in string[string] value) { update(postBuildEnvironments, value); } 151 void addPreRunEnvironments(in string[string] value) { add(preRunEnvironments, value); } 152 void updatePreRunEnvironments(in string[string] value) { update(preRunEnvironments, value); } 153 void addPostRunEnvironments(in string[string] value) { add(postRunEnvironments, value); } 154 void updatePostRunEnvironments(in string[string] value) { update(postRunEnvironments, value); } 155 void addRequirements(in BuildRequirement[] value...) { foreach (v; value) this.requirements |= v; } 156 void addRequirements(in BuildRequirements value) { this.requirements |= value; } 157 void addOptions(in BuildOption[] value...) { foreach (v; value) this.options |= v; } 158 void addOptions(in BuildOptions value) { this.options |= value; } 159 void removeOptions(in BuildOption[] value...) { foreach (v; value) this.options &= ~v; } 160 void removeOptions(in BuildOptions value) { this.options &= ~value; } 161 162 private: 163 static auto filterDuplicates(T)(ref string[] arr, in T vals, bool noDuplicates = true) 164 { 165 return noDuplicates 166 ? vals.filter!(filtered => !arr.any!(item => item == filtered)).array 167 : vals; 168 } 169 170 // Append vals to arr without adding duplicates. 171 static void add(ref string[] arr, in string[] vals, bool noDuplicates = true) 172 { 173 // vals might contain duplicates, add each val individually 174 foreach (val; vals) 175 arr ~= filterDuplicates(arr, [val], noDuplicates); 176 } 177 // Append vals to AA 178 static void add(ref string[string] aa, in string[string] vals) 179 { 180 // vals might contain duplicated keys, add each val individually 181 foreach (key, val; vals) 182 if (key !in aa) 183 aa[key] = val; 184 } 185 // Update vals to AA 186 static void update(ref string[string] aa, in string[string] vals) 187 { 188 // If there are duplicate keys, they will be ignored and overwritten. 189 foreach (key, val; vals) 190 aa[key] = val; 191 } 192 193 unittest 194 { 195 auto ary = ["-dip1000", "-vgc"]; 196 BuildSettings.add(ary, ["-dip1000", "-vgc"]); 197 assert(ary == ["-dip1000", "-vgc"]); 198 BuildSettings.add(ary, ["-dip1001", "-vgc"], false); 199 assert(ary == ["-dip1000", "-vgc", "-dip1001", "-vgc"]); 200 BuildSettings.add(ary, ["-dupflag", "-notdupflag", "-dupflag"]); 201 assert(ary == ["-dip1000", "-vgc", "-dip1001", "-vgc", "-dupflag", "-notdupflag"]); 202 } 203 204 // Prepend arr by vals without adding duplicates. 205 static void prepend(ref string[] arr, in string[] vals, bool noDuplicates = true) 206 { 207 import std.range : retro; 208 // vals might contain duplicates, add each val individually 209 foreach (val; vals.retro) 210 arr = filterDuplicates(arr, [val], noDuplicates) ~ arr; 211 } 212 213 unittest 214 { 215 auto ary = ["-dip1000", "-vgc"]; 216 BuildSettings.prepend(ary, ["-dip1000", "-vgc"]); 217 assert(ary == ["-dip1000", "-vgc"]); 218 BuildSettings.prepend(ary, ["-dip1001", "-vgc"], false); 219 assert(ary == ["-dip1001", "-vgc", "-dip1000", "-vgc"]); 220 BuildSettings.prepend(ary, ["-dupflag", "-notdupflag", "-dupflag"]); 221 assert(ary == ["-notdupflag", "-dupflag", "-dip1001", "-vgc", "-dip1000", "-vgc"]); 222 } 223 224 // add string import files (avoids file name duplicates in addition to path duplicates) 225 static void addSI(ref string[] arr, in string[] vals) 226 { 227 bool[string] existing; 228 foreach (v; arr) existing[NativePath(v).head.name] = true; 229 foreach (v; vals) { 230 auto s = NativePath(v).head.name; 231 if (s !in existing) { 232 existing[s] = true; 233 arr ~= v; 234 } 235 } 236 } 237 238 unittest 239 { 240 auto ary = ["path/foo.txt"]; 241 BuildSettings.addSI(ary, ["path2/foo2.txt"]); 242 assert(ary == ["path/foo.txt", "path2/foo2.txt"]); 243 BuildSettings.addSI(ary, ["path2/foo.txt"]); // no duplicate basenames 244 assert(ary == ["path/foo.txt", "path2/foo2.txt"]); 245 } 246 247 static bool pathMatch(string path, string pattern) 248 { 249 import std.functional : memoize; 250 251 alias nativePath = memoize!((string stringPath) => NativePath(stringPath)); 252 253 return nativePath(path) == nativePath(pattern) || globMatch(path, pattern); 254 } 255 256 static void removeValuesFromArray(alias Match)(ref string[] arr, in string[] vals) 257 { 258 bool matches(string s) 259 { 260 return vals.any!(item => Match(s, item)); 261 } 262 arr = arr.filter!(s => !matches(s)).array; 263 } 264 265 static void removePaths(ref string[] arr, in string[] vals) 266 { 267 removeValuesFromArray!(pathMatch)(arr, vals); 268 } 269 270 unittest 271 { 272 auto ary = ["path1", "root/path1", "root/path2", "root2/path1"]; 273 BuildSettings.removePaths(ary, ["path1"]); 274 assert(ary == ["root/path1", "root/path2", "root2/path1"]); 275 BuildSettings.removePaths(ary, ["*/path1"]); 276 assert(ary == ["root/path2"]); 277 BuildSettings.removePaths(ary, ["foo", "bar", "root/path2"]); 278 assert(ary == []); 279 } 280 281 static void remove(ref string[] arr, in string[] vals) 282 { 283 removeValuesFromArray!((a, b) => a == b)(arr, vals); 284 } 285 286 unittest 287 { 288 import std.string : join; 289 290 auto ary = ["path1", "root/path1", "root/path2", "root2/path1"]; 291 BuildSettings.remove(ary, ["path1"]); 292 assert(ary == ["root/path1", "root/path2", "root2/path1"]); 293 BuildSettings.remove(ary, ["root/path*"]); 294 assert(ary == ["root/path1", "root/path2", "root2/path1"]); 295 BuildSettings.removePaths(ary, ["foo", "root/path2", "bar", "root2/path1"]); 296 assert(ary == ["root/path1"]); 297 BuildSettings.remove(ary, ["root/path1", "foo"]); 298 assert(ary == []); 299 } 300 } 301 302 enum BuildSetting { 303 dflags = 1<<0, 304 lflags = 1<<1, 305 libs = 1<<2, 306 sourceFiles = 1<<3, 307 copyFiles = 1<<4, 308 versions = 1<<5, 309 debugVersions = 1<<6, 310 importPaths = 1<<7, 311 stringImportPaths = 1<<8, 312 options = 1<<9, 313 none = 0, 314 commandLine = dflags|copyFiles, 315 commandLineSeparate = commandLine|lflags, 316 all = dflags|lflags|libs|sourceFiles|copyFiles|versions|debugVersions|importPaths|stringImportPaths|options, 317 noOptions = all & ~options 318 } 319 320 enum TargetType { 321 autodetect, 322 none, 323 executable, 324 library, 325 sourceLibrary, 326 dynamicLibrary, 327 staticLibrary, 328 object 329 } 330 331 enum BuildRequirement { 332 none = 0, /// No special requirements 333 allowWarnings = 1<<0, /// Warnings do not abort compilation 334 silenceWarnings = 1<<1, /// Don't show warnings 335 disallowDeprecations = 1<<2, /// Using deprecated features aborts compilation 336 silenceDeprecations = 1<<3, /// Don't show deprecation warnings 337 disallowInlining = 1<<4, /// Avoid function inlining, even in release builds 338 disallowOptimization = 1<<5, /// Avoid optimizations, even in release builds 339 requireBoundsCheck = 1<<6, /// Always perform bounds checks 340 requireContracts = 1<<7, /// Leave assertions and contracts enabled in release builds 341 relaxProperties = 1<<8, /// DEPRECATED: Do not enforce strict property handling (-property) 342 noDefaultFlags = 1<<9, /// Do not issue any of the default build flags (e.g. -debug, -w, -property etc.) - use only for development purposes 343 } 344 345 struct BuildRequirements { 346 import dub.internal.vibecompat.data.serialization : ignore; 347 348 @ignore BitFlags!BuildRequirement values; 349 this(BuildRequirement req) { values = req; } 350 351 alias values this; 352 } 353 354 enum BuildOption { 355 none = 0, /// Use compiler defaults 356 debugMode = 1<<0, /// Compile in debug mode (enables contracts, -debug) 357 releaseMode = 1<<1, /// Compile in release mode (disables assertions and bounds checks, -release) 358 coverage = 1<<2, /// Enable code coverage analysis (-cov) 359 debugInfo = 1<<3, /// Enable symbolic debug information (-g) 360 debugInfoC = 1<<4, /// Enable symbolic debug information in C compatible form (-gc) 361 alwaysStackFrame = 1<<5, /// Always generate a stack frame (-gs) 362 stackStomping = 1<<6, /// Perform stack stomping (-gx) 363 inline = 1<<7, /// Perform function inlining (-inline) 364 noBoundsCheck = 1<<8, /// Disable all bounds checking (-noboundscheck) 365 optimize = 1<<9, /// Enable optimizations (-O) 366 profile = 1<<10, /// Emit profiling code (-profile) 367 unittests = 1<<11, /// Compile unit tests (-unittest) 368 verbose = 1<<12, /// Verbose compiler output (-v) 369 ignoreUnknownPragmas = 1<<13, /// Ignores unknown pragmas during compilation (-ignore) 370 syntaxOnly = 1<<14, /// Don't generate object files (-o-) 371 warnings = 1<<15, /// Enable warnings (-wi) 372 warningsAsErrors = 1<<16, /// Treat warnings as errors (-w) 373 ignoreDeprecations = 1<<17, /// Do not warn about using deprecated features (-d) 374 deprecationWarnings = 1<<18, /// Warn about using deprecated features (-dw) 375 deprecationErrors = 1<<19, /// Stop compilation upon usage of deprecated features (-de) 376 property = 1<<20, /// DEPRECATED: Enforce property syntax (-property) 377 profileGC = 1<<21, /// Profile runtime allocations 378 pic = 1<<22, /// Generate position independent code 379 betterC = 1<<23, /// Compile in betterC mode (-betterC) 380 lowmem = 1<<24, /// Compile in lowmem mode (-lowmem) 381 382 // for internal usage 383 _docs = 1<<25, // Write ddoc to docs 384 _ddox = 1<<26 // Compile docs.json 385 } 386 387 struct BuildOptions { 388 import dub.internal.vibecompat.data.serialization : ignore; 389 390 @ignore BitFlags!BuildOption values; 391 this(BuildOption opt) { values = opt; } 392 this(BitFlags!BuildOption v) { values = v; } 393 394 alias values this; 395 } 396 397 /** 398 All build options that will be inherited upwards in the dependency graph 399 400 Build options in this category fulfill one of the following properties: 401 $(UL 402 $(LI The option affects the semantics of the generated code) 403 $(LI The option affects if a certain piece of code is valid or not) 404 $(LI The option enabled meta information in dependent projects that are useful for the dependee (e.g. debug information)) 405 ) 406 */ 407 enum BuildOptions inheritedBuildOptions = BuildOption.debugMode | BuildOption.releaseMode 408 | BuildOption.coverage | BuildOption.debugInfo | BuildOption.debugInfoC 409 | BuildOption.alwaysStackFrame | BuildOption.stackStomping | BuildOption.inline 410 | BuildOption.noBoundsCheck | BuildOption.profile | BuildOption.ignoreUnknownPragmas 411 | BuildOption.syntaxOnly | BuildOption.warnings | BuildOption.warningsAsErrors 412 | BuildOption.ignoreDeprecations | BuildOption.deprecationWarnings 413 | BuildOption.deprecationErrors | BuildOption.property | BuildOption.profileGC 414 | BuildOption.pic;