summaryrefslogtreecommitdiffstats
path: root/build/moz.configure/rust.configure
blob: 261768f6402ef91a488bfde10c95b23adcae5d88 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# -*- 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/.

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

@depends('--enable-rust')
def rust_compiler_names(value):
    if value:
        return ['rustc']

@depends('--enable-rust')
def cargo_binary_names(value):
    if value:
        return ['cargo']

rustc = check_prog('RUSTC', rust_compiler_names, allow_missing=True)
cargo = check_prog('CARGO', cargo_binary_names, 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'),
        )

@depends_if(cargo)
@checking('cargo support for --frozen')
@imports('subprocess')
@imports('os')
def cargo_supports_frozen(cargo):
    try:
        lines = subprocess.check_output(
            [cargo, 'help', 'build']
        ).splitlines()
        supported = any('    --frozen' in l for l in lines)
        if 'MOZ_AUTOMATION' in os.environ and not supported:
            die('cargo in automation must support --frozen')
        return supported
    except subprocess.CalledProcessError as e:
        die('Failed to call cargo: %s', e.message)

set_config('MOZ_CARGO_SUPPORTS_FROZEN', cargo_supports_frozen)

@depends('--enable-rust', rustc, rustc_info)
@imports(_from='textwrap', _import='dedent')
def rust_compiler(value, rustc, rustc_info):
    if value:
        if not rustc:
            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.
            '''))
        version = rustc_info.version
        min_version = Version('1.10')
        if version < 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'.
            '''.format(version, min_version)))
        return True

set_config('MOZ_RUST', rust_compiler)

@depends(rust_compiler, rustc, target, cross_compiling)
@imports('os')
@imports('subprocess')
@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:
        #
        # https://github.com/rust-lang/rust/tree/master/mk/cfg

        # 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
            ('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'): 'i586-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'): 'armv7-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:
            die("Don't know how to translate {} for rustc".format(target.alias))

        # 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')
        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('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:
                failed()
        finally:
            os.remove(in_path)
            os.remove(out_path)
        # This target is usable.
        return rustc_target

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)