mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 06:43:32 +00:00
3c43de2d49
Differential Revision: https://phabricator.services.mozilla.com/D174554
404 lines
13 KiB
Python
404 lines
13 KiB
Python
# -*- 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",
|
|
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,
|
|
target_is_unix,
|
|
cxx_compiler,
|
|
bindgen_cflags_android,
|
|
bindgen_config_paths.clang_flags,
|
|
all_clang_arm_flags,
|
|
)
|
|
def basic_bindgen_cflags(
|
|
target, is_unix, 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 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 []) + (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)
|