|
1 | 1 | import os |
| 2 | +import re |
2 | 3 | import subprocess |
3 | 4 | import sys |
4 | 5 | from SCons.Script import ARGUMENTS |
|
9 | 10 | # Helper methods |
10 | 11 |
|
11 | 12 |
|
| 13 | +def get_compiler_version(env): |
| 14 | + """ |
| 15 | + Returns an array of version numbers as ints: [major, minor, patch]. |
| 16 | + The return array should have at least two values (major, minor). |
| 17 | + """ |
| 18 | + if not env.get("is_msvc", False): |
| 19 | + # Not using -dumpversion as some GCC distros only return major, and |
| 20 | + # Clang used to return hardcoded 4.2.1: # https://reviews.llvm.org/D56803 |
| 21 | + try: |
| 22 | + version = subprocess.check_output([env.subst(env["CXX"]), "--version"]).strip().decode("utf-8") |
| 23 | + except (subprocess.CalledProcessError, OSError): |
| 24 | + print("Couldn't parse CXX environment variable to infer compiler version.") |
| 25 | + return None |
| 26 | + else: # TODO: Implement for MSVC |
| 27 | + return None |
| 28 | + match = re.search( |
| 29 | + "(?:(?<=version )|(?<=\) )|(?<=^))" |
| 30 | + "(?P<major>\d+)" |
| 31 | + "(?:\.(?P<minor>\d*))?" |
| 32 | + "(?:\.(?P<patch>\d*))?" |
| 33 | + "(?:-(?P<metadata1>[0-9a-zA-Z-]*))?" |
| 34 | + "(?:\+(?P<metadata2>[0-9a-zA-Z-]*))?" |
| 35 | + "(?: (?P<date>[0-9]{8}|[0-9]{6})(?![0-9a-zA-Z]))?", |
| 36 | + version, |
| 37 | + ) |
| 38 | + if match is not None: |
| 39 | + return match.groupdict() |
| 40 | + else: |
| 41 | + return None |
| 42 | + |
| 43 | + |
| 44 | +def using_gcc(env): |
| 45 | + return "gcc" in os.path.basename(env["CC"]) |
| 46 | + |
| 47 | + |
| 48 | +def using_clang(env): |
| 49 | + return "clang" in os.path.basename(env["CC"]) |
| 50 | + |
| 51 | + |
| 52 | +def using_emcc(env): |
| 53 | + return "emcc" in os.path.basename(env["CC"]) |
| 54 | + |
| 55 | + |
12 | 56 | def get_cmdline_bool(option, default): |
13 | 57 | """We use `ARGUMENTS.get()` to check if options were manually overridden on the command line, |
14 | 58 | and SCons' _text2bool helper to convert them to booleans, otherwise they're handled as strings. |
@@ -49,6 +93,8 @@ def options(opts): |
49 | 93 | ) |
50 | 94 | opts.Add(BoolVariable("debug_symbols", "Build with debugging symbols", True)) |
51 | 95 | opts.Add(BoolVariable("dev_build", "Developer build with dev-only debugging code (DEV_ENABLED)", False)) |
| 96 | + opts.Add(EnumVariable("warnings", "Level of compilation warnings", "all", ("extra", "all", "moderate", "no"))) |
| 97 | + opts.Add(BoolVariable("werror", "Treat compiler warnings as errors", False)) |
52 | 98 |
|
53 | 99 |
|
54 | 100 | def exists(env): |
@@ -115,6 +161,37 @@ def generate(env): |
115 | 161 | env.Append(LINKFLAGS=["/OPT:REF"]) |
116 | 162 | elif env["optimize"] == "debug" or env["optimize"] == "none": |
117 | 163 | env.Append(CCFLAGS=["/Od"]) |
| 164 | + |
| 165 | + # Warnings |
| 166 | + if env["warnings"] == "no": |
| 167 | + env.Append(CCFLAGS=["/w"]) |
| 168 | + else: |
| 169 | + if env["warnings"] == "extra": |
| 170 | + env.Append(CCFLAGS=["/W4"]) |
| 171 | + elif env["warnings"] == "all": |
| 172 | + env.Append(CCFLAGS=["/W3"]) |
| 173 | + elif env["warnings"] == "moderate": |
| 174 | + env.Append(CCFLAGS=["/W2"]) |
| 175 | + # Disable warnings which we don't plan to fix. |
| 176 | + |
| 177 | + env.Append( |
| 178 | + CCFLAGS=[ |
| 179 | + "/wd4100", # C4100 (unreferenced formal parameter): Doesn't play nice with polymorphism. |
| 180 | + "/wd4127", # C4127 (conditional expression is constant) |
| 181 | + "/wd4201", # C4201 (non-standard nameless struct/union): Only relevant for C89. |
| 182 | + "/wd4244", # C4244 C4245 C4267 (narrowing conversions): Unavoidable at this scale. |
| 183 | + "/wd4245", |
| 184 | + "/wd4267", |
| 185 | + "/wd4305", # C4305 (truncation): double to float or real_t, too hard to avoid. |
| 186 | + "/wd4514", # C4514 (unreferenced inline function has been removed) |
| 187 | + "/wd4714", # C4714 (function marked as __forceinline not inlined) |
| 188 | + "/wd4820", # C4820 (padding added after construct) |
| 189 | + ] |
| 190 | + ) |
| 191 | + |
| 192 | + if env["werror"]: |
| 193 | + env.Append(CCFLAGS=["/WX"]) |
| 194 | + |
118 | 195 | else: |
119 | 196 | if env["debug_symbols"]: |
120 | 197 | # Adding dwarf-4 explicitly makes stacktraces work with clang builds, |
@@ -142,3 +219,64 @@ def generate(env): |
142 | 219 | env.Append(CCFLAGS=["-Og"]) |
143 | 220 | elif env["optimize"] == "none": |
144 | 221 | env.Append(CCFLAGS=["-O0"]) |
| 222 | + |
| 223 | + # Warnings |
| 224 | + cc_version = get_compiler_version(env) or { |
| 225 | + "major": None, |
| 226 | + "minor": None, |
| 227 | + "patch": None, |
| 228 | + "metadata1": None, |
| 229 | + "metadata2": None, |
| 230 | + "date": None, |
| 231 | + } |
| 232 | + cc_version_major = int(cc_version["major"] or -1) |
| 233 | + cc_version_minor = int(cc_version["minor"] or -1) |
| 234 | + |
| 235 | + common_warnings = [] |
| 236 | + |
| 237 | + if using_gcc(env): |
| 238 | + common_warnings += ["-Wshadow-local", "-Wno-misleading-indentation"] |
| 239 | + if cc_version_major == 7: # Bogus warning fixed in 8+. |
| 240 | + common_warnings += ["-Wno-strict-overflow"] |
| 241 | + if cc_version_major < 11: |
| 242 | + # Regression in GCC 9/10, spams so much in our variadic templates |
| 243 | + # that we need to outright disable it. |
| 244 | + common_warnings += ["-Wno-type-limits"] |
| 245 | + if cc_version_major >= 12: # False positives in our error macros, see GH-58747. |
| 246 | + common_warnings += ["-Wno-return-type"] |
| 247 | + elif using_clang(env) or using_emcc(env): |
| 248 | + # We often implement `operator<` for structs of pointers as a requirement |
| 249 | + # for putting them in `Set` or `Map`. We don't mind about unreliable ordering. |
| 250 | + common_warnings += ["-Wno-ordered-compare-function-pointers"] |
| 251 | + |
| 252 | + if env["warnings"] == "extra": |
| 253 | + env.Append(CCFLAGS=["-Wall", "-Wextra", "-Wwrite-strings", "-Wno-unused-parameter"] + common_warnings) |
| 254 | + env.Append(CXXFLAGS=["-Wctor-dtor-privacy", "-Wnon-virtual-dtor"]) |
| 255 | + if using_gcc(env): |
| 256 | + env.Append( |
| 257 | + CCFLAGS=[ |
| 258 | + "-Walloc-zero", |
| 259 | + "-Wduplicated-branches", |
| 260 | + "-Wduplicated-cond", |
| 261 | + "-Wstringop-overflow=4", |
| 262 | + ] |
| 263 | + ) |
| 264 | + env.Append(CXXFLAGS=["-Wplacement-new=1"]) |
| 265 | + # Need to fix a warning with AudioServer lambdas before enabling. |
| 266 | + # if cc_version_major != 9: # GCC 9 had a regression (GH-36325). |
| 267 | + # env.Append(CXXFLAGS=["-Wnoexcept"]) |
| 268 | + if cc_version_major >= 9: |
| 269 | + env.Append(CCFLAGS=["-Wattribute-alias=2"]) |
| 270 | + if cc_version_major >= 11: # Broke on MethodBind templates before GCC 11. |
| 271 | + env.Append(CCFLAGS=["-Wlogical-op"]) |
| 272 | + elif using_clang(env) or using_emcc(env): |
| 273 | + env.Append(CCFLAGS=["-Wimplicit-fallthrough"]) |
| 274 | + elif env["warnings"] == "all": |
| 275 | + env.Append(CCFLAGS=["-Wall"] + common_warnings) |
| 276 | + elif env["warnings"] == "moderate": |
| 277 | + env.Append(CCFLAGS=["-Wall", "-Wno-unused"] + common_warnings) |
| 278 | + else: # 'no' |
| 279 | + env.Append(CCFLAGS=["-w"]) |
| 280 | + |
| 281 | + if env["werror"]: |
| 282 | + env.Append(CCFLAGS=["-Werror"]) |
0 commit comments