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