diff options
Diffstat (limited to '')
-rw-r--r-- | build/moz.configure/bindgen.configure | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/build/moz.configure/bindgen.configure b/build/moz.configure/bindgen.configure new file mode 100644 index 0000000000..72f4d4173b --- /dev/null +++ b/build/moz.configure/bindgen.configure @@ -0,0 +1,377 @@ +# -*- Mode: python; 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/. + + +option(env="CBINDGEN", nargs=1, help="Path to cbindgen") + + +@imports(_from="textwrap", _import="dedent") +def check_cbindgen_version(cbindgen, fatal=False): + log.debug("trying cbindgen: %s" % cbindgen) + + cbindgen_min_version = Version("0.26.0") + + # cbindgen x.y.z + version = Version(check_cmd_output(cbindgen, "--version").strip().split(" ")[1]) + log.debug("%s has version %s" % (cbindgen, version)) + if version >= cbindgen_min_version: + return True + if not fatal: + return False + + die( + dedent( + """\ + cbindgen version {} is too old. At least version {} is required. + + Please update using 'cargo install cbindgen --force' or running + './mach bootstrap', after removing the existing executable located at + {}. + """.format( + version, cbindgen_min_version, cbindgen + ) + ) + ) + + +# Similar behavior to what check_prog does. +has_cbindgen_input = depends("CBINDGEN")(lambda x: x) +bootstrap_cbindgen = depends(has_cbindgen_input)(lambda i: not i) + + +@depends_if( + "CBINDGEN", + bootstrap_search_path("cbindgen", when=bootstrap_cbindgen), + rust_search_path, +) +@checking("for cbindgen") +@imports(_from="textwrap", _import="dedent") +def cbindgen(cbindgen_override, bootstrap_search_path, rust_search_path): + if cbindgen_override: + check_cbindgen_version(cbindgen_override[0], fatal=True) + return cbindgen_override[0] + + candidates = [] + for path in bootstrap_search_path + rust_search_path: + candidate = find_program("cbindgen", [path]) + if not candidate: + continue + if check_cbindgen_version(candidate): + return candidate + candidates.append(candidate) + + if not candidates: + raise FatalCheckError( + dedent( + """\ + Cannot find cbindgen. Please run `mach bootstrap`, + `cargo install cbindgen`, ensure that `cbindgen` is on your PATH, + or point at an executable with `CBINDGEN`. + """ + ) + ) + check_cbindgen_version(candidates[0], fatal=True) + + +set_config("CBINDGEN", cbindgen) + +# Bindgen can use rustfmt to format Rust file, but it's not required. +option(env="RUSTFMT", nargs=1, help="Path to the rustfmt program") + +rustfmt = check_prog( + "RUSTFMT", + ["rustfmt"], + paths=rust_search_path, + input="RUSTFMT", + allow_missing=True, +) + + +option( + "--with-libclang-path", + nargs=1, + help="Absolute path to a directory containing Clang/LLVM libraries for bindgen (version 3.9.x or above)", +) +option( + "--with-clang-path", + nargs=1, + help="Absolute path to a Clang binary for bindgen (version 3.9.x or above)", +) + + +@depends( + "--with-clang-path", + configure_cache, + c_compiler, + cxx_compiler, + clang_search_path, + target, + target_sysroot.path, + android_version, +) +@checking("for clang for bindgen", lambda x: x.path if x else "not found") +def bindgen_clang_compiler( + clang_path, + configure_cache, + c_compiler, + cxx_compiler, + clang_search_path, + target, + sysroot_path, + android_version, +): + # When the target compiler is clang, use that, including flags. + if cxx_compiler.type == "clang": + if clang_path and clang_path[0] not in ( + c_compiler.compiler, + cxx_compiler.compiler, + ): + die( + "--with-clang-path is not valid when the target compiler is %s", + cxx_compiler.type, + ) + return namespace( + path=cxx_compiler.compiler, + flags=cxx_compiler.flags, + ) + # When the target compiler is clang-cl, use clang in the same directory, + # and figure the right flags to use. + if cxx_compiler.type == "clang-cl": + if clang_path and os.path.dirname(clang_path[0]) != os.path.dirname( + cxx_compiler.compiler + ): + die( + "--with-clang-path must point to clang in the same directory " + "as the target compiler" + ) + if not clang_path: + clang_path = [os.path.join(os.path.dirname(cxx_compiler.compiler), "clang")] + + clang_path = find_program( + clang_path[0] if clang_path else "clang++", clang_search_path + ) + if not clang_path: + return + # Hack before bug 1617793: if the compiler is clang-cl, hack the target + if cxx_compiler.type == "clang-cl": + target = split_triplet("%s-pc-windows-msvc" % target.raw_cpu) + flags = [] + if sysroot_path: + flags.extend(("--sysroot", sysroot_path)) + info = check_compiler( + configure_cache, [clang_path] + flags, "C++", target, android_version + ) + # Usually, one check_compiler pass would be enough, but when cross-compiling + # and the host and target don't use the same default C++ standard, we don't + # get the --std flag, so try again. This is the same thing as valid_compiler() + # does in toolchain.configure. + if info.flags: + flags += info.flags + info = check_compiler( + configure_cache, [clang_path] + flags, "C++", target, android_version + ) + return namespace( + path=clang_path, + flags=flags + info.flags, + ) + + +@depends("--with-libclang-path", bindgen_clang_compiler, host_library_name_info, host) +@checking("for libclang for bindgen", lambda x: x if x else "not found") +@imports("glob") +@imports(_from="os", _import="pathsep") +@imports(_from="os.path", _import="split", _as="pathsplit") +@imports("re") +def bindgen_libclang_path(libclang_path, clang, library_name_info, host): + if not clang: + if libclang_path: + die( + "--with-libclang-path is not valid without a clang compiler " + "for bindgen" + ) + return + + # Try to ensure that the clang shared library that bindgen is going + # to look for is actually present. The files that we search for + # mirror the logic in clang-sys/build.rs. + libclang_choices = [] + if host.os == "WINNT": + libclang_choices.append("libclang.dll") + libclang_choices.append( + "%sclang%s" % (library_name_info.dll.prefix, library_name_info.dll.suffix) + ) + if host.kernel == "Linux": + libclang_choices.append("libclang.so.*") + + if host.os == "OpenBSD": + libclang_choices.append("libclang.so.*.*") + + candidates = [] + if not libclang_path: + # Try to find libclang_path based on clang search dirs. + clang_search_dirs = check_cmd_output(clang.path, "-print-search-dirs") + for line in clang_search_dirs.splitlines(): + name, _, value = line.partition(": =") + if host.os == "WINNT" and name == "programs": + # On Windows, libclang.dll is in bin/ rather than lib/, + # so scan the programs search dirs. + # To make matters complicated, clang before version 9 uses `:` + # separate between paths (and `;` in newer versions) + if pathsep in value: + candidates.extend(value.split(pathsep)) + else: + for part in value.split(":"): + # Assume that if previous "candidate" was of length 1, + # it's a drive letter and the current part is the rest of + # the corresponding full path. + if candidates and len(candidates[-1]) == 1: + candidates[-1] += ":" + part + else: + candidates.append(part) + elif host.os != "WINNT" and name == "libraries": + # On other platforms, use the directories from the libraries + # search dirs that looks like $something/clang/$version. + for dir in value.split(pathsep): + dir, version = pathsplit(dir) + if re.match(r"[0-9.]+", version): + dir, name = pathsplit(dir) + if name == "clang": + candidates.append(dir) + else: + candidates.append(libclang_path[0]) + + for dir in candidates: + for pattern in libclang_choices: + log.debug('Trying "%s" in "%s"', pattern, dir) + libs = glob.glob(os.path.join(dir, pattern)) + if libs: + return libs[0] + + +@depends(bindgen_clang_compiler, bindgen_libclang_path, build_project) +def bindgen_config_paths(clang, libclang, build_project): + # XXX: we want this code to be run for both Gecko and JS, but we don't + # necessarily want to force a bindgen/Rust dependency on JS just yet. + # Actually, we don't want to force an error if we're not building the + # browser generally. We therefore whitelist the projects that require + # bindgen facilities at this point and leave it at that. + if build_project in ("browser", "mobile/android"): + if not clang: + die( + "Could not find clang to generate run bindings for C/C++. " + "Please install the necessary packages, run `mach bootstrap`, " + "or use --with-clang-path to give the location of clang." + ) + + if not libclang: + die( + "Could not find libclang to generate rust bindings for C/C++. " + "Please install the necessary packages, run `mach bootstrap`, " + "or use --with-libclang-path to give the path containing it." + ) + + if clang and libclang: + return namespace( + libclang=libclang, + libclang_path=os.path.dirname(libclang), + clang_path=clang.path, + clang_flags=clang.flags, + ) + + +@depends(bindgen_config_paths.libclang, when=bindgen_config_paths) +@checking("that libclang is new enough", lambda s: "yes" if s else "no") +@imports(_from="ctypes", _import="CDLL") +@imports(_from="textwrap", _import="dedent") +def min_libclang_version(libclang): + try: + lib = CDLL(libclang) + # We want at least 5.0. The API we test below is enough for that. + # Just accessing it should throw if not found. + fun = lib.clang_getAddressSpace + return True + except: + die( + dedent( + """\ + The libclang located at {} is too old (need at least 5.0). + + Please make sure to update it or point to a newer libclang using + --with-libclang-path. + """.format( + libclang + ) + ) + ) + return False + + +set_config("MOZ_LIBCLANG_PATH", bindgen_config_paths.libclang_path) +set_config("MOZ_CLANG_PATH", bindgen_config_paths.clang_path) + + +@depends( + target, + cxx_compiler, + bindgen_cflags_android, + bindgen_config_paths.clang_flags, + all_clang_arm_flags, +) +def basic_bindgen_cflags( + target, compiler_info, android_cflags, clang_flags, all_arm_flags +): + args = [ + "-x", + "c++", + "-fno-sized-deallocation", + "-fno-aligned-new", + "-DTRACING=1", + "-DIMPL_LIBXUL", + "-DMOZILLA_INTERNAL_API", + "-DRUST_BINDGEN", + ] + + if target.os == "Android": + args += android_cflags + + args += { + "WINNT": [ + "-DWIN32=1", + ], + }.get(target.os, []) + + if compiler_info.type == "clang-cl": + args += [ + # To enable the builtin __builtin_offsetof so that CRT wouldn't + # use reinterpret_cast in offsetof() which is not allowed inside + # static_assert(). + "-D_CRT_USE_BUILTIN_OFFSETOF", + # Enable hidden attribute (which is not supported by MSVC and + # thus not enabled by default with a MSVC-compatibile build) + # to exclude hidden symbols from the generated file. + "-DHAVE_VISIBILITY_HIDDEN_ATTRIBUTE=1", + ] + + return args + (clang_flags or []) + (all_arm_flags or []) + + +option( + env="BINDGEN_CFLAGS", + nargs=1, + help="Options bindgen should pass to the C/C++ parser", +) + + +@depends(basic_bindgen_cflags, "BINDGEN_CFLAGS") +@checking("bindgen cflags", lambda s: s if s else "no") +def bindgen_cflags(base_flags, extra_flags): + flags = base_flags + if extra_flags and len(extra_flags): + flags += extra_flags[0].split() + return " ".join(flags) + + +set_config("BINDGEN_SYSTEM_FLAGS", bindgen_cflags) |