1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this
3# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5from __future__ import absolute_import, print_function, unicode_literals
6
7import logging
8import os
9import six
10from six import StringIO
11
12from mozunit import main
13
14from common import BaseConfigureTest
15from mozbuild.configure.util import Version
16from mozbuild.util import (
17    memoize,
18    ReadOnlyNamespace,
19)
20from mozpack import path as mozpath
21from test_toolchain_helpers import (
22    FakeCompiler,
23    CompilerResult,
24    PrependFlags,
25)
26
27
28DEFAULT_C99 = {
29    '__STDC_VERSION__': '199901L',
30}
31
32DEFAULT_C11 = {
33    '__STDC_VERSION__': '201112L',
34}
35
36DEFAULT_CXX_97 = {
37    '__cplusplus': '199711L',
38}
39
40DEFAULT_CXX_11 = {
41    '__cplusplus': '201103L',
42}
43
44DRAFT_CXX_14 = {
45    '__cplusplus': '201300L',
46}
47
48DEFAULT_CXX_14 = {
49    '__cplusplus': '201402L',
50}
51
52DRAFT_CXX17_201500 = {
53    '__cplusplus': '201500L',
54}
55
56DRAFT_CXX17_201406 = {
57    '__cplusplus': '201406L',
58}
59
60DEFAULT_CXX_17 = {
61    '__cplusplus': '201703L',
62}
63
64SUPPORTS_GNU99 = {
65    '-std=gnu99': DEFAULT_C99,
66}
67
68SUPPORTS_GNUXX11 = {
69    '-std=gnu++11': DEFAULT_CXX_11,
70}
71
72SUPPORTS_GNUXX14 = {
73    '-std=gnu++14': DEFAULT_CXX_14,
74}
75
76SUPPORTS_CXX14 = {
77    '-std=c++14': DEFAULT_CXX_14,
78}
79
80SUPPORTS_GNUXX17 = {
81    '-std=gnu++17': DEFAULT_CXX_17,
82}
83
84SUPPORTS_CXX17 = {
85    '-std=c++17': DEFAULT_CXX_17,
86}
87
88
89@memoize
90def GCC_BASE(version):
91    version = Version(version)
92    return FakeCompiler({
93        '__GNUC__': version.major,
94        '__GNUC_MINOR__': version.minor,
95        '__GNUC_PATCHLEVEL__': version.patch,
96        '__STDC__': 1,
97    })
98
99
100@memoize
101def GCC(version):
102    return GCC_BASE(version) + SUPPORTS_GNU99
103
104
105@memoize
106def GXX(version):
107    return GCC_BASE(version) + DEFAULT_CXX_97 + SUPPORTS_GNUXX11
108
109
110SUPPORTS_DRAFT_CXX14_VERSION = {
111    '-std=gnu++14': DRAFT_CXX_14,
112}
113
114SUPPORTS_GNUXX1Z = {
115    '-std=gnu++1z': DRAFT_CXX17_201406,
116}
117
118SUPPORTS_DRAFT_CXX17_201500_VERSION = {
119    '-std=gnu++17': DRAFT_CXX17_201500,
120}
121
122GCC_4_9 = GCC('4.9.3')
123GXX_4_9 = GXX('4.9.3') + SUPPORTS_DRAFT_CXX14_VERSION
124GCC_5 = GCC('5.2.1') + DEFAULT_C11
125GXX_5 = GXX('5.2.1') + SUPPORTS_GNUXX14
126GCC_6 = GCC('6.4.0') + DEFAULT_C11
127GXX_6 = GXX('6.4.0') + DEFAULT_CXX_14 + SUPPORTS_GNUXX17 + SUPPORTS_DRAFT_CXX17_201500_VERSION
128GCC_7 = GCC('7.3.0') + DEFAULT_C11
129GXX_7 = GXX('7.3.0') + DEFAULT_CXX_14 + SUPPORTS_GNUXX17 + SUPPORTS_CXX17
130GCC_8 = GCC('8.3.0') + DEFAULT_C11
131GXX_8 = GXX('8.3.0') + DEFAULT_CXX_14 + SUPPORTS_GNUXX17 + SUPPORTS_CXX17
132
133DEFAULT_GCC = GCC_7
134DEFAULT_GXX = GXX_7
135
136GCC_PLATFORM_LITTLE_ENDIAN = {
137    '__ORDER_LITTLE_ENDIAN__': 1234,
138    '__ORDER_BIG_ENDIAN__': 4321,
139    '__BYTE_ORDER__': 1234,
140}
141
142GCC_PLATFORM_BIG_ENDIAN = {
143    '__ORDER_LITTLE_ENDIAN__': 1234,
144    '__ORDER_BIG_ENDIAN__': 4321,
145    '__BYTE_ORDER__': 4321,
146}
147
148GCC_PLATFORM_X86 = FakeCompiler(GCC_PLATFORM_LITTLE_ENDIAN) + {
149    None: {
150        '__i386__': 1,
151    },
152    '-m64': {
153        '__i386__': False,
154        '__x86_64__': 1,
155    },
156}
157
158GCC_PLATFORM_X86_64 = FakeCompiler(GCC_PLATFORM_LITTLE_ENDIAN) + {
159    None: {
160        '__x86_64__': 1,
161    },
162    '-m32': {
163        '__x86_64__': False,
164        '__i386__': 1,
165    },
166}
167
168GCC_PLATFORM_ARM = FakeCompiler(GCC_PLATFORM_LITTLE_ENDIAN) + {
169    '__arm__': 1,
170}
171
172GCC_PLATFORM_LINUX = {
173    '__linux__': 1,
174}
175
176GCC_PLATFORM_DARWIN = {
177    '__APPLE__': 1,
178}
179
180GCC_PLATFORM_WIN = {
181    '_WIN32': 1,
182    'WINNT': 1,
183}
184
185GCC_PLATFORM_OPENBSD = {
186    '__OpenBSD__': 1,
187}
188
189GCC_PLATFORM_X86_LINUX = FakeCompiler(GCC_PLATFORM_X86, GCC_PLATFORM_LINUX)
190GCC_PLATFORM_X86_64_LINUX = FakeCompiler(GCC_PLATFORM_X86_64,
191                                         GCC_PLATFORM_LINUX)
192GCC_PLATFORM_ARM_LINUX = FakeCompiler(GCC_PLATFORM_ARM, GCC_PLATFORM_LINUX)
193GCC_PLATFORM_X86_OSX = FakeCompiler(GCC_PLATFORM_X86, GCC_PLATFORM_DARWIN)
194GCC_PLATFORM_X86_64_OSX = FakeCompiler(GCC_PLATFORM_X86_64,
195                                       GCC_PLATFORM_DARWIN)
196GCC_PLATFORM_X86_WIN = FakeCompiler(GCC_PLATFORM_X86, GCC_PLATFORM_WIN)
197GCC_PLATFORM_X86_64_WIN = FakeCompiler(GCC_PLATFORM_X86_64, GCC_PLATFORM_WIN)
198
199
200@memoize
201def CLANG_BASE(version):
202    version = Version(version)
203    return FakeCompiler({
204        '__clang__': 1,
205        '__clang_major__': version.major,
206        '__clang_minor__': version.minor,
207        '__clang_patchlevel__': version.patch,
208    })
209
210
211@memoize
212def CLANG(version):
213    return GCC_BASE('4.2.1') + CLANG_BASE(version) + SUPPORTS_GNU99
214
215
216@memoize
217def CLANGXX(version):
218    return (GCC_BASE('4.2.1') + CLANG_BASE(version) + DEFAULT_CXX_97 +
219            SUPPORTS_GNUXX11 + SUPPORTS_GNUXX14)
220
221
222CLANG_3_3 = CLANG('3.3.0') + DEFAULT_C99
223CLANGXX_3_3 = CLANGXX('3.3.0')
224CLANG_4_0 = CLANG('4.0.2') + DEFAULT_C11 + {
225    '__has_attribute(diagnose_if)': 1,
226}
227CLANGXX_4_0 = CLANGXX('4.0.2') + SUPPORTS_GNUXX1Z + {
228    '__has_attribute(diagnose_if)': 1,
229}
230CLANG_5_0 = CLANG('5.0.1') + DEFAULT_C11 + {
231    '__has_attribute(diagnose_if)': 1,
232    '__has_warning("-Wunguarded-availability")': 1,
233}
234CLANGXX_5_0 = CLANGXX('5.0.1') + SUPPORTS_GNUXX17 + {
235    '__has_attribute(diagnose_if)': 1,
236    '__has_warning("-Wunguarded-availability")': 1,
237}
238DEFAULT_CLANG = CLANG_5_0
239DEFAULT_CLANGXX = CLANGXX_5_0
240
241
242def CLANG_PLATFORM(gcc_platform):
243    base = {
244        '--target=x86_64-linux-gnu': GCC_PLATFORM_X86_64_LINUX[None],
245        '--target=x86_64-apple-darwin11.2.0': GCC_PLATFORM_X86_64_OSX[None],
246        '--target=i686-linux-gnu': GCC_PLATFORM_X86_LINUX[None],
247        '--target=i686-apple-darwin11.2.0': GCC_PLATFORM_X86_OSX[None],
248        '--target=arm-linux-gnu': GCC_PLATFORM_ARM_LINUX[None],
249    }
250    undo_gcc_platform = {
251        k: {symbol: False for symbol in gcc_platform[None]}
252        for k in base
253    }
254    return FakeCompiler(gcc_platform, undo_gcc_platform, base)
255
256
257CLANG_PLATFORM_X86_LINUX = CLANG_PLATFORM(GCC_PLATFORM_X86_LINUX)
258CLANG_PLATFORM_X86_64_LINUX = CLANG_PLATFORM(GCC_PLATFORM_X86_64_LINUX)
259CLANG_PLATFORM_X86_OSX = CLANG_PLATFORM(GCC_PLATFORM_X86_OSX)
260CLANG_PLATFORM_X86_64_OSX = CLANG_PLATFORM(GCC_PLATFORM_X86_64_OSX)
261CLANG_PLATFORM_X86_WIN = CLANG_PLATFORM(GCC_PLATFORM_X86_WIN)
262CLANG_PLATFORM_X86_64_WIN = CLANG_PLATFORM(GCC_PLATFORM_X86_64_WIN)
263
264
265@memoize
266def VS(version):
267    version = Version(version)
268    return FakeCompiler({
269        None: {
270            '_MSC_VER': '%02d%02d' % (version.major, version.minor),
271            '_MSC_FULL_VER': '%02d%02d%05d' % (version.major, version.minor,
272                                               version.patch),
273            '_MT': '1',
274        },
275        '*.cpp': DEFAULT_CXX_97,
276    })
277
278
279VS_2017u8 = VS('19.15.26726')
280
281VS_PLATFORM_X86 = {
282    '_M_IX86': 600,
283    '_WIN32': 1,
284}
285
286VS_PLATFORM_X86_64 = {
287    '_M_X64': 100,
288    '_WIN32': 1,
289    '_WIN64': 1,
290}
291
292# Note: In reality, the -std=gnu* options are only supported when preceded by
293# -Xclang.
294CLANG_CL_3_9 = (CLANG_BASE('3.9.0') + VS('18.00.00000') + DEFAULT_C11 +
295                SUPPORTS_GNU99 + SUPPORTS_GNUXX11 + SUPPORTS_CXX14) + {
296    '*.cpp': {
297        '__STDC_VERSION__': False,
298        '__cplusplus': '201103L',
299    },
300}
301CLANG_CL_8_0 = (CLANG_BASE('8.0.0') + VS('18.00.00000') + DEFAULT_C11 +
302                SUPPORTS_GNU99 + SUPPORTS_GNUXX11 + SUPPORTS_CXX14 +
303                SUPPORTS_CXX17) + {
304    '*.cpp': {
305        '__STDC_VERSION__': False,
306        '__cplusplus': '201103L',
307    },
308}
309
310CLANG_CL_PLATFORM_X86 = FakeCompiler(
311    VS_PLATFORM_X86, GCC_PLATFORM_X86[None], GCC_PLATFORM_LITTLE_ENDIAN)
312CLANG_CL_PLATFORM_X86_64 = FakeCompiler(
313    VS_PLATFORM_X86_64, GCC_PLATFORM_X86_64[None], GCC_PLATFORM_LITTLE_ENDIAN)
314
315LIBRARY_NAME_INFOS = {
316    'linux-gnu': {
317        'DLL_PREFIX': 'lib',
318        'DLL_SUFFIX': '.so',
319        'LIB_PREFIX': 'lib',
320        'LIB_SUFFIX': 'a',
321        'IMPORT_LIB_SUFFIX': '',
322        'RUST_LIB_PREFIX': 'lib',
323        'RUST_LIB_SUFFIX': 'a',
324        'OBJ_SUFFIX': 'o',
325    },
326    'darwin11.2.0': {
327        'DLL_PREFIX': 'lib',
328        'DLL_SUFFIX': '.dylib',
329        'LIB_PREFIX': 'lib',
330        'LIB_SUFFIX': 'a',
331        'IMPORT_LIB_SUFFIX': '',
332        'RUST_LIB_PREFIX': 'lib',
333        'RUST_LIB_SUFFIX': 'a',
334        'OBJ_SUFFIX': 'o',
335    },
336    'mingw32': {
337        'DLL_PREFIX': '',
338        'DLL_SUFFIX': '.dll',
339        'LIB_PREFIX': 'lib',
340        'LIB_SUFFIX': 'a',
341        'IMPORT_LIB_SUFFIX': 'a',
342        'RUST_LIB_PREFIX': '',
343        'RUST_LIB_SUFFIX': 'lib',
344        'OBJ_SUFFIX': 'o',
345    },
346    'msvc': {
347        'DLL_PREFIX': '',
348        'DLL_SUFFIX': '.dll',
349        'LIB_PREFIX': '',
350        'LIB_SUFFIX': 'lib',
351        'IMPORT_LIB_SUFFIX': 'lib',
352        'RUST_LIB_PREFIX': '',
353        'RUST_LIB_SUFFIX': 'lib',
354        'OBJ_SUFFIX': 'obj',
355    },
356    'openbsd6.1': {
357        'DLL_PREFIX': 'lib',
358        'DLL_SUFFIX': '.so.1.0',
359        'LIB_PREFIX': 'lib',
360        'LIB_SUFFIX': 'a',
361        'IMPORT_LIB_SUFFIX': '',
362        'RUST_LIB_PREFIX': 'lib',
363        'RUST_LIB_SUFFIX': 'a',
364        'OBJ_SUFFIX': 'o',
365    },
366}
367
368
369class BaseToolchainTest(BaseConfigureTest):
370    def setUp(self):
371        super(BaseToolchainTest, self).setUp()
372        self.out = StringIO()
373        self.logger = logging.getLogger('BaseToolchainTest')
374        self.logger.setLevel(logging.ERROR)
375        self.handler = logging.StreamHandler(self.out)
376        self.logger.addHandler(self.handler)
377
378    def tearDown(self):
379        self.logger.removeHandler(self.handler)
380        del self.handler
381        del self.out
382        super(BaseToolchainTest, self).tearDown()
383
384    def do_toolchain_test(self, paths, results, args=[], environ={}):
385        '''Helper to test the toolchain checks from toolchain.configure.
386
387        - `paths` is a dict associating compiler paths to FakeCompiler
388          definitions from above.
389        - `results` is a dict associating result variable names from
390          toolchain.configure (c_compiler, cxx_compiler, host_c_compiler,
391          host_cxx_compiler) with a result.
392          The result can either be an error string, or a CompilerResult
393          corresponding to the object returned by toolchain.configure checks.
394          When the results for host_c_compiler are identical to c_compiler,
395          they can be omitted. Likewise for host_cxx_compiler vs.
396          cxx_compiler.
397        '''
398        environ = dict(environ)
399        if 'PATH' not in environ:
400            environ['PATH'] = os.pathsep.join(
401                mozpath.abspath(p) for p in ('/bin', '/usr/bin'))
402
403        args = args + ['--enable-release']
404
405        sandbox = self.get_sandbox(paths, {}, args, environ,
406                                   logger=self.logger)
407
408        for var in ('c_compiler', 'cxx_compiler', 'host_c_compiler',
409                    'host_cxx_compiler'):
410            if var in results:
411                result = results[var]
412            elif var.startswith('host_'):
413                result = results.get(var[5:], {})
414            else:
415                result = {}
416            try:
417                self.out.truncate(0)
418                self.out.seek(0)
419                compiler = sandbox._value_for(sandbox[var])
420                # Add var on both ends to make it clear which of the
421                # variables is failing the test when that happens.
422                self.assertEquals((var, compiler), (var, result))
423            except SystemExit:
424                self.assertEquals((var, result),
425                                  (var, self.out.getvalue().strip()))
426                return
427
428        # Normalize the target os to match what we have as keys in
429        # LIBRARY_NAME_INFOS.
430        target_os = getattr(self, 'TARGET', self.HOST).split('-', 2)[2]
431        if target_os == 'mingw32':
432            compiler_type = sandbox._value_for(sandbox['c_compiler']).type
433            if compiler_type == 'clang-cl':
434                target_os = 'msvc'
435        elif target_os == 'linux-gnuabi64':
436            target_os = 'linux-gnu'
437
438        self.do_library_name_info_test(target_os, sandbox)
439
440        # Try again on artifact builds. In that case, we always get library
441        # name info for msvc on Windows
442        if target_os == 'mingw32':
443            target_os = 'msvc'
444
445        sandbox = self.get_sandbox(
446            paths, {}, args + ['--enable-artifact-builds'], environ,
447            logger=self.logger)
448
449        self.do_library_name_info_test(target_os, sandbox)
450
451    def do_library_name_info_test(self, target_os, sandbox):
452        library_name_info = LIBRARY_NAME_INFOS[target_os]
453        for k in (
454            'DLL_PREFIX',
455            'DLL_SUFFIX',
456            'LIB_PREFIX',
457            'LIB_SUFFIX',
458            'IMPORT_LIB_SUFFIX',
459            'RUST_LIB_PREFIX',
460            'RUST_LIB_SUFFIX',
461            'OBJ_SUFFIX',
462        ):
463            self.assertEquals('%s=%s' % (k, sandbox.get_config(k)),
464                              '%s=%s' % (k, library_name_info[k]))
465
466
467def old_gcc_message(old_ver):
468    return 'Only GCC 7.1 or newer is supported (found version {}).'.format(old_ver)
469
470
471class LinuxToolchainTest(BaseToolchainTest):
472    PATHS = {
473        '/usr/bin/gcc': DEFAULT_GCC + GCC_PLATFORM_X86_64_LINUX,
474        '/usr/bin/g++': DEFAULT_GXX + GCC_PLATFORM_X86_64_LINUX,
475        '/usr/bin/gcc-4.9': GCC_4_9 + GCC_PLATFORM_X86_64_LINUX,
476        '/usr/bin/g++-4.9': GXX_4_9 + GCC_PLATFORM_X86_64_LINUX,
477        '/usr/bin/gcc-5': GCC_5 + GCC_PLATFORM_X86_64_LINUX,
478        '/usr/bin/g++-5': GXX_5 + GCC_PLATFORM_X86_64_LINUX,
479        '/usr/bin/gcc-6': GCC_6 + GCC_PLATFORM_X86_64_LINUX,
480        '/usr/bin/g++-6': GXX_6 + GCC_PLATFORM_X86_64_LINUX,
481        '/usr/bin/gcc-7': GCC_7 + GCC_PLATFORM_X86_64_LINUX,
482        '/usr/bin/g++-7': GXX_7 + GCC_PLATFORM_X86_64_LINUX,
483        '/usr/bin/gcc-8': GCC_8 + GCC_PLATFORM_X86_64_LINUX,
484        '/usr/bin/g++-8': GXX_8 + GCC_PLATFORM_X86_64_LINUX,
485        '/usr/bin/clang': DEFAULT_CLANG + CLANG_PLATFORM_X86_64_LINUX,
486        '/usr/bin/clang++': DEFAULT_CLANGXX + CLANG_PLATFORM_X86_64_LINUX,
487        '/usr/bin/clang-5.0': CLANG_5_0 + CLANG_PLATFORM_X86_64_LINUX,
488        '/usr/bin/clang++-5.0': CLANGXX_5_0 + CLANG_PLATFORM_X86_64_LINUX,
489        '/usr/bin/clang-4.0': CLANG_4_0 + CLANG_PLATFORM_X86_64_LINUX,
490        '/usr/bin/clang++-4.0': CLANGXX_4_0 + CLANG_PLATFORM_X86_64_LINUX,
491        '/usr/bin/clang-3.3': CLANG_3_3 + CLANG_PLATFORM_X86_64_LINUX,
492        '/usr/bin/clang++-3.3': CLANGXX_3_3 + CLANG_PLATFORM_X86_64_LINUX,
493    }
494
495    GCC_4_7_RESULT = old_gcc_message('4.7.3')
496    GXX_4_7_RESULT = GCC_4_7_RESULT
497    GCC_4_9_RESULT = old_gcc_message('4.9.3')
498    GXX_4_9_RESULT = GCC_4_9_RESULT
499    GCC_5_RESULT = old_gcc_message('5.2.1')
500    GXX_5_RESULT = GCC_5_RESULT
501    GCC_6_RESULT = old_gcc_message('6.4.0')
502    GXX_6_RESULT = GCC_6_RESULT
503    GCC_7_RESULT = CompilerResult(
504        flags=['-std=gnu99'],
505        version='7.3.0',
506        type='gcc',
507        compiler='/usr/bin/gcc-7',
508        language='C',
509    )
510    GXX_7_RESULT = CompilerResult(
511        flags=['-std=gnu++17'],
512        version='7.3.0',
513        type='gcc',
514        compiler='/usr/bin/g++-7',
515        language='C++',
516    )
517    GCC_8_RESULT = CompilerResult(
518        flags=['-std=gnu99'],
519        version='8.3.0',
520        type='gcc',
521        compiler='/usr/bin/gcc-8',
522        language='C',
523    )
524    GXX_8_RESULT = CompilerResult(
525        flags=['-std=gnu++17'],
526        version='8.3.0',
527        type='gcc',
528        compiler='/usr/bin/g++-8',
529        language='C++',
530    )
531    DEFAULT_GCC_RESULT = GCC_7_RESULT + {'compiler': '/usr/bin/gcc'}
532    DEFAULT_GXX_RESULT = GXX_7_RESULT + {'compiler': '/usr/bin/g++'}
533
534    CLANG_3_3_RESULT = 'Only clang/llvm 5.0 or newer is supported.'
535    CLANGXX_3_3_RESULT = 'Only clang/llvm 5.0 or newer is supported.'
536    CLANG_4_0_RESULT = 'Only clang/llvm 5.0 or newer is supported.'
537    CLANGXX_4_0_RESULT = 'Only clang/llvm 5.0 or newer is supported.'
538    CLANG_5_0_RESULT = CompilerResult(
539        flags=['-std=gnu99'],
540        version='5.0.1',
541        type='clang',
542        compiler='/usr/bin/clang-5.0',
543        language='C',
544    )
545    CLANGXX_5_0_RESULT = CompilerResult(
546        flags=['-std=gnu++17'],
547        version='5.0.1',
548        type='clang',
549        compiler='/usr/bin/clang++-5.0',
550        language='C++',
551    )
552    DEFAULT_CLANG_RESULT = CLANG_5_0_RESULT + {'compiler': '/usr/bin/clang'}
553    DEFAULT_CLANGXX_RESULT = CLANGXX_5_0_RESULT + {'compiler': '/usr/bin/clang++'}
554
555    def test_default(self):
556        # We'll try clang and gcc, and find clang first.
557        self.do_toolchain_test(self.PATHS, {
558            'c_compiler': self.DEFAULT_CLANG_RESULT,
559            'cxx_compiler': self.DEFAULT_CLANGXX_RESULT,
560        })
561
562    def test_gcc(self):
563        self.do_toolchain_test(self.PATHS, {
564            'c_compiler': self.DEFAULT_GCC_RESULT,
565            'cxx_compiler': self.DEFAULT_GXX_RESULT,
566        }, environ={
567            'CC': 'gcc',
568            'CXX': 'g++',
569        })
570
571    def test_unsupported_gcc(self):
572        self.do_toolchain_test(self.PATHS, {
573            'c_compiler': self.GCC_4_9_RESULT,
574        }, environ={
575            'CC': 'gcc-4.9',
576            'CXX': 'g++-4.9',
577        })
578
579        # Maybe this should be reporting the mismatched version instead.
580        self.do_toolchain_test(self.PATHS, {
581            'c_compiler': self.DEFAULT_GCC_RESULT,
582            'cxx_compiler': self.GXX_4_9_RESULT,
583        }, environ={
584            'CC': 'gcc',
585            'CXX': 'g++-4.9',
586        })
587
588    def test_overridden_gcc(self):
589        self.do_toolchain_test(self.PATHS, {
590            'c_compiler': self.GCC_7_RESULT,
591            'cxx_compiler': self.GXX_7_RESULT,
592        }, environ={
593            'CC': 'gcc-7',
594            'CXX': 'g++-7',
595        })
596
597    def test_guess_cxx(self):
598        # When CXX is not set, we guess it from CC.
599        self.do_toolchain_test(self.PATHS, {
600            'c_compiler': self.GCC_7_RESULT,
601            'cxx_compiler': self.GXX_7_RESULT,
602        }, environ={
603            'CC': 'gcc-7',
604        })
605
606    def test_mismatched_gcc(self):
607        self.do_toolchain_test(self.PATHS, {
608            'c_compiler': self.DEFAULT_GCC_RESULT,
609            'cxx_compiler': (
610                'The target C compiler is version 7.3.0, while the target '
611                'C++ compiler is version 8.3.0. Need to use the same compiler '
612                'version.'),
613        }, environ={
614            'CC': 'gcc',
615            'CXX': 'g++-8',
616        })
617
618        self.do_toolchain_test(self.PATHS, {
619            'c_compiler': self.DEFAULT_GCC_RESULT,
620            'cxx_compiler': self.DEFAULT_GXX_RESULT,
621            'host_c_compiler': self.DEFAULT_GCC_RESULT,
622            'host_cxx_compiler': (
623                'The host C compiler is version 7.3.0, while the host '
624                'C++ compiler is version 8.3.0. Need to use the same compiler '
625                'version.'),
626        }, environ={
627            'CC': 'gcc',
628            'HOST_CXX': 'g++-8',
629        })
630
631    def test_mismatched_compiler(self):
632        self.do_toolchain_test(self.PATHS, {
633            'c_compiler': self.DEFAULT_CLANG_RESULT,
634            'cxx_compiler': (
635                'The target C compiler is clang, while the target C++ compiler '
636                'is gcc. Need to use the same compiler suite.'),
637        }, environ={
638            'CXX': 'g++',
639        })
640
641        self.do_toolchain_test(self.PATHS, {
642            'c_compiler': self.DEFAULT_CLANG_RESULT,
643            'cxx_compiler': self.DEFAULT_CLANGXX_RESULT,
644            'host_c_compiler': self.DEFAULT_CLANG_RESULT,
645            'host_cxx_compiler': (
646                'The host C compiler is clang, while the host C++ compiler '
647                'is gcc. Need to use the same compiler suite.'),
648        }, environ={
649            'HOST_CXX': 'g++',
650        })
651
652        self.do_toolchain_test(self.PATHS, {
653            'c_compiler': '`%s` is not a C compiler.'
654            % mozpath.abspath('/usr/bin/g++'),
655        }, environ={
656            'CC': 'g++',
657        })
658
659        self.do_toolchain_test(self.PATHS, {
660            'c_compiler': self.DEFAULT_CLANG_RESULT,
661            'cxx_compiler': '`%s` is not a C++ compiler.'
662            % mozpath.abspath('/usr/bin/clang'),
663        }, environ={
664            'CXX': 'clang',
665        })
666
667    def test_clang(self):
668        # We'll try gcc and clang, but since there is no gcc (gcc-x.y doesn't
669        # count), find clang.
670        paths = {
671            k: v for k, v in six.iteritems(self.PATHS)
672            if os.path.basename(k) not in ('gcc', 'g++')
673        }
674        self.do_toolchain_test(paths, {
675            'c_compiler': self.DEFAULT_CLANG_RESULT,
676            'cxx_compiler': self.DEFAULT_CLANGXX_RESULT,
677        })
678
679    def test_guess_cxx_clang(self):
680        # When CXX is not set, we guess it from CC.
681        self.do_toolchain_test(self.PATHS, {
682            'c_compiler': self.CLANG_5_0_RESULT,
683            'cxx_compiler': self.CLANGXX_5_0_RESULT,
684        }, environ={
685            'CC': 'clang-5.0',
686        })
687
688    def test_unsupported_clang(self):
689        self.do_toolchain_test(self.PATHS, {
690            'c_compiler': self.CLANG_3_3_RESULT,
691            'cxx_compiler': self.CLANGXX_3_3_RESULT,
692        }, environ={
693            'CC': 'clang-3.3',
694            'CXX': 'clang++-3.3',
695        })
696        self.do_toolchain_test(self.PATHS, {
697            'c_compiler': self.CLANG_4_0_RESULT,
698            'cxx_compiler': self.CLANGXX_4_0_RESULT,
699        }, environ={
700            'CC': 'clang-4.0',
701            'CXX': 'clang++-4.0',
702        })
703
704    def test_no_supported_compiler(self):
705        # Even if there are gcc-x.y or clang-x.y compilers available, we
706        # don't try them. This could be considered something to improve.
707        paths = {
708            k: v for k, v in six.iteritems(self.PATHS)
709            if os.path.basename(k) not in ('gcc', 'g++', 'clang', 'clang++')
710        }
711        self.do_toolchain_test(paths, {
712            'c_compiler': 'Cannot find the target C compiler',
713        })
714
715    def test_absolute_path(self):
716        paths = dict(self.PATHS)
717        paths.update({
718            '/opt/clang/bin/clang': paths['/usr/bin/clang'],
719            '/opt/clang/bin/clang++': paths['/usr/bin/clang++'],
720        })
721        result = {
722            'c_compiler': self.DEFAULT_CLANG_RESULT + {
723                'compiler': '/opt/clang/bin/clang',
724            },
725            'cxx_compiler': self.DEFAULT_CLANGXX_RESULT + {
726                'compiler': '/opt/clang/bin/clang++',
727            },
728        }
729        self.do_toolchain_test(paths, result, environ={
730            'CC': '/opt/clang/bin/clang',
731            'CXX': '/opt/clang/bin/clang++',
732        })
733        # With CXX guess too.
734        self.do_toolchain_test(paths, result, environ={
735            'CC': '/opt/clang/bin/clang',
736        })
737
738    def test_atypical_name(self):
739        paths = dict(self.PATHS)
740        paths.update({
741            '/usr/bin/afl-clang-fast': paths['/usr/bin/clang'],
742            '/usr/bin/afl-clang-fast++': paths['/usr/bin/clang++'],
743        })
744        self.do_toolchain_test(paths, {
745            'c_compiler': self.DEFAULT_CLANG_RESULT + {
746                'compiler': '/usr/bin/afl-clang-fast',
747            },
748            'cxx_compiler': self.DEFAULT_CLANGXX_RESULT + {
749                'compiler': '/usr/bin/afl-clang-fast++',
750            },
751        }, environ={
752            'CC': 'afl-clang-fast',
753            'CXX': 'afl-clang-fast++',
754        })
755
756    def test_mixed_compilers(self):
757        self.do_toolchain_test(self.PATHS, {
758            'c_compiler': self.DEFAULT_CLANG_RESULT,
759            'cxx_compiler': self.DEFAULT_CLANGXX_RESULT,
760            'host_c_compiler': self.DEFAULT_GCC_RESULT,
761            'host_cxx_compiler': self.DEFAULT_GXX_RESULT,
762        }, environ={
763            'CC': 'clang',
764            'HOST_CC': 'gcc',
765        })
766
767        self.do_toolchain_test(self.PATHS, {
768            'c_compiler': self.DEFAULT_CLANG_RESULT,
769            'cxx_compiler': self.DEFAULT_CLANGXX_RESULT,
770            'host_c_compiler': self.DEFAULT_GCC_RESULT,
771            'host_cxx_compiler': self.DEFAULT_GXX_RESULT,
772        }, environ={
773            'CC': 'clang',
774            'CXX': 'clang++',
775            'HOST_CC': 'gcc',
776        })
777
778
779class LinuxSimpleCrossToolchainTest(BaseToolchainTest):
780    TARGET = 'i686-pc-linux-gnu'
781    PATHS = LinuxToolchainTest.PATHS
782    DEFAULT_GCC_RESULT = LinuxToolchainTest.DEFAULT_GCC_RESULT
783    DEFAULT_GXX_RESULT = LinuxToolchainTest.DEFAULT_GXX_RESULT
784    DEFAULT_CLANG_RESULT = LinuxToolchainTest.DEFAULT_CLANG_RESULT
785    DEFAULT_CLANGXX_RESULT = LinuxToolchainTest.DEFAULT_CLANGXX_RESULT
786
787    def test_cross_gcc(self):
788        self.do_toolchain_test(self.PATHS, {
789            'c_compiler': self.DEFAULT_GCC_RESULT + {
790                'flags': ['-m32']
791            },
792            'cxx_compiler': self.DEFAULT_GXX_RESULT + {
793                'flags': ['-m32']
794            },
795            'host_c_compiler': self.DEFAULT_GCC_RESULT,
796            'host_cxx_compiler': self.DEFAULT_GXX_RESULT,
797        }, environ={
798            'CC': 'gcc'
799        })
800
801    def test_cross_clang(self):
802        self.do_toolchain_test(self.PATHS, {
803            'c_compiler': self.DEFAULT_CLANG_RESULT + {
804                'flags': ['-m32']
805            },
806            'cxx_compiler': self.DEFAULT_CLANGXX_RESULT + {
807                'flags': ['-m32']
808            },
809            'host_c_compiler': self.DEFAULT_CLANG_RESULT,
810            'host_cxx_compiler': self.DEFAULT_CLANGXX_RESULT,
811        })
812
813
814class LinuxX86_64CrossToolchainTest(BaseToolchainTest):
815    HOST = 'i686-pc-linux-gnu'
816    TARGET = 'x86_64-pc-linux-gnu'
817    PATHS = {
818        '/usr/bin/gcc': DEFAULT_GCC + GCC_PLATFORM_X86_LINUX,
819        '/usr/bin/g++': DEFAULT_GXX + GCC_PLATFORM_X86_LINUX,
820        '/usr/bin/clang': DEFAULT_CLANG + CLANG_PLATFORM_X86_LINUX,
821        '/usr/bin/clang++': DEFAULT_CLANGXX + CLANG_PLATFORM_X86_LINUX,
822    }
823    DEFAULT_GCC_RESULT = LinuxToolchainTest.DEFAULT_GCC_RESULT
824    DEFAULT_GXX_RESULT = LinuxToolchainTest.DEFAULT_GXX_RESULT
825    DEFAULT_CLANG_RESULT = LinuxToolchainTest.DEFAULT_CLANG_RESULT
826    DEFAULT_CLANGXX_RESULT = LinuxToolchainTest.DEFAULT_CLANGXX_RESULT
827
828    def test_cross_gcc(self):
829        self.do_toolchain_test(self.PATHS, {
830            'c_compiler': self.DEFAULT_GCC_RESULT + {
831                'flags': ['-m64']
832            },
833            'cxx_compiler': self.DEFAULT_GXX_RESULT + {
834                'flags': ['-m64']
835            },
836            'host_c_compiler': self.DEFAULT_GCC_RESULT,
837            'host_cxx_compiler': self.DEFAULT_GXX_RESULT,
838        }, environ={
839            'CC': 'gcc',
840        })
841
842    def test_cross_clang(self):
843        self.do_toolchain_test(self.PATHS, {
844            'c_compiler': self.DEFAULT_CLANG_RESULT + {
845                'flags': ['-m64']
846            },
847            'cxx_compiler': self.DEFAULT_CLANGXX_RESULT + {
848                'flags': ['-m64']
849            },
850            'host_c_compiler': self.DEFAULT_CLANG_RESULT,
851            'host_cxx_compiler': self.DEFAULT_CLANGXX_RESULT,
852        })
853
854
855def xcrun(stdin, args):
856    if args == ('--show-sdk-path',):
857        return 0, os.path.join(os.path.abspath(os.path.dirname(__file__)),
858                               'fake_macos_sdk'), ''
859    raise NotImplementedError()
860
861
862class OSXToolchainTest(BaseToolchainTest):
863    HOST = 'x86_64-apple-darwin11.2.0'
864    PATHS = {
865        '/usr/bin/gcc-5': GCC_5 + GCC_PLATFORM_X86_64_OSX,
866        '/usr/bin/g++-5': GXX_5 + GCC_PLATFORM_X86_64_OSX,
867        '/usr/bin/gcc-7': GCC_7 + GCC_PLATFORM_X86_64_OSX,
868        '/usr/bin/g++-7': GXX_7 + GCC_PLATFORM_X86_64_OSX,
869        '/usr/bin/clang': CLANG_5_0 + CLANG_PLATFORM_X86_64_OSX,
870        '/usr/bin/clang++': CLANGXX_5_0 + CLANG_PLATFORM_X86_64_OSX,
871        '/usr/bin/clang-4.0': CLANG_4_0 + CLANG_PLATFORM_X86_64_OSX,
872        '/usr/bin/clang++-4.0': CLANGXX_4_0 + CLANG_PLATFORM_X86_64_OSX,
873        '/usr/bin/clang-3.3': CLANG_3_3 + CLANG_PLATFORM_X86_64_OSX,
874        '/usr/bin/clang++-3.3': CLANGXX_3_3 + CLANG_PLATFORM_X86_64_OSX,
875        '/usr/bin/xcrun': xcrun,
876    }
877    CLANG_3_3_RESULT = 'Only clang/llvm 5.0 or newer is supported.'
878    CLANGXX_3_3_RESULT = 'Only clang/llvm 5.0 or newer is supported.'
879    CLANG_4_0_RESULT = 'Only clang/llvm 5.0 or newer is supported.'
880    CLANGXX_4_0_RESULT = 'Only clang/llvm 5.0 or newer is supported.'
881    DEFAULT_CLANG_RESULT = CompilerResult(
882        flags=['-std=gnu99'],
883        version='5.0.1',
884        type='clang',
885        compiler='/usr/bin/clang',
886        language='C',
887    )
888    DEFAULT_CLANGXX_RESULT = CompilerResult(
889        flags=['-std=gnu++17'],
890        version='5.0.1',
891        type='clang',
892        compiler='/usr/bin/clang++',
893        language='C++',
894    )
895    GCC_5_RESULT = LinuxToolchainTest.GCC_5_RESULT
896    GXX_5_RESULT = LinuxToolchainTest.GXX_5_RESULT
897    GCC_7_RESULT = LinuxToolchainTest.GCC_7_RESULT
898    GXX_7_RESULT = LinuxToolchainTest.GXX_7_RESULT
899    SYSROOT_FLAGS = {
900        'flags': PrependFlags(['-isysroot', xcrun('', ('--show-sdk-path',))[1]]),
901    }
902
903    def test_clang(self):
904        # We only try clang because gcc is known not to work.
905        self.do_toolchain_test(self.PATHS, {
906            'c_compiler': self.DEFAULT_CLANG_RESULT + self.SYSROOT_FLAGS,
907            'cxx_compiler': self.DEFAULT_CLANGXX_RESULT + self.SYSROOT_FLAGS,
908        })
909
910    def test_not_gcc(self):
911        # We won't pick GCC if it's the only thing available.
912        paths = {
913            k: v for k, v in six.iteritems(self.PATHS)
914            if os.path.basename(k) not in ('clang', 'clang++')
915        }
916        self.do_toolchain_test(paths, {
917            'c_compiler': 'Cannot find the target C compiler',
918        })
919
920    def test_unsupported_clang(self):
921        self.do_toolchain_test(self.PATHS, {
922            'c_compiler': self.CLANG_3_3_RESULT,
923            'cxx_compiler': self.CLANGXX_3_3_RESULT,
924        }, environ={
925            'CC': 'clang-3.3',
926            'CXX': 'clang++-3.3',
927        })
928        # When targeting mac, we require at least version 5.
929        self.do_toolchain_test(self.PATHS, {
930            'c_compiler': self.CLANG_4_0_RESULT,
931            'cxx_compiler': self.CLANGXX_4_0_RESULT,
932        }, environ={
933            'CC': 'clang-4.0',
934            'CXX': 'clang++-4.0',
935        })
936
937    def test_forced_gcc(self):
938        # GCC can still be forced if the user really wants it.
939        self.do_toolchain_test(self.PATHS, {
940            'c_compiler': self.GCC_7_RESULT + self.SYSROOT_FLAGS,
941            'cxx_compiler': self.GXX_7_RESULT + self.SYSROOT_FLAGS,
942        }, environ={
943            'CC': 'gcc-7',
944            'CXX': 'g++-7',
945        })
946
947    def test_forced_unsupported_gcc(self):
948        self.do_toolchain_test(self.PATHS, {
949            'c_compiler': self.GCC_5_RESULT,
950        }, environ={
951            'CC': 'gcc-5',
952            'CXX': 'g++-5',
953        })
954
955
956class WindowsToolchainTest(BaseToolchainTest):
957    HOST = 'i686-pc-mingw32'
958
959    # For the purpose of this test, it doesn't matter that the paths are not
960    # real Windows paths.
961    PATHS = {
962        '/usr/bin/cl': VS_2017u8 + VS_PLATFORM_X86,
963        '/usr/bin/clang-cl-3.9': CLANG_CL_3_9 + CLANG_CL_PLATFORM_X86,
964        '/usr/bin/clang-cl': CLANG_CL_8_0 + CLANG_CL_PLATFORM_X86,
965        '/usr/bin/gcc': DEFAULT_GCC + GCC_PLATFORM_X86_WIN,
966        '/usr/bin/g++': DEFAULT_GXX + GCC_PLATFORM_X86_WIN,
967        '/usr/bin/gcc-4.9': GCC_4_9 + GCC_PLATFORM_X86_WIN,
968        '/usr/bin/g++-4.9': GXX_4_9 + GCC_PLATFORM_X86_WIN,
969        '/usr/bin/gcc-5': GCC_5 + GCC_PLATFORM_X86_WIN,
970        '/usr/bin/g++-5': GXX_5 + GCC_PLATFORM_X86_WIN,
971        '/usr/bin/gcc-6': GCC_6 + GCC_PLATFORM_X86_WIN,
972        '/usr/bin/g++-6': GXX_6 + GCC_PLATFORM_X86_WIN,
973        '/usr/bin/gcc-7': GCC_7 + GCC_PLATFORM_X86_WIN,
974        '/usr/bin/g++-7': GXX_7 + GCC_PLATFORM_X86_WIN,
975        '/usr/bin/clang': DEFAULT_CLANG + CLANG_PLATFORM_X86_WIN,
976        '/usr/bin/clang++': DEFAULT_CLANGXX + CLANG_PLATFORM_X86_WIN,
977        '/usr/bin/clang-5.0': CLANG_5_0 + CLANG_PLATFORM_X86_WIN,
978        '/usr/bin/clang++-5.0': CLANGXX_5_0 + CLANG_PLATFORM_X86_WIN,
979        '/usr/bin/clang-4.0': CLANG_4_0 + CLANG_PLATFORM_X86_WIN,
980        '/usr/bin/clang++-4.0': CLANGXX_4_0 + CLANG_PLATFORM_X86_WIN,
981        '/usr/bin/clang-3.3': CLANG_3_3 + CLANG_PLATFORM_X86_WIN,
982        '/usr/bin/clang++-3.3': CLANGXX_3_3 + CLANG_PLATFORM_X86_WIN,
983    }
984
985    CLANG_CL_3_9_RESULT = 'Only clang-cl 8.0 or newer is supported (found version 3.9.0)'
986    CLANG_CL_8_0_RESULT = CompilerResult(
987        version='8.0.0',
988        flags=['-Xclang', '-std=gnu99'],
989        type='clang-cl',
990        compiler='/usr/bin/clang-cl',
991        language='C',
992    )
993    CLANGXX_CL_3_9_RESULT = 'Only clang-cl 8.0 or newer is supported (found version 3.9.0)'
994    CLANGXX_CL_8_0_RESULT = CompilerResult(
995        version='8.0.0',
996        flags=['-Xclang', '-std=c++17'],
997        type='clang-cl',
998        compiler='/usr/bin/clang-cl',
999        language='C++',
1000    )
1001    CLANG_3_3_RESULT = LinuxToolchainTest.CLANG_3_3_RESULT
1002    CLANGXX_3_3_RESULT = LinuxToolchainTest.CLANGXX_3_3_RESULT
1003    CLANG_4_0_RESULT = LinuxToolchainTest.CLANG_4_0_RESULT
1004    CLANGXX_4_0_RESULT = LinuxToolchainTest.CLANGXX_4_0_RESULT
1005    DEFAULT_CLANG_RESULT = LinuxToolchainTest.DEFAULT_CLANG_RESULT
1006    DEFAULT_CLANGXX_RESULT = LinuxToolchainTest.DEFAULT_CLANGXX_RESULT
1007    GCC_4_9_RESULT = LinuxToolchainTest.GCC_4_9_RESULT
1008    GXX_4_9_RESULT = LinuxToolchainTest.GXX_4_9_RESULT
1009    GCC_5_RESULT = LinuxToolchainTest.GCC_5_RESULT
1010    GXX_5_RESULT = LinuxToolchainTest.GXX_5_RESULT
1011    GCC_6_RESULT = LinuxToolchainTest.GCC_6_RESULT
1012    GXX_6_RESULT = LinuxToolchainTest.GXX_6_RESULT
1013    GCC_7_RESULT = LinuxToolchainTest.GCC_7_RESULT
1014    GXX_7_RESULT = LinuxToolchainTest.GXX_7_RESULT
1015    DEFAULT_GCC_RESULT = LinuxToolchainTest.DEFAULT_GCC_RESULT
1016    DEFAULT_GXX_RESULT = LinuxToolchainTest.DEFAULT_GXX_RESULT
1017
1018    def test_unsupported_msvc(self):
1019        self.do_toolchain_test(self.PATHS, {
1020            'c_compiler': 'Unknown compiler or compiler not supported.'
1021        }, environ={
1022            'CC': '/usr/bin/cl',
1023        })
1024
1025    def test_unsupported_clang_cl(self):
1026        self.do_toolchain_test(self.PATHS, {
1027            'c_compiler': self.CLANG_CL_3_9_RESULT,
1028        }, environ={
1029            'CC': '/usr/bin/clang-cl-3.9',
1030        })
1031
1032    def test_clang_cl(self):
1033        self.do_toolchain_test(self.PATHS, {
1034            'c_compiler': self.CLANG_CL_8_0_RESULT,
1035            'cxx_compiler': self.CLANGXX_CL_8_0_RESULT,
1036        })
1037
1038    def test_gcc(self):
1039        # We'll pick GCC if msvc and clang-cl can't be found.
1040        paths = {
1041            k: v for k, v in six.iteritems(self.PATHS)
1042            if os.path.basename(k) not in ('cl', 'clang-cl')
1043        }
1044        self.do_toolchain_test(paths, {
1045            'c_compiler': self.DEFAULT_GCC_RESULT,
1046            'cxx_compiler': self.DEFAULT_GXX_RESULT,
1047        })
1048
1049    def test_overridden_unsupported_gcc(self):
1050        self.do_toolchain_test(self.PATHS, {
1051            'c_compiler': self.GCC_5_RESULT,
1052        }, environ={
1053            'CC': 'gcc-5',
1054            'CXX': 'g++-5',
1055        })
1056
1057    def test_clang(self):
1058        # We'll pick clang if nothing else is found.
1059        paths = {
1060            k: v for k, v in six.iteritems(self.PATHS)
1061            if os.path.basename(k) not in ('cl', 'clang-cl', 'gcc')
1062        }
1063        self.do_toolchain_test(paths, {
1064            'c_compiler': self.DEFAULT_CLANG_RESULT,
1065            'cxx_compiler': self.DEFAULT_CLANGXX_RESULT,
1066        })
1067
1068    def test_overridden_unsupported_clang(self):
1069        # clang 3.3 C compiler is perfectly fine, but we need more for C++.
1070        self.do_toolchain_test(self.PATHS, {
1071            'c_compiler': self.CLANG_3_3_RESULT,
1072            'cxx_compiler': self.CLANGXX_3_3_RESULT,
1073        }, environ={
1074            'CC': 'clang-3.3',
1075            'CXX': 'clang++-3.3',
1076        })
1077
1078
1079class Windows64ToolchainTest(WindowsToolchainTest):
1080    HOST = 'x86_64-pc-mingw32'
1081
1082    # For the purpose of this test, it doesn't matter that the paths are not
1083    # real Windows paths.
1084    PATHS = {
1085        '/usr/bin/cl': VS_2017u8 + VS_PLATFORM_X86_64,
1086        '/usr/bin/clang-cl': CLANG_CL_8_0 + CLANG_CL_PLATFORM_X86_64,
1087        '/usr/bin/clang-cl-3.9': CLANG_CL_3_9 + CLANG_CL_PLATFORM_X86_64,
1088        '/usr/bin/gcc': DEFAULT_GCC + GCC_PLATFORM_X86_64_WIN,
1089        '/usr/bin/g++': DEFAULT_GXX + GCC_PLATFORM_X86_64_WIN,
1090        '/usr/bin/gcc-4.9': GCC_4_9 + GCC_PLATFORM_X86_64_WIN,
1091        '/usr/bin/g++-4.9': GXX_4_9 + GCC_PLATFORM_X86_64_WIN,
1092        '/usr/bin/gcc-5': GCC_5 + GCC_PLATFORM_X86_64_WIN,
1093        '/usr/bin/g++-5': GXX_5 + GCC_PLATFORM_X86_64_WIN,
1094        '/usr/bin/gcc-6': GCC_6 + GCC_PLATFORM_X86_64_WIN,
1095        '/usr/bin/g++-6': GXX_6 + GCC_PLATFORM_X86_64_WIN,
1096        '/usr/bin/gcc-7': GCC_7 + GCC_PLATFORM_X86_64_WIN,
1097        '/usr/bin/g++-7': GXX_7 + GCC_PLATFORM_X86_64_WIN,
1098        '/usr/bin/clang': DEFAULT_CLANG + CLANG_PLATFORM_X86_64_WIN,
1099        '/usr/bin/clang++': DEFAULT_CLANGXX + CLANG_PLATFORM_X86_64_WIN,
1100        '/usr/bin/clang-5.0': CLANG_5_0 + CLANG_PLATFORM_X86_64_WIN,
1101        '/usr/bin/clang++-5.0': CLANGXX_5_0 + CLANG_PLATFORM_X86_64_WIN,
1102        '/usr/bin/clang-4.0': CLANG_4_0 + CLANG_PLATFORM_X86_64_WIN,
1103        '/usr/bin/clang++-4.0': CLANGXX_4_0 + CLANG_PLATFORM_X86_64_WIN,
1104        '/usr/bin/clang-3.3': CLANG_3_3 + CLANG_PLATFORM_X86_64_WIN,
1105        '/usr/bin/clang++-3.3': CLANGXX_3_3 + CLANG_PLATFORM_X86_64_WIN,
1106    }
1107
1108
1109class LinuxCrossCompileToolchainTest(BaseToolchainTest):
1110    TARGET = 'arm-unknown-linux-gnu'
1111    PATHS = {
1112        '/usr/bin/arm-linux-gnu-gcc-4.9': GCC_4_9 + GCC_PLATFORM_ARM_LINUX,
1113        '/usr/bin/arm-linux-gnu-g++-4.9': GXX_4_9 + GCC_PLATFORM_ARM_LINUX,
1114        '/usr/bin/arm-linux-gnu-gcc-5': GCC_5 + GCC_PLATFORM_ARM_LINUX,
1115        '/usr/bin/arm-linux-gnu-g++-5': GXX_5 + GCC_PLATFORM_ARM_LINUX,
1116        '/usr/bin/arm-linux-gnu-gcc': DEFAULT_GCC + GCC_PLATFORM_ARM_LINUX,
1117        '/usr/bin/arm-linux-gnu-g++': DEFAULT_GXX + GCC_PLATFORM_ARM_LINUX,
1118        '/usr/bin/arm-linux-gnu-gcc-7': GCC_7 + GCC_PLATFORM_ARM_LINUX,
1119        '/usr/bin/arm-linux-gnu-g++-7': GXX_7 + GCC_PLATFORM_ARM_LINUX,
1120    }
1121    PATHS.update(LinuxToolchainTest.PATHS)
1122    ARM_GCC_4_9_RESULT = LinuxToolchainTest.GCC_4_9_RESULT
1123    ARM_GXX_4_9_RESULT = LinuxToolchainTest.GXX_4_9_RESULT
1124    ARM_GCC_5_RESULT = LinuxToolchainTest.GCC_5_RESULT
1125    ARM_GXX_5_RESULT = LinuxToolchainTest.GXX_5_RESULT
1126    ARM_DEFAULT_GCC_RESULT = LinuxToolchainTest.DEFAULT_GCC_RESULT + {
1127        'compiler': '/usr/bin/arm-linux-gnu-gcc',
1128    }
1129    ARM_DEFAULT_GXX_RESULT = LinuxToolchainTest.DEFAULT_GXX_RESULT + {
1130        'compiler': '/usr/bin/arm-linux-gnu-g++',
1131    }
1132    ARM_GCC_7_RESULT = LinuxToolchainTest.GCC_7_RESULT + {
1133        'compiler': '/usr/bin/arm-linux-gnu-gcc-7',
1134    }
1135    ARM_GXX_7_RESULT = LinuxToolchainTest.GXX_7_RESULT + {
1136        'compiler': '/usr/bin/arm-linux-gnu-g++-7',
1137    }
1138    DEFAULT_CLANG_RESULT = LinuxToolchainTest.DEFAULT_CLANG_RESULT
1139    DEFAULT_CLANGXX_RESULT = LinuxToolchainTest.DEFAULT_CLANGXX_RESULT
1140    DEFAULT_GCC_RESULT = LinuxToolchainTest.DEFAULT_GCC_RESULT
1141    DEFAULT_GXX_RESULT = LinuxToolchainTest.DEFAULT_GXX_RESULT
1142
1143    little_endian = FakeCompiler(GCC_PLATFORM_LINUX,
1144                                 GCC_PLATFORM_LITTLE_ENDIAN)
1145    big_endian = FakeCompiler(GCC_PLATFORM_LINUX, GCC_PLATFORM_BIG_ENDIAN)
1146
1147    PLATFORMS = {
1148        'i686-pc-linux-gnu': GCC_PLATFORM_X86_LINUX,
1149        'x86_64-pc-linux-gnu': GCC_PLATFORM_X86_64_LINUX,
1150        'arm-unknown-linux-gnu': GCC_PLATFORM_ARM_LINUX,
1151        'aarch64-unknown-linux-gnu': little_endian + {
1152            '__aarch64__': 1,
1153        },
1154        'ia64-unknown-linux-gnu': little_endian + {
1155            '__ia64__': 1,
1156        },
1157        's390x-unknown-linux-gnu': big_endian + {
1158            '__s390x__': 1,
1159            '__s390__': 1,
1160        },
1161        's390-unknown-linux-gnu': big_endian + {
1162            '__s390__': 1,
1163        },
1164        'powerpc64-unknown-linux-gnu': big_endian + {
1165            None: {
1166                '__powerpc64__': 1,
1167                '__powerpc__': 1,
1168            },
1169            '-m32': {
1170                '__powerpc64__': False,
1171            },
1172        },
1173        'powerpc-unknown-linux-gnu': big_endian + {
1174            None: {
1175                '__powerpc__': 1,
1176            },
1177            '-m64': {
1178                '__powerpc64__': 1,
1179            },
1180        },
1181        'alpha-unknown-linux-gnu': little_endian + {
1182            '__alpha__': 1,
1183        },
1184        'hppa-unknown-linux-gnu': big_endian + {
1185            '__hppa__': 1,
1186        },
1187        'sparc64-unknown-linux-gnu': big_endian + {
1188            None: {
1189                '__arch64__': 1,
1190                '__sparc__': 1,
1191            },
1192            '-m32': {
1193                '__arch64__': False,
1194            },
1195        },
1196        'sparc-unknown-linux-gnu': big_endian + {
1197            None: {
1198                '__sparc__': 1,
1199            },
1200            '-m64': {
1201                '__arch64__': 1,
1202            },
1203        },
1204        'mips64-unknown-linux-gnuabi64': big_endian + {
1205            '__mips64': 1,
1206            '__mips__': 1,
1207        },
1208        'mips-unknown-linux-gnu': big_endian + {
1209            '__mips__': 1,
1210        },
1211        'sh4-unknown-linux-gnu': little_endian + {
1212            '__sh__': 1,
1213        },
1214    }
1215
1216    PLATFORMS['powerpc64le-unknown-linux-gnu'] = \
1217        PLATFORMS['powerpc64-unknown-linux-gnu'] + GCC_PLATFORM_LITTLE_ENDIAN
1218    PLATFORMS['mips64el-unknown-linux-gnuabi64'] = \
1219        PLATFORMS['mips64-unknown-linux-gnuabi64'] + GCC_PLATFORM_LITTLE_ENDIAN
1220    PLATFORMS['mipsel-unknown-linux-gnu'] = \
1221        PLATFORMS['mips-unknown-linux-gnu'] + GCC_PLATFORM_LITTLE_ENDIAN
1222
1223    def do_test_cross_gcc_32_64(self, host, target):
1224        self.HOST = host
1225        self.TARGET = target
1226        paths = {
1227            '/usr/bin/gcc': DEFAULT_GCC + self.PLATFORMS[host],
1228            '/usr/bin/g++': DEFAULT_GXX + self.PLATFORMS[host],
1229        }
1230        cross_flags = {
1231            'flags': ['-m64' if '64' in target else '-m32']
1232        }
1233        self.do_toolchain_test(paths, {
1234            'c_compiler': self.DEFAULT_GCC_RESULT + cross_flags,
1235            'cxx_compiler': self.DEFAULT_GXX_RESULT + cross_flags,
1236            'host_c_compiler': self.DEFAULT_GCC_RESULT,
1237            'host_cxx_compiler': self.DEFAULT_GXX_RESULT,
1238        })
1239        self.HOST = LinuxCrossCompileToolchainTest.HOST
1240        self.TARGET = LinuxCrossCompileToolchainTest.TARGET
1241
1242    def test_cross_x86_x64(self):
1243        self.do_test_cross_gcc_32_64(
1244            'i686-pc-linux-gnu', 'x86_64-pc-linux-gnu')
1245        self.do_test_cross_gcc_32_64(
1246            'x86_64-pc-linux-gnu', 'i686-pc-linux-gnu')
1247
1248    def test_cross_sparc_sparc64(self):
1249        self.do_test_cross_gcc_32_64(
1250            'sparc-unknown-linux-gnu', 'sparc64-unknown-linux-gnu')
1251        self.do_test_cross_gcc_32_64(
1252            'sparc64-unknown-linux-gnu', 'sparc-unknown-linux-gnu')
1253
1254    def test_cross_ppc_ppc64(self):
1255        self.do_test_cross_gcc_32_64(
1256            'powerpc-unknown-linux-gnu', 'powerpc64-unknown-linux-gnu')
1257        self.do_test_cross_gcc_32_64(
1258            'powerpc64-unknown-linux-gnu', 'powerpc-unknown-linux-gnu')
1259
1260    def do_test_cross_gcc(self, host, target):
1261        self.HOST = host
1262        self.TARGET = target
1263        host_cpu = host.split('-')[0]
1264        cpu, manufacturer, os = target.split('-', 2)
1265        toolchain_prefix = '/usr/bin/%s-%s' % (cpu, os)
1266        paths = {
1267            '/usr/bin/gcc': DEFAULT_GCC + self.PLATFORMS[host],
1268            '/usr/bin/g++': DEFAULT_GXX + self.PLATFORMS[host],
1269        }
1270        self.do_toolchain_test(paths, {
1271            'c_compiler': ('Target C compiler target CPU (%s) '
1272                           'does not match --target CPU (%s)'
1273                           % (host_cpu, cpu)),
1274        })
1275
1276        paths.update({
1277            '%s-gcc' % toolchain_prefix: DEFAULT_GCC + self.PLATFORMS[target],
1278            '%s-g++' % toolchain_prefix: DEFAULT_GXX + self.PLATFORMS[target],
1279        })
1280        self.do_toolchain_test(paths, {
1281            'c_compiler': self.DEFAULT_GCC_RESULT + {
1282                'compiler': '%s-gcc' % toolchain_prefix,
1283            },
1284            'cxx_compiler': self.DEFAULT_GXX_RESULT + {
1285                'compiler': '%s-g++' % toolchain_prefix,
1286            },
1287            'host_c_compiler': self.DEFAULT_GCC_RESULT,
1288            'host_cxx_compiler': self.DEFAULT_GXX_RESULT,
1289        })
1290        self.HOST = LinuxCrossCompileToolchainTest.HOST
1291        self.TARGET = LinuxCrossCompileToolchainTest.TARGET
1292
1293    def test_cross_gcc_misc(self):
1294        for target in self.PLATFORMS:
1295            if not target.endswith('-pc-linux-gnu'):
1296                self.do_test_cross_gcc('x86_64-pc-linux-gnu', target)
1297
1298    def test_cannot_cross(self):
1299        self.TARGET = 'mipsel-unknown-linux-gnu'
1300
1301        paths = {
1302            '/usr/bin/gcc': DEFAULT_GCC + self.PLATFORMS['mips-unknown-linux-gnu'],
1303            '/usr/bin/g++': DEFAULT_GXX + self.PLATFORMS['mips-unknown-linux-gnu'],
1304        }
1305        self.do_toolchain_test(paths, {
1306            'c_compiler': ('Target C compiler target endianness (big) '
1307                           'does not match --target endianness (little)'),
1308        })
1309        self.TARGET = LinuxCrossCompileToolchainTest.TARGET
1310
1311    def test_overridden_cross_gcc(self):
1312        self.do_toolchain_test(self.PATHS, {
1313            'c_compiler': self.ARM_GCC_7_RESULT,
1314            'cxx_compiler': self.ARM_GXX_7_RESULT,
1315            'host_c_compiler': self.DEFAULT_GCC_RESULT,
1316            'host_cxx_compiler': self.DEFAULT_GXX_RESULT,
1317        }, environ={
1318            'CC': 'arm-linux-gnu-gcc-7',
1319            'CXX': 'arm-linux-gnu-g++-7',
1320        })
1321
1322    def test_overridden_unsupported_cross_gcc(self):
1323        self.do_toolchain_test(self.PATHS, {
1324            'c_compiler': self.ARM_GCC_4_9_RESULT,
1325        }, environ={
1326            'CC': 'arm-linux-gnu-gcc-4.9',
1327            'CXX': 'arm-linux-gnu-g++-4.9',
1328        })
1329
1330    def test_guess_cross_cxx(self):
1331        # When CXX is not set, we guess it from CC.
1332        self.do_toolchain_test(self.PATHS, {
1333            'c_compiler': self.ARM_GCC_7_RESULT,
1334            'cxx_compiler': self.ARM_GXX_7_RESULT,
1335            'host_c_compiler': self.DEFAULT_GCC_RESULT,
1336            'host_cxx_compiler': self.DEFAULT_GXX_RESULT,
1337        }, environ={
1338            'CC': 'arm-linux-gnu-gcc-7',
1339        })
1340
1341        self.do_toolchain_test(self.PATHS, {
1342            'c_compiler': self.ARM_DEFAULT_GCC_RESULT,
1343            'cxx_compiler': self.ARM_DEFAULT_GXX_RESULT,
1344            'host_c_compiler': self.DEFAULT_CLANG_RESULT,
1345            'host_cxx_compiler': self.DEFAULT_CLANGXX_RESULT,
1346        }, environ={
1347            'CC': 'arm-linux-gnu-gcc',
1348            'HOST_CC': 'clang',
1349        })
1350
1351        self.do_toolchain_test(self.PATHS, {
1352            'c_compiler': self.ARM_DEFAULT_GCC_RESULT,
1353            'cxx_compiler': self.ARM_DEFAULT_GXX_RESULT,
1354            'host_c_compiler': self.DEFAULT_CLANG_RESULT,
1355            'host_cxx_compiler': self.DEFAULT_CLANGXX_RESULT,
1356        }, environ={
1357            'CC': 'arm-linux-gnu-gcc',
1358            'CXX': 'arm-linux-gnu-g++',
1359            'HOST_CC': 'clang',
1360        })
1361
1362    def test_cross_clang(self):
1363        cross_clang_result = self.DEFAULT_CLANG_RESULT + {
1364            'flags': ['--target=arm-linux-gnu'],
1365        }
1366        cross_clangxx_result = self.DEFAULT_CLANGXX_RESULT + {
1367            'flags': ['--target=arm-linux-gnu'],
1368        }
1369        self.do_toolchain_test(self.PATHS, {
1370            'c_compiler': cross_clang_result,
1371            'cxx_compiler': cross_clangxx_result,
1372            'host_c_compiler': self.DEFAULT_CLANG_RESULT,
1373            'host_cxx_compiler': self.DEFAULT_CLANGXX_RESULT,
1374        }, environ={
1375            'CC': 'clang',
1376            'HOST_CC': 'clang',
1377        })
1378
1379        self.do_toolchain_test(self.PATHS, {
1380            'c_compiler': cross_clang_result,
1381            'cxx_compiler': cross_clangxx_result,
1382            'host_c_compiler': self.DEFAULT_CLANG_RESULT,
1383            'host_cxx_compiler': self.DEFAULT_CLANGXX_RESULT,
1384        }, environ={
1385            'CC': 'clang',
1386        })
1387
1388    def test_cross_atypical_clang(self):
1389        paths = dict(self.PATHS)
1390        paths.update({
1391            '/usr/bin/afl-clang-fast': paths['/usr/bin/clang'],
1392            '/usr/bin/afl-clang-fast++': paths['/usr/bin/clang++'],
1393        })
1394        afl_clang_result = self.DEFAULT_CLANG_RESULT + {
1395            'compiler': '/usr/bin/afl-clang-fast',
1396        }
1397        afl_clangxx_result = self.DEFAULT_CLANGXX_RESULT + {
1398            'compiler': '/usr/bin/afl-clang-fast++',
1399        }
1400        self.do_toolchain_test(paths, {
1401            'c_compiler': afl_clang_result + {
1402                'flags': ['--target=arm-linux-gnu'],
1403            },
1404            'cxx_compiler': afl_clangxx_result + {
1405                'flags': ['--target=arm-linux-gnu'],
1406            },
1407            'host_c_compiler': afl_clang_result,
1408            'host_cxx_compiler': afl_clangxx_result,
1409        }, environ={
1410            'CC': 'afl-clang-fast',
1411            'CXX': 'afl-clang-fast++',
1412        })
1413
1414
1415class OSXCrossToolchainTest(BaseToolchainTest):
1416    TARGET = 'i686-apple-darwin11.2.0'
1417    PATHS = dict(LinuxToolchainTest.PATHS)
1418    PATHS.update({
1419        '/usr/bin/clang': CLANG_5_0 + CLANG_PLATFORM_X86_64_LINUX,
1420        '/usr/bin/clang++': CLANGXX_5_0 + CLANG_PLATFORM_X86_64_LINUX,
1421    })
1422    DEFAULT_CLANG_RESULT = OSXToolchainTest.DEFAULT_CLANG_RESULT
1423    DEFAULT_CLANGXX_RESULT = OSXToolchainTest.DEFAULT_CLANGXX_RESULT
1424
1425    def test_osx_cross(self):
1426        self.do_toolchain_test(self.PATHS, {
1427            'c_compiler': self.DEFAULT_CLANG_RESULT + OSXToolchainTest.SYSROOT_FLAGS + {
1428                'flags': ['--target=i686-apple-darwin11.2.0'],
1429            },
1430            'cxx_compiler': self.DEFAULT_CLANGXX_RESULT + OSXToolchainTest.SYSROOT_FLAGS + {
1431                'flags': ['--target=i686-apple-darwin11.2.0'],
1432            },
1433            'host_c_compiler': self.DEFAULT_CLANG_RESULT,
1434            'host_cxx_compiler': self.DEFAULT_CLANGXX_RESULT,
1435        }, environ={
1436            'CC': 'clang',
1437        }, args=['--with-macos-sdk=%s' % OSXToolchainTest.SYSROOT_FLAGS['flags'][1]])
1438
1439    def test_cannot_osx_cross(self):
1440        self.do_toolchain_test(self.PATHS, {
1441            'c_compiler': 'Target C compiler target kernel (Linux) does not '
1442                          'match --target kernel (Darwin)',
1443        }, environ={
1444            'CC': 'gcc',
1445        }, args=['--with-macos-sdk=%s' % OSXToolchainTest.SYSROOT_FLAGS['flags'][1]])
1446
1447
1448class WindowsCrossToolchainTest(BaseToolchainTest):
1449    TARGET = 'x86_64-pc-mingw32'
1450    DEFAULT_CLANG_RESULT = LinuxToolchainTest.DEFAULT_CLANG_RESULT
1451    DEFAULT_CLANGXX_RESULT = LinuxToolchainTest.DEFAULT_CLANGXX_RESULT
1452
1453    def test_clang_cl_cross(self):
1454        paths = {
1455            '/usr/bin/clang-cl': CLANG_CL_8_0 + CLANG_CL_PLATFORM_X86_64,
1456        }
1457        paths.update(LinuxToolchainTest.PATHS)
1458        self.do_toolchain_test(paths, {
1459            'c_compiler': WindowsToolchainTest.CLANG_CL_8_0_RESULT,
1460            'cxx_compiler': WindowsToolchainTest.CLANGXX_CL_8_0_RESULT,
1461            'host_c_compiler': self.DEFAULT_CLANG_RESULT,
1462            'host_cxx_compiler': self.DEFAULT_CLANGXX_RESULT,
1463        })
1464
1465
1466class OpenBSDToolchainTest(BaseToolchainTest):
1467    HOST = 'x86_64-unknown-openbsd6.1'
1468    TARGET = 'x86_64-unknown-openbsd6.1'
1469    PATHS = {
1470        '/usr/bin/gcc': DEFAULT_GCC + GCC_PLATFORM_X86_64 + GCC_PLATFORM_OPENBSD,
1471        '/usr/bin/g++': DEFAULT_GXX + GCC_PLATFORM_X86_64 + GCC_PLATFORM_OPENBSD,
1472    }
1473    DEFAULT_GCC_RESULT = LinuxToolchainTest.DEFAULT_GCC_RESULT
1474    DEFAULT_GXX_RESULT = LinuxToolchainTest.DEFAULT_GXX_RESULT
1475
1476    def test_gcc(self):
1477        self.do_toolchain_test(self.PATHS, {
1478            'c_compiler': self.DEFAULT_GCC_RESULT,
1479            'cxx_compiler': self.DEFAULT_GXX_RESULT,
1480        })
1481
1482
1483@memoize
1484def gen_invoke_cargo(version, rustup_wrapper=False):
1485    def invoke_cargo(stdin, args):
1486        args = tuple(args)
1487        if not rustup_wrapper and args == ('+stable',):
1488            return (101, '', 'we are the real thing')
1489        if args == ('--version', '--verbose'):
1490            return 0, 'cargo %s\nrelease: %s' % (version, version), ''
1491        raise NotImplementedError('unsupported arguments')
1492    return invoke_cargo
1493
1494
1495@memoize
1496def gen_invoke_rustc(version, rustup_wrapper=False):
1497    def invoke_rustc(stdin, args):
1498        args = tuple(args)
1499        # TODO: we don't have enough machinery set up to test the `rustup which`
1500        # fallback yet.
1501        if not rustup_wrapper and args == ('+stable',):
1502            return (1, '', 'error: couldn\'t read +stable: No such file or directory')
1503        if args == ('--version', '--verbose'):
1504            return (0, 'rustc %s\nrelease: %s\nhost: x86_64-unknown-linux-gnu'
1505                       % (version, version), '')
1506        if args == ('--print', 'target-list'):
1507            # Raw list returned by rustc version 1.32, + ios, which somehow
1508            # don't appear in the default list.
1509            # https://github.com/rust-lang/rust/issues/36156
1510            rust_targets = [
1511                'aarch64-apple-ios',
1512                'aarch64-fuchsia',
1513                'aarch64-linux-android',
1514                'aarch64-pc-windows-msvc',
1515                'aarch64-unknown-cloudabi',
1516                'aarch64-unknown-freebsd',
1517                'aarch64-unknown-hermit',
1518                'aarch64-unknown-linux-gnu',
1519                'aarch64-unknown-linux-musl',
1520                'aarch64-unknown-netbsd',
1521                'aarch64-unknown-none',
1522                'aarch64-unknown-openbsd',
1523                'arm-linux-androideabi',
1524                'arm-unknown-linux-gnueabi',
1525                'arm-unknown-linux-gnueabihf',
1526                'arm-unknown-linux-musleabi',
1527                'arm-unknown-linux-musleabihf',
1528                'armebv7r-none-eabi',
1529                'armebv7r-none-eabihf',
1530                'armv4t-unknown-linux-gnueabi',
1531                'armv5te-unknown-linux-gnueabi',
1532                'armv5te-unknown-linux-musleabi',
1533                'armv6-unknown-netbsd-eabihf',
1534                'armv7-linux-androideabi',
1535                'armv7-unknown-cloudabi-eabihf',
1536                'armv7-unknown-linux-gnueabihf',
1537                'armv7-unknown-linux-musleabihf',
1538                'armv7-unknown-netbsd-eabihf',
1539                'armv7r-none-eabi',
1540                'armv7r-none-eabihf',
1541                'armv7s-apple-ios',
1542                'asmjs-unknown-emscripten',
1543                'i386-apple-ios',
1544                'i586-pc-windows-msvc',
1545                'i586-unknown-linux-gnu',
1546                'i586-unknown-linux-musl',
1547                'i686-apple-darwin',
1548                'i686-linux-android',
1549                'i686-pc-windows-gnu',
1550                'i686-pc-windows-msvc',
1551                'i686-unknown-cloudabi',
1552                'i686-unknown-dragonfly',
1553                'i686-unknown-freebsd',
1554                'i686-unknown-haiku',
1555                'i686-unknown-linux-gnu',
1556                'i686-unknown-linux-musl',
1557                'i686-unknown-netbsd',
1558                'i686-unknown-openbsd',
1559                'mips-unknown-linux-gnu',
1560                'mips-unknown-linux-musl',
1561                'mips-unknown-linux-uclibc',
1562                'mips64-unknown-linux-gnuabi64',
1563                'mips64el-unknown-linux-gnuabi64',
1564                'mipsel-unknown-linux-gnu',
1565                'mipsel-unknown-linux-musl',
1566                'mipsel-unknown-linux-uclibc',
1567                'msp430-none-elf',
1568                'powerpc-unknown-linux-gnu',
1569                'powerpc-unknown-linux-gnuspe',
1570                'powerpc-unknown-linux-musl',
1571                'powerpc-unknown-netbsd',
1572                'powerpc64-unknown-linux-gnu',
1573                'powerpc64-unknown-linux-musl',
1574                'powerpc64le-unknown-linux-gnu',
1575                'powerpc64le-unknown-linux-musl',
1576                'riscv32imac-unknown-none-elf',
1577                'riscv32imc-unknown-none-elf',
1578                's390x-unknown-linux-gnu',
1579                'sparc-unknown-linux-gnu',
1580                'sparc64-unknown-linux-gnu',
1581                'sparc64-unknown-netbsd',
1582                'sparcv9-sun-solaris',
1583                'thumbv6m-none-eabi',
1584                'thumbv7a-pc-windows-msvc',
1585                'thumbv7em-none-eabi',
1586                'thumbv7em-none-eabihf',
1587                'thumbv7m-none-eabi',
1588                'thumbv8m.base-none-eabi',
1589                'wasm32-experimental-emscripten',
1590                'wasm32-unknown-emscripten',
1591                'wasm32-unknown-unknown',
1592                'x86_64-apple-darwin',
1593                'x86_64-apple-ios',
1594                'x86_64-fortanix-unknown-sgx',
1595                'x86_64-fuchsia',
1596                'x86_64-linux-android',
1597                'x86_64-pc-windows-gnu',
1598                'x86_64-pc-windows-msvc',
1599                'x86_64-rumprun-netbsd',
1600                'x86_64-sun-solaris',
1601                'x86_64-unknown-bitrig',
1602                'x86_64-unknown-cloudabi',
1603                'x86_64-unknown-dragonfly',
1604                'x86_64-unknown-freebsd',
1605                'x86_64-unknown-haiku',
1606                'x86_64-unknown-hermit',
1607                'x86_64-unknown-l4re-uclibc',
1608                'x86_64-unknown-linux-gnu',
1609                'x86_64-unknown-linux-gnux32',
1610                'x86_64-unknown-linux-musl',
1611                'x86_64-unknown-netbsd',
1612                'x86_64-unknown-openbsd',
1613                'x86_64-unknown-redox',
1614            ]
1615            # Additional targets from 1.33
1616            if Version(version) >= '1.33.0':
1617                rust_targets += [
1618                    'thumbv7neon-linux-androideabi',
1619                    'thumbv7neon-unknown-linux-gnueabihf',
1620                    'x86_64-unknown-uefi',
1621                    'thumbv8m.main-none-eabi',
1622                    'thumbv8m.main-none-eabihf',
1623                ]
1624            # Additional targets from 1.34
1625            if Version(version) >= '1.34.0':
1626                rust_targets += [
1627                    'nvptx64-nvidia-cuda',
1628                    'powerpc64-unknown-freebsd',
1629                    'riscv64gc-unknown-none-elf',
1630                    'riscv64imac-unknown-none-elf',
1631                ]
1632            # Additional targets from 1.35
1633            if Version(version) >= '1.35.0':
1634                rust_targets += [
1635                    'armv6-unknown-freebsd',
1636                    'armv7-unknown-freebsd',
1637                    'mipsisa32r6-unknown-linux-gnu',
1638                    'mipsisa32r6el-unknown-linux-gnu',
1639                    'mipsisa64r6-unknown-linux-gnuabi64',
1640                    'mipsisa64r6el-unknown-linux-gnuabi64',
1641                    'wasm32-unknown-wasi',
1642                ]
1643            # Additional targets from 1.36
1644            if Version(version) >= '1.36.0':
1645                rust_targets += [
1646                    'wasm32-wasi',
1647                ]
1648                rust_targets.remove('wasm32-unknown-wasi')
1649                rust_targets.remove('x86_64-unknown-bitrig')
1650            # Additional targets from 1.37
1651            if Version(version) >= '1.37.0':
1652                rust_targets += [
1653                    'x86_64-pc-solaris',
1654                ]
1655            # Additional targets from 1.38
1656            if Version(version) >= '1.38.0':
1657                rust_targets += [
1658                    'aarch64-unknown-redox',
1659                    'aarch64-wrs-vxworks',
1660                    'armv7-unknown-linux-gnueabi',
1661                    'armv7-unknown-linux-musleabi',
1662                    'armv7-wrs-vxworks',
1663                    'hexagon-unknown-linux-musl',
1664                    'i586-wrs-vxworks',
1665                    'i686-uwp-windows-gnu',
1666                    'i686-wrs-vxworks',
1667                    'powerpc-wrs-vxworks',
1668                    'powerpc-wrs-vxworks-spe',
1669                    'powerpc64-wrs-vxworks',
1670                    'riscv32i-unknown-none-elf',
1671                    'x86_64-uwp-windows-gnu',
1672                    'x86_64-wrs-vxworks',
1673                ]
1674            # Additional targets from 1.38
1675            if Version(version) >= '1.39.0':
1676                rust_targets += [
1677                    'aarch64-uwp-windows-msvc',
1678                    'armv7-wrs-vxworks-eabihf',
1679                    'i686-unknown-uefi',
1680                    'i686-uwp-windows-msvc',
1681                    'mips64-unknown-linux-muslabi64',
1682                    'mips64el-unknown-linux-muslabi64',
1683                    'sparc64-unknown-openbsd',
1684                    'x86_64-linux-kernel',
1685                    'x86_64-uwp-windows-msvc',
1686                ]
1687                rust_targets.remove('armv7-wrs-vxworks')
1688                rust_targets.remove('i586-wrs-vxworks')
1689
1690            return 0, '\n'.join(sorted(rust_targets)), ''
1691        if (len(args) == 6 and args[:2] == ('--crate-type', 'staticlib') and
1692            args[2].startswith('--target=') and args[3] == '-o'):
1693            with open(args[4], 'w') as fh:
1694                fh.write('foo')
1695            return 0, '', ''
1696        raise NotImplementedError('unsupported arguments')
1697    return invoke_rustc
1698
1699
1700class RustTest(BaseConfigureTest):
1701    def get_rust_target(self, target, compiler_type='gcc', version='1.39.0',
1702                        arm_target=None):
1703        environ = {
1704            'PATH': os.pathsep.join(
1705                mozpath.abspath(p) for p in ('/bin', '/usr/bin')),
1706        }
1707
1708        paths = {
1709            mozpath.abspath('/usr/bin/cargo'): gen_invoke_cargo(version),
1710            mozpath.abspath('/usr/bin/rustc'): gen_invoke_rustc(version),
1711        }
1712
1713        self.TARGET = target
1714        # --enable-project=tools/crashreporter for more relaxed rust
1715        # dependency.
1716        sandbox = self.get_sandbox(
1717            paths, {}, ['--enable-project=tools/crashreporter'], environ)
1718
1719        # Trick the sandbox into not running the target compiler check
1720        dep = sandbox._depends[sandbox['c_compiler']]
1721        getattr(sandbox, '__value_for_depends')[(dep,)] = \
1722            CompilerResult(type=compiler_type)
1723        # Same for the arm_target checks.
1724        dep = sandbox._depends[sandbox['arm_target']]
1725        getattr(sandbox, '__value_for_depends')[(dep,)] = \
1726            arm_target or ReadOnlyNamespace(arm_arch=7, thumb2=False,
1727                                            fpu='vfpv2', float_abi='softfp')
1728        return sandbox._value_for(sandbox['rust_target_triple'])
1729
1730    def test_rust_target(self):
1731        # Cases where the output of config.sub matches a rust target
1732        for straightforward in (
1733            'x86_64-unknown-dragonfly',
1734            'aarch64-unknown-freebsd',
1735            'i686-unknown-freebsd',
1736            'x86_64-unknown-freebsd',
1737            'sparc64-unknown-netbsd',
1738            'i686-unknown-netbsd',
1739            'x86_64-unknown-netbsd',
1740            'i686-unknown-openbsd',
1741            'x86_64-unknown-openbsd',
1742            'aarch64-unknown-linux-gnu',
1743            'sparc64-unknown-linux-gnu',
1744            'i686-unknown-linux-gnu',
1745            'i686-apple-darwin',
1746            'x86_64-apple-darwin',
1747            'mips-unknown-linux-gnu',
1748            'mipsel-unknown-linux-gnu',
1749            'mips64-unknown-linux-gnuabi64',
1750            'mips64el-unknown-linux-gnuabi64',
1751            'powerpc64-unknown-linux-gnu',
1752            'powerpc64le-unknown-linux-gnu',
1753        ):
1754            self.assertEqual(self.get_rust_target(straightforward), straightforward)
1755
1756        # Cases where the output of config.sub is different
1757        for autoconf, rust in (
1758            ('aarch64-unknown-linux-android', 'aarch64-linux-android'),
1759            ('arm-unknown-linux-androideabi', 'armv7-linux-androideabi'),
1760            ('armv7-unknown-linux-androideabi', 'armv7-linux-androideabi'),
1761            ('i386-unknown-linux-android', 'i686-linux-android'),
1762            ('i686-unknown-linux-android', 'i686-linux-android'),
1763            ('i686-pc-linux-gnu', 'i686-unknown-linux-gnu'),
1764            ('x86_64-unknown-linux-android', 'x86_64-linux-android'),
1765            ('x86_64-pc-linux-gnu', 'x86_64-unknown-linux-gnu'),
1766            ('sparcv9-sun-solaris2', 'sparcv9-sun-solaris'),
1767            ('x86_64-sun-solaris2', 'x86_64-sun-solaris'),
1768        ):
1769            self.assertEqual(self.get_rust_target(autoconf), rust)
1770
1771        # Windows
1772        for autoconf, building_with_gcc, rust in (
1773            ('i686-pc-mingw32', 'cl', 'i686-pc-windows-msvc'),
1774            ('x86_64-pc-mingw32', 'cl', 'x86_64-pc-windows-msvc'),
1775            ('i686-pc-mingw32', 'gcc', 'i686-pc-windows-gnu'),
1776            ('x86_64-pc-mingw32', 'gcc', 'x86_64-pc-windows-gnu'),
1777            ('i686-pc-mingw32', 'clang', 'i686-pc-windows-gnu'),
1778            ('x86_64-pc-mingw32', 'clang', 'x86_64-pc-windows-gnu'),
1779            ('i686-w64-mingw32', 'clang', 'i686-pc-windows-gnu'),
1780            ('x86_64-w64-mingw32', 'clang', 'x86_64-pc-windows-gnu'),
1781            ('aarch64-windows-mingw32', 'clang-cl', 'aarch64-pc-windows-msvc'),
1782        ):
1783            self.assertEqual(self.get_rust_target(autoconf, building_with_gcc), rust)
1784
1785        # Arm special cases
1786        self.assertEqual(
1787            self.get_rust_target('arm-unknown-linux-androideabi',
1788                                 arm_target=ReadOnlyNamespace(
1789                                     arm_arch=7, fpu='neon', thumb2=True, float_abi='softfp')),
1790            'thumbv7neon-linux-androideabi')
1791
1792        self.assertEqual(
1793            self.get_rust_target('arm-unknown-linux-androideabi',
1794                                 arm_target=ReadOnlyNamespace(
1795                                     arm_arch=7, fpu='neon', thumb2=False, float_abi='softfp')),
1796            'armv7-linux-androideabi')
1797
1798        self.assertEqual(
1799            self.get_rust_target('arm-unknown-linux-androideabi',
1800                                 arm_target=ReadOnlyNamespace(
1801                                     arm_arch=7, fpu='vfpv2', thumb2=True, float_abi='softfp')),
1802            'armv7-linux-androideabi')
1803
1804        self.assertEqual(
1805            self.get_rust_target('armv7-unknown-linux-gnueabihf',
1806                                 arm_target=ReadOnlyNamespace(
1807                                     arm_arch=7, fpu='neon', thumb2=True, float_abi='hard')),
1808            'thumbv7neon-unknown-linux-gnueabihf')
1809
1810        self.assertEqual(
1811            self.get_rust_target('armv7-unknown-linux-gnueabihf',
1812                                 arm_target=ReadOnlyNamespace(
1813                                     arm_arch=7, fpu='neon', thumb2=False, float_abi='hard')),
1814            'armv7-unknown-linux-gnueabihf')
1815
1816        self.assertEqual(
1817            self.get_rust_target('armv7-unknown-linux-gnueabihf',
1818                                 arm_target=ReadOnlyNamespace(
1819                                     arm_arch=7, fpu='vfpv2', thumb2=True, float_abi='hard')),
1820            'armv7-unknown-linux-gnueabihf')
1821
1822        self.assertEqual(
1823            self.get_rust_target('arm-unknown-freebsd13.0-gnueabihf',
1824                                 arm_target=ReadOnlyNamespace(
1825                                     arm_arch=7, fpu='vfpv2', thumb2=True, float_abi='hard')),
1826            'armv7-unknown-freebsd')
1827
1828        self.assertEqual(
1829            self.get_rust_target('arm-unknown-freebsd13.0-gnueabihf',
1830                                 arm_target=ReadOnlyNamespace(
1831                                     arm_arch=6, fpu=None, thumb2=False, float_abi='hard')),
1832            'armv6-unknown-freebsd')
1833
1834        self.assertEqual(
1835            self.get_rust_target('arm-unknown-linux-gnueabi',
1836                                 arm_target=ReadOnlyNamespace(
1837                                     arm_arch=4, fpu=None, thumb2=False, float_abi='softfp')),
1838            'armv4t-unknown-linux-gnueabi')
1839
1840
1841if __name__ == '__main__':
1842    main()
1843