2016-07-14 19:16:42 +03:00
|
|
|
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
2016-05-16 22:27:37 +03:00
|
|
|
# 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/.
|
|
|
|
|
2017-11-27 17:57:49 +03:00
|
|
|
|
2017-09-05 23:38:18 +03:00
|
|
|
@imports(_from='os.path', _import='expanduser')
|
|
|
|
def add_rustup_path(what):
|
|
|
|
# rustup installs rustc/cargo into ~/.cargo/bin by default,
|
|
|
|
# so look there if the binaries aren't in $PATH.
|
|
|
|
return [what, os.path.join(expanduser('~/.cargo/bin'), what)]
|
|
|
|
|
2017-11-27 17:57:49 +03:00
|
|
|
|
2017-02-02 02:38:49 +03:00
|
|
|
# Rust is required by `rust_compiler` below. We allow_missing here
|
|
|
|
# to propagate failures to the better error message there.
|
2018-08-02 15:27:11 +03:00
|
|
|
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', add_rustup_path('rustc'),
|
|
|
|
input='RUSTC', allow_missing=True)
|
|
|
|
cargo = check_prog('CARGO', add_rustup_path('cargo'),
|
|
|
|
input='CARGO', allow_missing=True)
|
2016-05-16 22:27:37 +03:00
|
|
|
|
2017-10-12 16:22:59 +03:00
|
|
|
|
2016-05-16 22:27:37 +03:00
|
|
|
@depends_if(rustc)
|
2016-07-05 00:12:04 +03:00
|
|
|
@checking('rustc version', lambda info: info.version)
|
|
|
|
def rustc_info(rustc):
|
2017-02-13 18:42:33 +03:00
|
|
|
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'),
|
2017-12-14 19:20:33 +03:00
|
|
|
host=info['host'],
|
2017-02-13 18:42:33 +03:00
|
|
|
)
|
2016-05-16 22:27:37 +03:00
|
|
|
|
2017-10-12 16:22:59 +03:00
|
|
|
|
2017-02-13 18:42:33 +03:00
|
|
|
@depends_if(cargo)
|
|
|
|
@checking('cargo version', lambda info: info.version)
|
|
|
|
@imports('re')
|
|
|
|
def cargo_info(cargo):
|
2017-02-15 05:22:34 +03:00
|
|
|
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+).*'
|
2017-02-13 18:42:33 +03:00
|
|
|
|
2017-02-15 05:22:34 +03:00
|
|
|
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)
|
2017-02-13 18:42:33 +03:00
|
|
|
|
|
|
|
return namespace(
|
2017-02-15 05:22:34 +03:00
|
|
|
version=Version(version),
|
2017-02-13 18:42:33 +03:00
|
|
|
)
|
|
|
|
|
2017-10-12 16:22:59 +03:00
|
|
|
|
2017-02-25 07:11:26 +03:00
|
|
|
@depends(rustc_info, cargo_info)
|
2016-05-16 22:27:37 +03:00
|
|
|
@imports(_from='textwrap', _import='dedent')
|
2017-02-25 07:11:26 +03:00
|
|
|
def rust_compiler(rustc_info, cargo_info):
|
|
|
|
if not rustc_info:
|
2017-02-02 02:38:49 +03:00
|
|
|
die(dedent('''\
|
|
|
|
Rust compiler not found.
|
|
|
|
To compile rust language sources, you must have 'rustc' in your path.
|
2017-04-21 10:43:14 +03:00
|
|
|
See https://www.rust-lang.org/ for more information.
|
2016-12-02 04:01:55 +03:00
|
|
|
|
2017-02-02 02:38:49 +03:00
|
|
|
You can install rust by running './mach bootstrap'
|
|
|
|
or by directly running the installer from https://rustup.rs/
|
|
|
|
'''))
|
2018-09-27 01:18:25 +03:00
|
|
|
rustc_min_version = Version('1.29.0')
|
2018-06-26 12:05:23 +03:00
|
|
|
cargo_min_version = rustc_min_version
|
2017-02-13 18:42:33 +03:00
|
|
|
|
2017-02-02 02:38:49 +03:00
|
|
|
version = rustc_info.version
|
2017-02-13 18:42:33 +03:00
|
|
|
if version < rustc_min_version:
|
2017-02-02 02:38:49 +03:00
|
|
|
die(dedent('''\
|
|
|
|
Rust compiler {} is too old.
|
2016-12-02 04:01:55 +03:00
|
|
|
|
2017-02-02 02:38:49 +03:00
|
|
|
To compile Rust language sources please install at least
|
|
|
|
version {} of the 'rustc' toolchain and make sure it is
|
|
|
|
first in your path.
|
2016-12-02 04:01:55 +03:00
|
|
|
|
2017-02-02 02:38:49 +03:00
|
|
|
You can verify this by typing 'rustc --version'.
|
2016-12-02 04:01:55 +03:00
|
|
|
|
2017-02-02 02:38:49 +03:00
|
|
|
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/
|
2017-02-13 18:42:33 +03:00
|
|
|
'''.format(version, rustc_min_version)))
|
|
|
|
|
2017-02-25 07:11:26 +03:00
|
|
|
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))
|
|
|
|
|
2017-02-02 02:38:49 +03:00
|
|
|
return True
|
2016-05-16 22:27:37 +03:00
|
|
|
|
2017-10-05 10:10:45 +03:00
|
|
|
|
|
|
|
@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)
|
2017-10-13 12:33:30 +03:00
|
|
|
key = (t.cpu, t.endianness, t.os)
|
2017-10-05 10:10:45 +03:00
|
|
|
if key in per_os:
|
|
|
|
previous = per_os[key]
|
2017-10-13 12:33:30 +03:00
|
|
|
per_raw_os[(previous.cpu, previous.endianness,
|
|
|
|
previous.raw_os)] = previous
|
2017-10-05 10:10:45 +03:00
|
|
|
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'
|
2017-10-13 12:33:30 +03:00
|
|
|
per_raw_os[(t.cpu, t.endianness, raw_os)] = t
|
2017-10-05 10:10:45 +03:00
|
|
|
else:
|
|
|
|
per_os[key] = t
|
|
|
|
return namespace(per_os=per_os, per_raw_os=per_raw_os)
|
|
|
|
|
|
|
|
|
2016-11-28 19:20:39 +03:00
|
|
|
@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).
|
|
|
|
"""
|
2018-10-12 16:44:08 +03:00
|
|
|
assert host_or_target in {host, target}
|
2016-11-28 19:20:39 +03:00
|
|
|
|
2018-06-25 20:57:43 +03:00
|
|
|
@depends(rustc, host_or_target, c_compiler, rust_supported_targets,
|
2017-10-05 10:10:45 +03:00
|
|
|
when=rust_compiler)
|
2016-11-28 19:20:39 +03:00
|
|
|
@imports('os')
|
|
|
|
@imports('subprocess')
|
|
|
|
@imports(_from='mozbuild.configure.util', _import='LineIO')
|
|
|
|
@imports(_from='mozbuild.shellutil', _import='quote')
|
|
|
|
@imports(_from='tempfile', _import='mkstemp')
|
2016-11-28 21:51:59 +03:00
|
|
|
@imports(_from='textwrap', _import='dedent')
|
2018-06-25 20:57:43 +03:00
|
|
|
def rust_target(rustc, host_or_target, compiler_info,
|
2017-10-05 10:10:45 +03:00
|
|
|
rust_supported_targets):
|
2016-05-16 22:27:37 +03:00
|
|
|
# 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.
|
2017-10-05 10:10:45 +03:00
|
|
|
# We correlate the autoconf-derived targets with the list of targets
|
|
|
|
# rustc gives us with --print target-list.
|
|
|
|
if host_or_target.kernel == 'WINNT':
|
2018-06-25 20:57:43 +03:00
|
|
|
if compiler_info.type in ('gcc', 'clang'):
|
2017-10-05 10:10:45 +03:00
|
|
|
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
|
|
|
|
|
|
|
|
rustc_target = rust_supported_targets.per_os.get(
|
2017-10-13 12:33:30 +03:00
|
|
|
(host_or_target.cpu, host_or_target.endianness, host_or_target_os))
|
2017-10-05 10:10:45 +03:00
|
|
|
|
|
|
|
if rustc_target is None:
|
|
|
|
rustc_target = rust_supported_targets.per_raw_os.get(
|
2017-10-13 12:33:30 +03:00
|
|
|
(host_or_target.cpu, host_or_target.endianness,
|
|
|
|
host_or_target_raw_os))
|
2016-05-16 22:27:37 +03:00
|
|
|
|
|
|
|
if rustc_target is None:
|
2017-10-12 16:22:59 +03:00
|
|
|
die("Don't know how to translate {} for rustc".format(
|
|
|
|
host_or_target.alias))
|
2016-05-16 22:27:37 +03:00
|
|
|
|
|
|
|
# Check to see whether our rustc has a reasonably functional stdlib
|
|
|
|
# for our chosen target.
|
2017-10-05 10:10:45 +03:00
|
|
|
target_arg = '--target=' + rustc_target.alias
|
2016-05-16 22:27:37 +03:00
|
|
|
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,
|
|
|
|
]
|
2017-10-12 16:22:59 +03:00
|
|
|
|
2016-05-16 22:27:37 +03:00
|
|
|
def failed():
|
2016-11-28 21:51:59 +03:00
|
|
|
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 {}
|
2017-10-05 10:10:45 +03:00
|
|
|
'''.format(host_or_target.alias, rustc, rustc_target.alias)))
|
2016-05-16 22:27:37 +03:00
|
|
|
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)
|
2016-11-28 19:20:39 +03:00
|
|
|
|
2016-05-16 22:27:37 +03:00
|
|
|
# This target is usable.
|
2017-10-05 10:10:45 +03:00
|
|
|
return rustc_target.alias
|
2016-05-16 22:27:37 +03:00
|
|
|
|
2016-11-28 19:20:39 +03:00
|
|
|
return rust_target
|
|
|
|
|
2017-10-12 16:22:59 +03:00
|
|
|
|
2016-11-28 19:20:39 +03:00
|
|
|
rust_target_triple = rust_triple_alias(target)
|
|
|
|
rust_host_triple = rust_triple_alias(host)
|
|
|
|
|
|
|
|
set_config('RUST_TARGET', rust_target_triple)
|
|
|
|
set_config('RUST_HOST_TARGET', rust_host_triple)
|
2016-05-16 22:27:37 +03:00
|
|
|
|
2017-10-12 16:22:59 +03:00
|
|
|
|
2017-04-28 21:06:42 +03:00
|
|
|
@depends(rust_target_triple)
|
|
|
|
def rust_target_env_name(triple):
|
2017-10-12 16:22:59 +03:00
|
|
|
return triple.upper().replace('-', '_')
|
|
|
|
|
2017-04-28 21:06:42 +03:00
|
|
|
|
|
|
|
# We need this to form various Cargo environment variables, as there is no
|
|
|
|
# uppercase function in make, and we don't want to shell out just for
|
|
|
|
# converting a string to uppercase.
|
|
|
|
set_config('RUST_TARGET_ENV_NAME', rust_target_env_name)
|
|
|
|
|
2017-06-23 23:19:49 +03:00
|
|
|
# This is used for putting source info into symbol files.
|
|
|
|
set_config('RUSTC_COMMIT', depends(rustc_info)(lambda i: i.commit))
|
|
|
|
|
2016-05-16 22:27:37 +03:00
|
|
|
# Until we remove all the other Rust checks in old-configure.
|
|
|
|
add_old_configure_assignment('RUSTC', rustc)
|
2016-11-28 19:20:39 +03:00
|
|
|
add_old_configure_assignment('RUST_TARGET', rust_target_triple)
|
2017-10-02 16:21:22 +03:00
|
|
|
|
2018-05-02 13:02:04 +03:00
|
|
|
# Rustdoc is required by Rust tests below.
|
2018-08-02 15:27:11 +03:00
|
|
|
js_option(env='RUSTDOC', nargs=1, help='Path to the rustdoc program')
|
|
|
|
|
|
|
|
rustdoc = check_prog('RUSTDOC', add_rustup_path('rustdoc'),
|
|
|
|
input='RUSTDOC', allow_missing=True)
|
2018-05-02 13:02:04 +03:00
|
|
|
|
2017-10-02 16:21:22 +03:00
|
|
|
# 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 of Rust tests, and build-time execution of them')
|
|
|
|
|
2018-05-02 13:02:04 +03:00
|
|
|
|
|
|
|
@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)
|
2017-12-14 19:20:33 +03:00
|
|
|
|
2018-08-17 19:43:06 +03:00
|
|
|
# cbindgen is needed by the style system build.
|
|
|
|
cbindgen = check_prog('CBINDGEN', add_rustup_path('cbindgen'), paths=toolchain_search_path,
|
|
|
|
when=depends(build_project)
|
|
|
|
(lambda build_project: build_project != 'js'))
|
|
|
|
|
2018-08-20 18:20:42 +03:00
|
|
|
|
|
|
|
@depends_if(cbindgen)
|
|
|
|
@checking('cbindgen version')
|
|
|
|
@imports(_from='textwrap', _import='dedent')
|
|
|
|
def cbindgen_version(cbindgen):
|
2018-10-04 14:17:02 +03:00
|
|
|
cbindgen_min_version = Version('0.6.4')
|
2018-08-20 18:20:42 +03:00
|
|
|
|
|
|
|
# cbindgen x.y.z
|
|
|
|
version = Version(check_cmd_output(cbindgen, '--version').strip().split(" ")[1])
|
|
|
|
|
|
|
|
if version < cbindgen_min_version:
|
|
|
|
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)))
|
|
|
|
|
|
|
|
return version
|
|
|
|
|
|
|
|
|
2018-06-27 08:27:43 +03:00
|
|
|
# Bindgen can use rustfmt to format Rust file, but it's not required.
|
2018-08-02 15:27:11 +03:00
|
|
|
js_option(env='RUSTFMT', nargs=1, help='Path to the rustfmt program')
|
|
|
|
|
|
|
|
rustfmt = check_prog('RUSTFMT', add_rustup_path('rustfmt'),
|
|
|
|
input='RUSTFMT', allow_missing=True)
|
2018-06-27 08:27:43 +03:00
|
|
|
|
2018-08-02 15:27:11 +03:00
|
|
|
js_option(env='WIN64_LINK', nargs=1, help='Path to link.exe that targets win64')
|
|
|
|
js_option(env='WIN64_LIB', nargs=1, help='Paths to libraries for the win64 linker')
|
2017-12-14 19:20:33 +03:00
|
|
|
|
|
|
|
set_config('WIN64_LINK', depends('WIN64_LINK')(lambda x: x))
|
|
|
|
set_config('WIN64_LIB', depends('WIN64_LIB')(lambda x: x))
|
|
|
|
|
|
|
|
|
|
|
|
@depends(target, rustc_info, c_compiler, 'WIN64_LINK', 'WIN64_LIB')
|
|
|
|
def win64_cargo_linker(target, rustc_info, compiler_info, link, lib):
|
|
|
|
# When we're building a 32-bit Windows build with a 64-bit rustc, we
|
|
|
|
# need to configure the linker it will use for host binaries (build scripts)
|
|
|
|
# specially because the compiler configuration we use for the build is for
|
|
|
|
# MSVC targeting 32-bit binaries.
|
|
|
|
if target.kernel == 'WINNT' and target.cpu == 'x86' and \
|
|
|
|
compiler_info.type in ('msvc', 'clang-cl') and \
|
|
|
|
rustc_info.host == 'x86_64-pc-windows-msvc' and link and lib:
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
set_config('WIN64_CARGO_LINKER', win64_cargo_linker)
|
|
|
|
|
|
|
|
|
|
|
|
@depends(win64_cargo_linker, check_build_environment)
|
|
|
|
@imports(_from='textwrap', _import='dedent')
|
|
|
|
def win64_cargo_linker_config(linker, env):
|
|
|
|
if linker:
|
|
|
|
return dedent('''\
|
|
|
|
[target.x86_64-pc-windows-msvc]
|
|
|
|
linker = "{objdir}/build/win64/cargo-linker.bat"
|
|
|
|
'''.format(objdir=env.topobjdir))
|
|
|
|
# We want an empty string here so we don't leave the @ variable in the config file.
|
|
|
|
return ''
|
|
|
|
|
|
|
|
|
|
|
|
set_config('WIN64_CARGO_LINKER_CONFIG', win64_cargo_linker_config)
|