author Ted Mielczarek <>
Mon, 16 May 2016 15:27:37 -0400
changeset 343046 4b555450a4d80e208e33342620d27294ee900d59
child 346136 3ec6380fca8207fa199693206a3ca24975bf9833
permissions -rw-r--r--
bug 1266368 - move rust.m4 to configure. r=glandium MozReview-Commit-ID: 9ol2nMYM0a0

# -*- Mode: python; c-basic-offset: 4; 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

option('--enable-rust', help='Include Rust language sources')

def rust_compiler_names(value):
    if value:
        return ['rustc']

rustc = check_prog('RUSTC', rust_compiler_names, allow_missing=True)

@checking('rustc version')
def rustc_version(rustc):
        # TODO: We should run `rustc --version -v` and parse that output instead.
        version = Version(subprocess.check_output(
            [rustc, '--version']
        return version
    except subprocess.CalledProcessError as e:
        die('Failed to get rustc version: %s', e.message)

@depends('--enable-rust', rustc, rustc_version)
@imports(_from='textwrap', _import='dedent')
def rust_compiler(value, rustc, rustc_version):
    if value:
        if not rustc:
            Rust compiler not found.
            To compile rust language sources, you must have 'rustc' in your path.
            See for more information.
        if rustc_version < '1.5':
            Rust compiler {} is too old.
            To compile Rust language sources please install at least
            version 1.5 of the 'rustc' toolchain and make sure it is
            first in your path.
            You can verify this by typing 'rustc --version'.
        return True

set_config('MOZ_RUST', rust_compiler)

@depends(rust_compiler, rustc, target, cross_compiling)
@imports(_from='mozbuild.configure.util', _import='LineIO')
@imports(_from='mozbuild.shellutil', _import='quote')
@imports(_from='tempfile', _import='mkstemp')
def rust_target(rust_compiler, rustc, target, cross_compiling):
    if rust_compiler:
        # 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.
        # The canonical list of targets supported can be derived from:

        # Avoid having to write out os+kernel for all the platforms where
        # they don't differ.
        os_or_kernel = target.kernel if target.kernel == 'Linux' and target.os != 'Android' else target.os
        rustc_target = {
            # DragonFly
            ('x86_64', 'Dragonfly'): 'x86_64-unknown-dragonfly',
            # FreeBSD, GNU/kFreeBSD
            ('x86', 'FreeBSD'): 'i686-unknown-freebsd',
            ('x86_64', 'FreeBSD'): 'x86_64-unknown-freebsd',
            # NetBSD
            ('x86_64', 'NetBSD'): 'x86_64-unknown-netbsd',
            # OpenBSD
            ('x86_64', 'OpenBSD'): 'x86_64-unknown-openbsd',
            # Linux
            ('x86', 'Linux'): 'i686-unknown-linux-gnu',
            # Linux
            ('x86_64', 'Linux'): 'x86_64-unknown-linux-gnu',
            # OS X and iOS
            ('x86', 'OSX'): 'i686-apple-darwin',
            ('x86', 'iOS'): 'i386-apple-ios',
            ('x86_64', 'OSX'): 'x86_64-apple-darwin',
            # Android
            ('x86', 'Android'): 'i686-linux-android',
            ('arm', 'Android'): 'arm-linux-androideabi',
            # Windows
            # XXX better detection of CXX needed here, to figure out whether
            # we need i686-pc-windows-gnu instead, since mingw32 builds work.
            ('x86', 'WINNT'): 'i686-pc-windows-msvc',
            ('x86_64', 'WINNT'): 'x86_64-pc-windows-msvc',
        }.get((target.cpu, os_or_kernel), None)

        if rustc_target is None:
            if cross_compiling:
                die("Don't know how to translate {} for rustc".format(target.alias))
            # Fall back to implicit (native) target when not cross-compiling
            return None

        # Check to see whether our rustc has a reasonably functional stdlib
        # for our chosen target.
        target_arg = '--target=' + rustc_target
        in_fd, in_path = mkstemp(prefix='conftest', suffix='.rs')
        out_fd, out_path = mkstemp(prefix='conftest', suffix='.rlib')
            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:

            os.write(in_fd, source)

            cmd = [
                '--crate-type', 'staticlib',
                '-o', out_path,
            def failed():
                die('Cannot compile for {} with {}'.format(target.alias, rustc))
            check_cmd_output(*cmd, onerror=failed)
            if not os.path.exists(out_path) or os.path.getsize(out_path) == 0:
        # This target is usable.
        return target_arg

set_config('RUST_TARGET', rust_target)

# Until we remove all the other Rust checks in old-configure.
add_old_configure_assignment('MOZ_RUST', rust_compiler)
add_old_configure_assignment('RUSTC', rustc)
add_old_configure_assignment('RUST_TARGET', rust_target)