# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # The following flags are explicitly passed to C++ compiler invocations from rust # even when only passing base flags. If you add more similarly important flags, # please consider whether they should be handled the same way. # See config/makefiles/rust.mk. # We support C++14, but we don't want to enable the sized deallocation # facilities in C++14 yet. check_and_add_flag("-fno-sized-deallocation", compiler=cxx_compiler) # Likewise for C++17 and aligned allocation. It's not immediately obvious # from the clang and GCC documentation, but they both support this. check_and_add_flag("-fno-aligned-new", compiler=cxx_compiler) # Detect static linkage of libstdc++ # ============================================================== @depends( cxx_compiler, extra_toolchain_flags, linker_ldflags, llvm_objdump, when=target_has_linux_kernel, ) @checking("whether we're trying to statically link with libstdc++") @imports("os") @imports("re") @imports(_from="mozbuild.shellutil", _import="split", _as="shell_split") def link_libstdcxx_statically( cxx_compiler, extra_toolchain_flags, linker_ldflags, llvm_objdump, ): with create_temporary_file(suffix=".out") as bin_path: # This test is quite conservative: it assumes dynamic linkage if the # compilation step fails or if the binary format is not supported. But # it still detects basic issues. # FIXME: At some point we should make these dependencies explicit. # See #1903233. env_flags = [] if "CXXFLAGS" in os.environ: env_flags.extend(shell_split(os.environ["CXXFLAGS"])) if "LDFLAGS" in os.environ: env_flags.extend(shell_split(os.environ["LDFLAGS"])) if ( try_invoke_compiler( None, [cxx_compiler.compiler] + env_flags + cxx_compiler.flags + linker_ldflags, cxx_compiler.language, """ #include int main() { std::cout << 1;} """, ["-o", bin_path] + (extra_toolchain_flags or []), wrapper=cxx_compiler.wrapper, onerror=lambda: None, ) is not None ): objdump_command = [llvm_objdump, "--private-headers", bin_path] result, stdout, stderr = get_cmd_output(*objdump_command) # This is very conservative :-). return ( result == 0 and not stderr and not re.search("NEEDED.*lib(std)?c\\+\\+", stdout) ) @depends(when=link_libstdcxx_statically) def check_libstdcxx_linkage(): die("Firefox does not support linking statically with libstdc++") check_and_add_flag( "-mieee", when=target_has_linux_kernel & depends(target.cpu)(lambda cpu: cpu == "Alpha"), ) # Identical Code Folding option( "--enable-icf", help="{Enable|Disable} Identical Code Folding", default=~developer_options, ) enable_icf = depends("--enable-icf")(lambda v: bool(v)) with only_when(building_with_gnu_compatible_cc): with only_when(~developer_options | enable_icf): add_flag("-ffunction-sections") add_flag("-fdata-sections") add_flag("-fno-math-errno") add_flag("-fno-exceptions", compiler=cxx_compiler) add_flag("-D_HAS_EXCEPTIONS=0", when=target_is_windows) # We only use -faddrsig with clang on android, and all version of clang we # support have it. add_addrsig_flag = depends(c_compiler, when=target_is_android)( lambda c: c.type == "clang" ) @depends( c_compiler, extra_toolchain_flags, linker_ldflags, llvm_objdump, add_addrsig_flag, when=building_with_gnu_compatible_cc & gcc_use_gnu_ld & enable_icf, ) @checking("whether the linker supports Identical Code Folding") @imports("os") def linker_supports_icf( c_compiler, extra_toolchain_flags, linker_ldflags, llvm_objdump, add_addrsig_flag ): with create_temporary_file(suffix=".out") as bin_path: if ( try_invoke_compiler( None, [c_compiler.compiler] + c_compiler.flags + linker_ldflags, c_compiler.language, """ int foo() {return 42;} int bar() {return 42;} int main() {return 0;} """, ["-o", bin_path, "-Wl,--icf=safe", "-ffunction-sections"] + (["-faddrsig"] if add_addrsig_flag else []) + (extra_toolchain_flags or []), wrapper=c_compiler.wrapper, onerror=lambda: None, ) is not None ): objdump_command = [llvm_objdump, "-t", bin_path] result = check_cmd_output(*objdump_command, onerror=lambda: None) if result is not None: sym_addr = { line[5]: line[0] for line in [line.split() for line in result.splitlines()] if len(line) >= 6 } foo_addr = sym_addr.get("foo") bar_addr = sym_addr.get("bar") return foo_addr == bar_addr and foo_addr is not None @depends(compilation_flags, linker_flags, add_addrsig_flag, when=linker_supports_icf) def add_icf_flags(compilation_flags, linker_flags, add_addrsig_flag): if add_addrsig_flag: compilation_flags.cflags.append("-faddrsig") compilation_flags.cxxflags.append("-faddrsig") linker_flags.ldflags.append("-Wl,--icf=safe") option( "--disable-new-pass-manager", help="Use the legacy LLVM pass manager in clang builds", ) @depends( "--enable-new-pass-manager", c_compiler, host, target, "MOZ_PGO", enable_fuzzing, ubsan, ) def pass_manager(enabled, compiler, host, target, pgo, enable_fuzzing, ubsan): if compiler.type not in ("clang", "clang-cl"): return None # As of clang 13, the default pass manager is the new one. if compiler.version >= "13.0.0": if enabled: return namespace(flags=None, enabled=True) if compiler.type == "clang": return namespace(flags=["-flegacy-pass-manager"], enabled=False) if compiler.type == "clang-cl": return namespace(flags=["-Xclang", "-flegacy-pass-manager"], enabled=False) if not enabled: if compiler.version >= "15.0.0": die("--disable-new-pass-manager is only supported with clang < 15") return None if compiler.version < "9.0.0": if enabled.origin != "default": die("--enable-new-pass-manager is only supported with clang >= 9") return None if host.os == "OSX": # Some native Mac builds hang with the new pass manager. Given the # inability to test in CI, don't take the risk of further breakage. if enabled.origin != "default": die( "--enable-new-pass-manager causes problems on mac hosts with clang < 13" ) return None if target.os == "OSX" and not pgo: # Also disable when cross-compiling to Mac, because plain-ish opt # builds hang. Variants like asan and ccov work fine, but it would be # too tedious to test them all here. PGO is the only thing that matters # enough to make an exception for. if enabled.origin != "default": die( "--enable-new-pass-manager causes problems on mac builds with clang < 13" ) return None if enable_fuzzing and compiler.version < "10.0.0": # Clang 9 does not seem to play well with libFuzzer if enabled.origin != "default": die( "--enable-new-pass-manager causes problems on fuzzing builds with clang < 10" ) return None if ubsan and compiler.version == "10.0.0": # Clang 10.0.0 hangs with some ubsan-inserted code constructs. # This was fixed in 10.0.1 (https://llvm.org/pr45835) if enabled.origin != "default": die( "--enable-new-pass-manager causes problems with ubsan builds with clang 10.0.0" ) return None if compiler.type == "clang": return namespace(flags=["-fexperimental-new-pass-manager"], enabled=True) elif compiler.type == "clang-cl": return namespace( flags=["-Xclang", "-fexperimental-new-pass-manager"], enabled=True ) set_config("MOZ_PASS_MANAGER_FLAGS", pass_manager.flags) # Debugging options ## @depends(moz_debug, asan, target, "--enable-jemalloc") def moz_no_debug_rtl(moz_debug, asan, target, enable_jemalloc): if not moz_debug or asan: return True if enable_jemalloc and target.kernel == "WINNT": return True set_config("MOZ_NO_DEBUG_RTL", moz_no_debug_rtl) @depends( try_compile( includes=["stdio.h"], body='puts("Hello World");', check_msg="for valid debug flags", flags=debug_flags, when=moz_debug, ), debug_flags, when=moz_debug, ) @imports(_from="mozbuild.shellutil", _import="quote") def check_debug_flags(check, flags): if not check: die(f"These compiler flags are invalid: {quote(*flags)}") # Try to make builds more reproducible and allow sharing built artifacts across # source and object directories by using -ffile-prefix-map and friends. To # "unwind" the prefix maps, use: # # (gdb) set substitute-path /topsrcdir/ $topsrcdir/ # # (lldb) settings set target.source-map /topobjdir/ $topobjdir/ # # See, for example, https://lldb.llvm.org/use/map.html. @depends( path_remapping, path_remappings, c_compiler, ) @imports(_from="os", _import="sep") def file_prefix_map_flags(path_remapping, path_remappings, compiler): if "c" not in path_remapping: return [] if (compiler.type == "gcc" and compiler.version < "8.1") or ( compiler.type in ("clang", "clang-cl") and compiler.version < "10.0.0" ): die( f"Compiler of type {compiler.type} and version {compiler.version} " "does not support --enable-path-remapping." ) flags = [] for old, new in path_remappings: # We would prefer to use just -ffile-prefix-map, but clang-cl doesn't # seem to recognize it. for flag in ("-fdebug-prefix-map", "-fmacro-prefix-map"): flag = f"{flag}={old}={new}" if compiler.type in ("gcc", "clang"): flags.append(flag) elif compiler.type == "clang-cl": flags.extend(["-Xclang", flag]) return flags set_config("MOZ_FILE_PREFIX_MAP_FLAGS", file_prefix_map_flags) @depends(developer_options, when=building_with_gnu_compatible_cc) def check_build_id_uuid(developer_options): return developer_options @depends(developer_options, when=building_with_gnu_compatible_cc) def check_build_id_sha1(developer_options): return not developer_options check_and_add_flag("-pipe", when=building_with_gcc) check_and_add_linker_flag("-Wl,--build-id=uuid", when=check_build_id_uuid) check_and_add_linker_flag("-Wl,--build-id=sha1", when=check_build_id_sha1) check_and_add_asm_flag("-Wa,--noexecstack", when=building_with_gnu_compatible_cc) check_and_add_linker_flag("-Wl,-z,noexecstack", when=building_with_gnu_compatible_cc) check_and_add_linker_flag("-Wl,-z,text", when=building_with_gnu_compatible_cc) check_and_add_linker_flag("-Wl,-z,relro", when=building_with_gnu_compatible_cc) check_and_add_linker_flag("-Wl,-z,now", when=building_with_gnu_compatible_cc) check_and_add_linker_flag("-Wl,-z,nocopyreloc", when=building_with_gnu_compatible_cc) check_and_add_linker_optimize_flag("-Wl,-dead_strip", when=target_is_darwin & ~dtrace) check_and_add_asm_flag( "-fPIC", when=building_with_gnu_compatible_cc & ~target_is_windows ) check_and_add_flag("-fPIC", when=building_with_gnu_compatible_cc & ~target_is_windows) set_define("PIC", True, when=target_is_netbsd) # DSO flags manipulations have_linker_support_ignore_unresolved = try_link( flags=["-Wl,--ignore-unresolved-symbol,environ"], check_msg="for --ignore-unresolved-symbol option to the linker", when=building_with_gnu_compatible_cc & gcc_use_gnu_ld & depends(target)(lambda t: t.os in ("DragonFly", "FreeBSD", "NetBSD", "OpenBSD")), ) @depends( dso_flags, gcc_use_gnu_ld, target, asan | msan | any_ubsan | tsan | enable_fuzzing_interfaces, have_linker_support_ignore_unresolved, when=building_with_gnu_compatible_cc, ) @imports(_from="os", _import="environ") def add_gnu_specific_dso_flags( dso_flags, use_gnu_ld, target, use_sanitizer_runtime, have_linker_support_ignore_unresolved, ): ldopts = [] if target.kernel != "Darwin": ldopts.append("-shared") if use_gnu_ld: # Some tools like ASan use a runtime library that is only # linked against executables, so we must allow undefined # symbols for shared objects in some cases. if not use_sanitizer_runtime: if target.os != "WINNT": # Don't allow undefined symbols in libraries ldopts.append("-Wl,-z,defs") # BSDs need `environ' exposed for posix_spawn (bug 753046) if target.os in ("DragonFly", "FreeBSD", "NetBSD", "OpenBSD"): if have_linker_support_ignore_unresolved: ldopts.append("-Wl,--ignore-unresolved-symbol,environ") else: ldopts.append("-Wl,--warn-unresolved-symbols") if librunpath := environ.get("LIBRUNPATH"): if target.os == "NetBSD": ldopts.insert(0, f"-Wl,-R{librunpath}") elif target.os == "OpenBSD": ldopts.insert(0, f"-R{librunpath}") dso_flags.ldopts.extend(ldopts) @depends(dso_flags, target, when=~building_with_gnu_compatible_cc) def add_non_gnu_dso_flags(dso_flags, target): ldopts = ["-SUBSYSTEM:WINDOWS,{}".format(win32_subsystem_version())] if target.cpu == "x86": ldopts.append("-MACHINE:X86") if target.cpu == "x86_64": ldopts.append("-MACHINE:X64") if target.cpu == "aarch64": ldopts.append("-MACHINE:ARM64") dso_flags.ldopts.extend(ldopts) @depends(libfuzzer_flags, when=enable_fuzzing) def sancov(libfuzzer_flags): for flag in libfuzzer_flags.use_flags: if "-fsanitize-coverage" in flag or "-fsanitize=fuzzer" in flag: return True return False @depends(target, build_environment) @imports(_from="os", _import="environ") def moz_fix_link_paths(target, build_env): dist = build_env.dist flags = [f"-Wl,-rpath-link,{dist}/bin"] if target.kernel == "Darwin": return [] if target.kernel == "WINNT": return [] if target.kernel == "SunOS": return [f"-L{dist}/bin"] if target.os == "WASI": return [] return flags set_config("MOZ_FIX_LINK_PATHS", moz_fix_link_paths) @depends("--enable-address-sanitizer", building_with_gnu_compatible_cc) def check_Bsymbolic(enable_asan, building_with_gnu_compatible_cc): return enable_asan and building_with_gnu_compatible_cc # ASan assumes no symbols are being interposed, and when that happens, # it's not happy with it. Inconveniently, since Firefox is exporting # libffi symbols and Gtk+3 pulls system libffi via libwayland-client, # system libffi interposes libffi symbols that ASan assumes are in # libxul, so it barfs about buffer overflows. # Using -Wl,-Bsymbolic ensures no exported symbol can be interposed. check_and_add_linker_flag("-Wl,-Bsymbolic", when=check_Bsymbolic) # Check what kind of list files are supported by the linker @depends(c_compiler, link, linker_ldflags, extra_toolchain_flags) @checking("what kind of list files are supported by the linker") @imports("os") @imports(_from="__builtin__", _import="open") def expand_libs_list_style(c_compiler, link, linker_flags, extra_flags): with create_temporary_file(suffix=".o") as objname, create_temporary_file( suffix=".list" ) as listname, create_temporary_file(suffix=".out") as outname: with open(listname, "w") as fd: fd.write(objname) cflags = c_compiler.flags if ( try_invoke_compiler( # No configure_cache because it would not create the # expected output file. None, [c_compiler.compiler] + cflags, c_compiler.language, "int main() {}", ["-c", "-o", objname], wrapper=c_compiler.wrapper, onerror=lambda: None, ) is None ): die("couldn't compile a simple C file") ldflags = linker_flags + (extra_flags or []) if c_compiler.type == "clang-cl": base_linker_cmd = [link] + ldflags + [f"-OUT:{outname}"] else: base_linker_cmd = [c_compiler.compiler] + cflags + ldflags + ["-o", outname] # -filelist is for the OS X linker. We need to try -filelist # first because clang understands @file, but may pass an # oversized argument list to the linker depending on the # contents of @file. options = [ ("linkerlist", f"-Wl,@{listname}"), ("filelist", f"-Wl,@{listname}"), ("list", f"@{listname}"), ] for kind, option in options: linker_cmd = base_linker_cmd + [option] if check_cmd_output(*linker_cmd, onerror=lambda: None) is not None: return kind die("Couldn't find one that works") set_config("EXPAND_LIBS_LIST_STYLE", expand_libs_list_style) # MOZ_PROGRAM_LDFLAGS # --------------------------------- @depends(building_with_gnu_compatible_cc, target) def moz_program_ldflags(building_with_gnu_compatible_cc, target): if building_with_gnu_compatible_cc and target.kernel not in ("Darwin", "WASI"): return ["-pie"] set_config("MOZ_PROGRAM_LDFLAGS", moz_program_ldflags) @depends(host, when=depends(c_compiler)(lambda c: c.type == "clang-cl")) def moz_debug_ldflags(host): flags = ["-DEBUG"] if host.kernel != "WINNT": # %_PDB% is a special signal to emit only the PDB basename. This # avoids problems in Windows tools that don't like forward-slashes. flags.append("-PDBALTPATH:%_PDB%") return flags set_config("MOZ_DEBUG_LDFLAGS", moz_debug_ldflags) # Objective-C/ Objective-C++ flags. # --------------------------------- @depends(compilation_flags, toolkit) def add_cmflags(compilation_flags, toolkit): cmflags = ["-x", "objective-c", "-fobjc-exceptions"] cmmflags = ["-x", "objective-c++", "-fobjc-exceptions"] compilation_flags.cmflags.extend(cmflags) compilation_flags.cmmflags.extend(cmmflags) if toolkit == "uikit": uiflags = ["-fobjc-abi-version=2", "-fobjc-legacy-dispatch"] compilation_flags.cmflags.extend(uiflags) compilation_flags.cmmflags.extend(uiflags) set_config("OS_COMPILE_CMFLAGS", compilation_flags.cmflags) set_config("OS_COMPILE_CMMFLAGS", compilation_flags.cmmflags) # Final flags settings # --------------------------------- @depends(host_c_compiler) def host_optimize_flags(compiler): if compiler.type == "clang-cl": return ["-O2"] else: return ["-O3"] set_config("HOST_OPTIMIZE_FLAGS", host_optimize_flags) @depends("HOST_CPPFLAGS", host, host_c_compiler) @imports(_from="mozbuild.shellutil", _import="split") def host_cppflags(base_cppflags, host, compiler): flags = [] if host.kernel == "WINNT": if compiler.type != "clang-cl": flags += ["-mwindows"] flags += ["-DXP_WIN", "-DWIN32", "-D_WIN32", "-D_CRT_SECURE_NO_WARNINGS"] if host.cpu == "x86_64": flags += ["-D_AMD64_"] elif host.kernel == "Darwin": flags += ["-DXP_UNIX", "-DXP_MACOSX"] else: flags += ["-DXP_UNIX"] if base_cppflags: flags += split(base_cppflags[0]) return flags @depends("HOST_CFLAGS", compilation_flags) @imports(_from="mozbuild.shellutil", _import="split") def host_cflags(base_cflags, compilation_flags): flags = list(compilation_flags.host_cflags) if base_cflags: flags += split(base_cflags[0]) return flags @depends("HOST_CXXFLAGS", compilation_flags) @imports(_from="mozbuild.shellutil", _import="split") def host_cxxflags(base_cxxflags, compilation_flags): flags = list(compilation_flags.host_cxxflags) if base_cxxflags: flags += split(base_cxxflags[0]) return flags @depends("HOST_LDFLAGS", linker_flags, host_linker_ldflags, host, host_c_compiler) @imports(_from="mozbuild.shellutil", _import="split") def host_ldflags(env_ldflags, linker_flags, host_linker_ldflags, host, compiler): flags = [] if env_ldflags: flags += split(env_ldflags[0]) flags += host_linker_ldflags if host.kernel == "WINNT" and compiler.type == "clang-cl": if host.cpu == "x86": flags += ["-MACHINE:X86"] elif host.cpu == "x86_64": flags += ["-MACHINE:X64"] flags += linker_flags.host_ldflags return flags @depends("CPPFLAGS") @imports(_from="mozbuild.shellutil", _import="split") def os_cppflags(env_cppflags): flags = [] if env_cppflags: flags = split(env_cppflags[0]) return flags @depends("CFLAGS", compilation_flags, android_flags, all_arm_flags) @imports(_from="mozbuild.shellutil", _import="split") def os_cflags(env_cflags, compilation_flags, android_flags, all_arm_flags): flags = [] if android_flags: flags.extend(android_flags.cflags) if all_arm_flags: flags.extend(all_arm_flags) flags.extend(compilation_flags.cflags) if env_cflags: flags.extend(split(env_cflags[0])) return flags @depends("CXXFLAGS", compilation_flags, android_flags, all_arm_flags) @imports(_from="mozbuild.shellutil", _import="split") def os_cxxflags(env_cxxflags, compilation_flags, android_flags, all_arm_flags): flags = [] if android_flags: flags.extend(android_flags.cxxflags) if all_arm_flags: flags.extend(all_arm_flags) flags.extend(compilation_flags.cxxflags) if env_cxxflags: flags.extend(split(env_cxxflags[0])) return flags @depends( "ASFLAGS", asm_flags, android_flags, all_arm_flags, defines_cpp_flags, c_compiler, build_project, ) @imports(_from="mozbuild.shellutil", _import="split") def os_asflags( env_asflags, asm_flags, android_flags, all_arm_flags, defines_cpp_flags, c_compiler, build_project, ): flags = [] if android_flags: flags.extend(android_flags.asflags) flags.append("-DANDROID") if all_arm_flags: flags.extend(all_arm_flags) flags.extend(asm_flags.asflags) if build_project != "js" and c_compiler.type != "clang-cl": flags.extend(defines_cpp_flags) if env_asflags: flags.extend(split(env_asflags[0])) return flags # VS2012+ defaults to -arch:SSE2. We want to target nothing more recent, so set # that explicitly here unless another target arch has already been set. @template def add_sse2_flag_if_needed(flags): @depends( flags, when=~building_with_gnu_compatible_cc & depends(target.cpu)(lambda cpu: cpu == "x86"), ) def add_sse2_flag(flags): is_arch_flag = lambda flag: flag.lower().startswith(("-arch:", "/arch:")) if not any(is_arch_flag(flag) for flag in flags): flags.append("-arch:SSE2") add_sse2_flag_if_needed(os_cflags) add_sse2_flag_if_needed(os_cxxflags) @depends(compilation_flags, when=depends(target.os)(lambda os: os == "WASI")) def add_thread_model_single(compilation_flags): compilation_flags.cxxflags.extend(["-mthread-model", "single"]) # Please keep these last in this file. set_config("HOST_CPPFLAGS", host_cppflags) set_config("HOST_CFLAGS", host_cflags) set_config("HOST_CXXFLAGS", host_cxxflags) set_config("HOST_LDFLAGS", host_ldflags) set_config("OS_CPPFLAGS", os_cppflags) set_config("OS_CFLAGS", os_cflags) set_config("OS_CXXFLAGS", os_cxxflags) set_config("ASFLAGS", os_asflags) set_config("DSO_LDOPTS", dso_flags.ldopts) set_config("LIBS", depends("LIBS")(lambda x: x))