Source code

Revision control

Copy as Markdown

Other Tools

# -*- 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/.
@depends(build_project, "--enable-smoosh")
def cbindgen_is_needed(build_project, js_enable_smoosh):
if build_project != "js":
# cbindgen is needed by the style system build and webrender.
return True
# cbindgen is needed by SmooshMonkey.
return js_enable_smoosh
option(env="CBINDGEN", nargs=1, when=cbindgen_is_needed, 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.24.3")
# 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", when=cbindgen_is_needed)(lambda x: x)
bootstrap_cbindgen = depends(cbindgen_is_needed, has_cbindgen_input)(
lambda n, i: n and not i
)
@depends_if(
"CBINDGEN",
bootstrap_search_path("cbindgen", when=bootstrap_cbindgen),
rust_search_path,
when=cbindgen_is_needed,
)
@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",
c_compiler,
cxx_compiler,
clang_search_path,
target,
target_sysroot.path,
)
@checking("for clang for bindgen", lambda x: x.path if x else "not found")
def bindgen_clang_compiler(
clang_path, c_compiler, cxx_compiler, clang_search_path, target, sysroot_path
):
# 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, allow_msvc=True)
flags = []
if sysroot_path:
flags.extend(("--sysroot", sysroot_path))
info = check_compiler([clang_path] + flags, "C++", target)
# 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([clang_path] + flags, "C++", target)
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,
target_is_unix,
cxx_compiler,
bindgen_cflags_android,
bindgen_config_paths.clang_flags,
)
def basic_bindgen_cflags(target, is_unix, compiler_info, android_cflags, clang_flags):
args = [
"-x",
"c++",
"-fno-sized-deallocation",
"-fno-aligned-new",
"-DTRACING=1",
"-DIMPL_LIBXUL",
"-DMOZILLA_INTERNAL_API",
"-DRUST_BINDGEN",
]
if is_unix:
args += ["-DOS_POSIX=1"]
if target.os == "Android":
args += android_cflags
args += {
"Android": ["-DOS_ANDROID=1"],
"DragonFly": ["-DOS_BSD=1", "-DOS_DRAGONFLY=1"],
"FreeBSD": ["-DOS_BSD=1", "-DOS_FREEBSD=1"],
"GNU": ["-DOS_LINUX=1"],
"NetBSD": ["-DOS_BSD=1", "-DOS_NETBSD=1"],
"OpenBSD": ["-DOS_BSD=1", "-DOS_OPENBSD=1"],
"OSX": ["-DOS_MACOSX=1"],
"SunOS": ["-DOS_SOLARIS=1"],
"WINNT": [
"-DOS_WIN=1",
"-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 [])
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)