зеркало из https://github.com/mozilla/gecko-dev.git
314 строки
12 KiB
Python
314 строки
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/.
|
|
|
|
|
|
# Rust is required by `rust_compiler` below. We allow_missing here
|
|
# to propagate failures to the better error message there.
|
|
js_option(env='RUSTC', nargs=1, help='Path to the rust compiler')
|
|
js_option(env='CARGO', nargs=1, help='Path to the Cargo package manager')
|
|
|
|
rustc = check_prog('RUSTC', ['rustc'], paths=toolchain_search_path,
|
|
input='RUSTC', allow_missing=True)
|
|
cargo = check_prog('CARGO', ['cargo'], paths=toolchain_search_path,
|
|
input='CARGO', allow_missing=True)
|
|
|
|
|
|
@depends_if(rustc)
|
|
@checking('rustc version', lambda info: info.version)
|
|
def rustc_info(rustc):
|
|
out = check_cmd_output(rustc, '--version', '--verbose').splitlines()
|
|
info = dict((s.strip() for s in line.split(':', 1)) for line in out[1:])
|
|
return namespace(
|
|
version=Version(info.get('release', '0')),
|
|
commit=info.get('commit-hash', 'unknown'),
|
|
host=info['host'],
|
|
)
|
|
|
|
set_config('RUSTC_VERSION', depends(rustc_info)(lambda info: str(info.version)))
|
|
|
|
@depends_if(cargo)
|
|
@checking('cargo version', lambda info: info.version)
|
|
@imports('re')
|
|
def cargo_info(cargo):
|
|
out = check_cmd_output(cargo, '--version', '--verbose').splitlines()
|
|
info = dict((s.strip() for s in line.split(':', 1)) for line in out[1:])
|
|
version = info.get('release')
|
|
# Older versions of cargo didn't support --verbose, in which case, they
|
|
# only output a not-really-pleasant-to-parse output. Fortunately, they
|
|
# don't error out, so we can just try some regexp matching on the output
|
|
# we already got.
|
|
if version is None:
|
|
VERSION_FORMAT = r'^cargo (\d\.\d+\.\d+).*'
|
|
|
|
m = re.search(VERSION_FORMAT, out[0])
|
|
# Fail fast if cargo changes its output on us.
|
|
if not m:
|
|
die('Could not determine cargo version from output: %s', out)
|
|
version = m.group(1)
|
|
|
|
return namespace(
|
|
version=Version(version),
|
|
)
|
|
|
|
|
|
@depends(rustc_info, cargo_info, build_project)
|
|
@imports(_from='textwrap', _import='dedent')
|
|
def rust_compiler(rustc_info, cargo_info, build_project):
|
|
if not rustc_info:
|
|
die(dedent('''\
|
|
Rust compiler not found.
|
|
To compile rust language sources, you must have 'rustc' in your path.
|
|
See https://www.rust-lang.org/ for more information.
|
|
|
|
You can install rust by running './mach bootstrap'
|
|
or by directly running the installer from https://rustup.rs/
|
|
'''))
|
|
if build_project == 'tools/crashreporter':
|
|
rustc_min_version = Version('1.22.0')
|
|
cargo_min_version = Version('0.23.0')
|
|
else:
|
|
rustc_min_version = Version('1.32.0')
|
|
cargo_min_version = rustc_min_version
|
|
|
|
version = rustc_info.version
|
|
if version < rustc_min_version:
|
|
die(dedent('''\
|
|
Rust compiler {} is too old.
|
|
|
|
To compile Rust language sources please install at least
|
|
version {} of the 'rustc' toolchain and make sure it is
|
|
first in your path.
|
|
|
|
You can verify this by typing 'rustc --version'.
|
|
|
|
If you have the 'rustup' tool installed you can upgrade
|
|
to the latest release by typing 'rustup update'. The
|
|
installer is available from https://rustup.rs/
|
|
'''.format(version, rustc_min_version)))
|
|
|
|
if not cargo_info:
|
|
die(dedent('''\
|
|
Cargo package manager not found.
|
|
To compile Rust language sources, you must have 'cargo' in your path.
|
|
See https://www.rust-lang.org/ for more information.
|
|
|
|
You can install cargo by running './mach bootstrap'
|
|
or by directly running the installer from https://rustup.rs/
|
|
'''))
|
|
|
|
version = cargo_info.version
|
|
if version < cargo_min_version:
|
|
die(dedent('''\
|
|
Cargo package manager {} is too old.
|
|
|
|
To compile Rust language sources please install at least
|
|
version {} of 'cargo' and make sure it is first in your path.
|
|
|
|
You can verify this by typing 'cargo --version'.
|
|
''').format(version, cargo_min_version))
|
|
|
|
return True
|
|
|
|
|
|
@depends(rustc, when=rust_compiler)
|
|
def rust_supported_targets(rustc):
|
|
out = check_cmd_output(rustc, '--print', 'target-list').splitlines()
|
|
# The os in the triplets used by rust may match the same OSes, in which
|
|
# case we need to check the raw_os instead.
|
|
per_os = {}
|
|
ambiguous = set()
|
|
per_raw_os = {}
|
|
for t in out:
|
|
t = split_triplet(t, allow_unknown=True)
|
|
endianness = t.endianness
|
|
if t.cpu.startswith('thumb') and endianness not in ('big', 'little'):
|
|
endianness = 'little'
|
|
key = (t.cpu, endianness, t.os)
|
|
if key in per_os:
|
|
previous = per_os[key]
|
|
per_raw_os[(previous.cpu, previous.endianness,
|
|
previous.raw_os)] = previous
|
|
del per_os[key]
|
|
ambiguous.add(key)
|
|
if key in ambiguous:
|
|
raw_os = t.raw_os
|
|
# split_triplet will return a raw_os of 'androideabi' for
|
|
# rust targets in the form cpu-linux-androideabi, but what
|
|
# we get from the build system is linux-androideabi, so
|
|
# normalize.
|
|
if raw_os == 'androideabi':
|
|
raw_os = 'linux-androideabi'
|
|
per_raw_os[(t.cpu, endianness, raw_os)] = t
|
|
else:
|
|
per_os[key] = t
|
|
return namespace(per_os=per_os, per_raw_os=per_raw_os)
|
|
|
|
|
|
@template
|
|
def rust_triple_alias(host_or_target):
|
|
"""Template defining the alias used for rustc's --target flag.
|
|
`host_or_target` is either `host` or `target` (the @depends functions
|
|
from init.configure).
|
|
"""
|
|
assert host_or_target in {host, target}
|
|
|
|
host_or_target_str = {host: 'host', target: 'target'}[host_or_target]
|
|
|
|
@depends(rustc, host_or_target, c_compiler, rust_supported_targets,
|
|
arm_target, when=rust_compiler)
|
|
@checking('for rust %s triplet' % host_or_target_str)
|
|
@imports('os')
|
|
@imports('subprocess')
|
|
@imports(_from='mozbuild.configure.util', _import='LineIO')
|
|
@imports(_from='mozbuild.shellutil', _import='quote')
|
|
@imports(_from='tempfile', _import='mkstemp')
|
|
@imports(_from='textwrap', _import='dedent')
|
|
def rust_target(rustc, host_or_target, compiler_info,
|
|
rust_supported_targets, arm_target):
|
|
# Rust's --target options are similar to, but not exactly the same
|
|
# as, the autoconf-derived targets we use. An example would be that
|
|
# Rust uses distinct target triples for targetting the GNU C++ ABI
|
|
# and the MSVC C++ ABI on Win32, whereas autoconf has a single
|
|
# triple and relies on the user to ensure that everything is
|
|
# compiled for the appropriate ABI. We need to perform appropriate
|
|
# munging to get the correct option to rustc.
|
|
# We correlate the autoconf-derived targets with the list of targets
|
|
# rustc gives us with --print target-list.
|
|
if host_or_target.kernel == 'WINNT':
|
|
if compiler_info.type in ('gcc', 'clang'):
|
|
host_or_target_os = 'windows-gnu'
|
|
else:
|
|
host_or_target_os = 'windows-msvc'
|
|
host_or_target_raw_os = host_or_target_os
|
|
else:
|
|
host_or_target_os = host_or_target.os
|
|
host_or_target_raw_os = host_or_target.raw_os
|
|
|
|
if host_or_target.cpu == 'arm' and arm_target.arm_arch == 7 and \
|
|
arm_target.fpu == 'neon' and arm_target.thumb2:
|
|
host_or_target_cpus = ('thumbv7neon', host_or_target.cpu)
|
|
else:
|
|
host_or_target_cpus = (host_or_target.cpu,)
|
|
|
|
for host_or_target_cpu in host_or_target_cpus:
|
|
rustc_target = rust_supported_targets.per_os.get(
|
|
(host_or_target_cpu, host_or_target.endianness, host_or_target_os))
|
|
if rustc_target:
|
|
break
|
|
|
|
rustc_target = rust_supported_targets.per_raw_os.get(
|
|
(host_or_target_cpu, host_or_target.endianness,
|
|
host_or_target_raw_os))
|
|
if rustc_target:
|
|
break
|
|
|
|
if rustc_target is None:
|
|
die("Don't know how to translate {} for rustc".format(
|
|
host_or_target.alias))
|
|
|
|
# Check to see whether our rustc has a reasonably functional stdlib
|
|
# for our chosen target.
|
|
target_arg = '--target=' + rustc_target.alias
|
|
in_fd, in_path = mkstemp(prefix='conftest', suffix='.rs')
|
|
out_fd, out_path = mkstemp(prefix='conftest', suffix='.rlib')
|
|
os.close(out_fd)
|
|
try:
|
|
source = 'pub extern fn hello() { println!("Hello world"); }'
|
|
log.debug('Creating `%s` with content:', in_path)
|
|
with LineIO(lambda l: log.debug('| %s', l)) as out:
|
|
out.write(source)
|
|
|
|
os.write(in_fd, source)
|
|
os.close(in_fd)
|
|
|
|
cmd = [
|
|
rustc,
|
|
'--crate-type', 'staticlib',
|
|
target_arg,
|
|
'-o', out_path,
|
|
in_path,
|
|
]
|
|
|
|
def failed():
|
|
die(dedent('''\
|
|
Cannot compile for {} with {}
|
|
The target may be unsupported, or you may not have
|
|
a rust std library for that target installed. Try:
|
|
|
|
rustup target add {}
|
|
'''.format(host_or_target.alias, rustc, rustc_target.alias)))
|
|
check_cmd_output(*cmd, onerror=failed)
|
|
if not os.path.exists(out_path) or os.path.getsize(out_path) == 0:
|
|
failed()
|
|
finally:
|
|
os.remove(in_path)
|
|
os.remove(out_path)
|
|
|
|
# This target is usable.
|
|
return rustc_target.alias
|
|
|
|
return rust_target
|
|
|
|
|
|
rust_target_triple = rust_triple_alias(target)
|
|
rust_host_triple = rust_triple_alias(host)
|
|
|
|
|
|
@depends(host, rust_host_triple, rustc_info.host)
|
|
def validate_rust_host_triple(host, rust_host, rustc_host):
|
|
if rust_host != rustc_host:
|
|
if host.alias == rust_host:
|
|
configure_host = host.alias
|
|
else:
|
|
configure_host = '{}/{}'.format(host.alias, rust_host)
|
|
die("The rust compiler host ({}) is not suitable for the configure host ({})."
|
|
.format(rustc_host, configure_host))
|
|
|
|
|
|
set_config('RUST_TARGET', rust_target_triple)
|
|
set_config('RUST_HOST_TARGET', rust_host_triple)
|
|
|
|
|
|
# This is used for putting source info into symbol files.
|
|
set_config('RUSTC_COMMIT', depends(rustc_info)(lambda i: i.commit))
|
|
|
|
# Rustdoc is required by Rust tests below.
|
|
js_option(env='RUSTDOC', nargs=1, help='Path to the rustdoc program')
|
|
|
|
rustdoc = check_prog('RUSTDOC', ['rustdoc'], paths=toolchain_search_path,
|
|
input='RUSTDOC', allow_missing=True)
|
|
|
|
# This option is separate from --enable-tests because Rust tests are particularly
|
|
# expensive in terms of compile time (especially for code in libxul).
|
|
option('--enable-rust-tests',
|
|
help='Enable building and running of Rust tests during `make check`')
|
|
|
|
|
|
@depends('--enable-rust-tests', rustdoc)
|
|
def rust_tests(enable_rust_tests, rustdoc):
|
|
if enable_rust_tests and not rustdoc:
|
|
die('--enable-rust-tests requires rustdoc')
|
|
return bool(enable_rust_tests)
|
|
|
|
|
|
set_config('MOZ_RUST_TESTS', rust_tests)
|
|
|
|
|
|
@depends(target, c_compiler, rustc)
|
|
@imports('os')
|
|
def rustc_natvis_ldflags(target, compiler_info, rustc):
|
|
if target.kernel == 'WINNT' and compiler_info.type == 'clang-cl':
|
|
sysroot = check_cmd_output(rustc, '--print', 'sysroot').strip()
|
|
etc = os.path.join(sysroot, 'lib/rustlib/etc')
|
|
ldflags = []
|
|
for f in os.listdir(etc):
|
|
if f.endswith('.natvis'):
|
|
ldflags.append('-NATVIS:' + normsep(os.path.join(etc, f)))
|
|
return ldflags
|
|
|
|
|
|
set_config('RUSTC_NATVIS_LDFLAGS', rustc_natvis_ldflags)
|