зеркало из https://github.com/mozilla/gecko-dev.git
298 строки
12 KiB
Python
298 строки
12 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/.
|
|
|
|
# cbindgen is needed by the style system build and webrender.
|
|
cbindgen_is_needed = depends(build_project)(lambda build_project: build_project != 'js')
|
|
|
|
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.9.1')
|
|
|
|
# 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)))
|
|
|
|
|
|
@depends_if('CBINDGEN', toolchain_search_path, when=cbindgen_is_needed)
|
|
@checking('for cbindgen')
|
|
@imports(_from='textwrap', _import='dedent')
|
|
def cbindgen(cbindgen_override, toolchain_search_path):
|
|
if cbindgen_override:
|
|
check_cbindgen_version(cbindgen_override[0], fatal=True)
|
|
return cbindgen_override[0]
|
|
|
|
candidates = []
|
|
for path in toolchain_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.
|
|
js_option(env='RUSTFMT', nargs=1, help='Path to the rustfmt program')
|
|
|
|
rustfmt = check_prog('RUSTFMT', ['rustfmt'], paths=toolchain_search_path,
|
|
input='RUSTFMT', allow_missing=True)
|
|
|
|
|
|
js_option('--with-libclang-path', nargs=1,
|
|
help='Absolute path to a directory containing Clang/LLVM libraries for bindgen (version 3.9.x or above)')
|
|
js_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, toolchain_search_path,
|
|
target, macos_sdk)
|
|
@checking('for clang for bindgen', lambda x: x.path if x else 'not found')
|
|
def bindgen_clang_compiler(clang_path, c_compiler, cxx_compiler,
|
|
toolchain_search_path, target, macos_sdk):
|
|
# 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++',
|
|
toolchain_search_path)
|
|
if not clang_path:
|
|
return
|
|
flags = prepare_flags(target, macos_sdk)
|
|
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.1')
|
|
|
|
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.encode('utf-8'))
|
|
# We want at least 4.0. The API we test below is enough for that.
|
|
# Just accessing it should throw if not found.
|
|
fun = lib.clang_EvalResult_getAsLongLong
|
|
return True
|
|
except:
|
|
die(dedent('''\
|
|
The libclang located at {} is too old (need at least 4.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', '-stdlib=libc++'],
|
|
'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 [])
|
|
|
|
|
|
js_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)
|
|
|
|
|
|
add_old_configure_assignment('_BINDGEN_CFLAGS', bindgen_cflags)
|