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