blob: d6ba42c8014e3a06b05294cee9b1fc9cbb70c106 [file] [log] [blame]
#!/usr/bin/env fuchsia-vendored-python
# Copyright 2022 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import unittest
import sys
from unittest import mock
from pathlib import Path
import rustc
import cl_utils
from typing import Any, Sequence
def _strs(items: Sequence[Any]) -> Sequence[str]:
return [str(i) for i in items]
def _paths(items: Sequence[Any]) -> Sequence[Path]:
if isinstance(items, list):
return [Path(i) for i in items]
elif isinstance(items, set):
return {Path(i) for i in items}
elif isinstance(items, tuple):
return tuple(Path(i) for i in items)
t = type(items)
raise TypeError(f"Unhandled sequence type: {t}")
class RustActionTests(unittest.TestCase):
def test_simple(self):
compiler = Path('../tools/rustc')
source = Path('../foo/lib.rs')
output = Path('foo.rlib')
r = rustc.RustAction(_strs([compiler, source, '-o', output]))
self.assertEqual(r.compiler, compiler)
self.assertEqual(r.env, [])
self.assertEqual(r.crate_type, rustc.CrateType.UNKNOWN)
self.assertEqual(r.output_file, output)
self.assertEqual(r._output_file_base, 'foo')
self.assertEqual(r._auxiliary_output_path, 'foo')
self.assertEqual(r.direct_sources, [source])
self.assertEqual(r.emit, {})
self.assertIsNone(r.target)
self.assertFalse(r.emit_llvm_ir)
self.assertFalse(r.emit_llvm_bc)
self.assertFalse(r.save_analysis)
self.assertFalse(r.llvm_time_trace)
self.assertEqual(r.extra_filename, '')
self.assertIsNone(r.depfile)
self.assertIsNone(r.linker)
self.assertEqual(r.link_arg_files, [])
self.assertIsNone(r.link_map_output)
self.assertIsNone(r.c_sysroot)
self.assertIsNone(r.use_ld)
self.assertEqual(r.native, [])
self.assertEqual(r.explicit_link_arg_files, [])
self.assertEqual(r.externs, {})
self.assertEqual(set(r.extern_paths()), set())
def test_executable_suffixed(self):
compiler = Path('../tools/rustc')
source = Path('../foo/lib.rs')
for output in (Path('bin/foo.exe'), Path('bin/foo')):
r = rustc.RustAction(
_strs([compiler, source, '--crate-type=bin', '-o', output]))
self.assertEqual(r.compiler, compiler)
self.assertEqual(r.env, [])
self.assertEqual(r.crate_type, rustc.CrateType.BINARY)
self.assertEqual(r.output_file, output)
self.assertEqual(r._output_file_base, 'bin/foo')
self.assertEqual(r._auxiliary_output_path, 'bin/foo')
self.assertEqual(r.direct_sources, [source])
def test_no_source(self):
compiler = Path('../tools/bin/rustc')
output = Path('foo.rlib')
r = rustc.RustAction(_strs([compiler, '-o', output]))
self.assertEqual(r.direct_sources, [])
def test_no_output(self):
compiler = Path('../tools/bin/rustc')
source = Path('../foo/lib.rs')
r = rustc.RustAction(_strs([compiler, source]))
with self.assertRaises(RuntimeError):
base = r._output_file_base
def test_do_not_help(self):
compiler = Path('../tools/rustc')
source = Path('../foo/lib.rs')
output = Path('foo.rlib')
for opt in ('-h', '--help', '-halp-ignore=foo'):
with mock.patch.object(sys, 'exit') as mock_exit:
r = rustc.RustAction(
_strs([compiler, opt, source, '-o', output]))
self.assertEqual(r.compiler, compiler)
mock_exit.assert_not_called()
def test_env(self):
r = rustc.RustAction(
['A=B', 'C=D', '../tools/rustc', '../foo/lib.rs', '-o', 'foo.rlib'])
self.assertEqual(r.env, ['A=B', 'C=D'])
def test_crate_type(self):
cases = [
("rlib", rustc.CrateType.RLIB, False, False),
("bin", rustc.CrateType.BINARY, True, True),
("cdylib", rustc.CrateType.CDYLIB, True, True),
("dylib", rustc.CrateType.DYLIB, True, True),
("proc-macro", rustc.CrateType.PROC_MACRO, True, False),
("staticlib", rustc.CrateType.STATICLIB, False, False),
]
for k, v, needs_linker, is_executable in cases:
r = rustc.RustAction(
['../tools/rustc', '--crate-type', k, '-o', 'foo.rlib'])
self.assertEqual(r.crate_type, v)
self.assertEqual(r.needs_linker, needs_linker)
self.assertEqual(r.main_output_is_executable, is_executable)
def test_emit_link(self):
r = rustc.RustAction(
[
'../tools/rustc', '--emit=link', '../foo/lib.rs', '-o',
'foo.rlib'
])
self.assertEqual(r.emit, {'link': []})
def test_emit_depfile(self):
depfile = Path('foo.rlib.d')
r = rustc.RustAction(
[
'../tools/rustc', f'--emit=dep-info={depfile}', '../foo/lib.rs',
'-o', 'foo.rlib'
])
self.assertEqual(r.emit, {'dep-info': [str(depfile)]})
self.assertEqual(r.depfile, depfile)
def test_emit_link_and_depfile(self):
depfile = Path('foo.rlib.d')
r = rustc.RustAction(
[
'../tools/rustc', f'--emit=dep-info={depfile},link',
'../foo/lib.rs', '-o', 'foo.rlib'
])
self.assertEqual(r.emit, {'link': [], 'dep-info': [str(depfile)]})
self.assertEqual(r.depfile, depfile)
def test_emit_llvm_ir(self):
output = Path('obj/foo.rlib')
r = rustc.RustAction(
[
'../tools/rustc', '--emit=llvm-ir', '../foo/lib.rs', '-o',
str(output)
])
self.assertTrue(r.emit_llvm_ir)
self.assertFalse(r.emit_llvm_bc)
self.assertEqual(r.output_file, output)
self.assertIn(Path('obj/foo.ll'), set(r.extra_output_files()))
def test_emit_llvm_bc(self):
output = Path('obj/foo.rlib')
r = rustc.RustAction(
[
'../tools/rustc', '--emit=llvm-bc', '../foo/lib.rs', '-o',
str(output)
])
self.assertTrue(r.emit_llvm_bc)
self.assertFalse(r.emit_llvm_ir)
self.assertEqual(r.output_file, output)
self.assertIn(Path('obj/foo.bc'), set(r.extra_output_files()))
def test_emit_llvm_ir_bc_extra_filename(self):
output = Path('obj/foo.rlib')
r = rustc.RustAction(
[
'../tools/rustc', '--emit=llvm-bc,llvm-ir', '../foo/lib.rs',
'-o',
str(output), '-C', 'extra-filename=-feedbeef'
])
self.assertTrue(r.emit_llvm_bc)
self.assertTrue(r.emit_llvm_ir)
self.assertEqual(r.output_file, output)
self.assertEqual(
_paths({'obj/foo-feedbeef.bc', 'obj/foo-feedbeef.ll'}),
set(r.extra_output_files()))
def test_c_sysroot(self):
c_sysroot = Path('../path/to/platform/sysroot')
r = rustc.RustAction(
[
'../tools/rustc', f'-Clink-arg=--sysroot={c_sysroot}',
'../foo/lib.rs', '-o', 'foo.rlib'
])
self.assertEqual(r.c_sysroot, c_sysroot)
def test_rust_sysroot(self):
rust_sysroot = Path('../path/to/rust/sysroot')
r = rustc.RustAction(
[
'../tools/rustc', '--sysroot',
str(rust_sysroot), '../foo/lib.rs', '-o', 'foo.rlib'
])
self.assertEqual(r.rust_sysroot, rust_sysroot)
def test_fuse_ld(self):
ld = Path('gold')
r = rustc.RustAction(
[
'../tools/rustc', f'-Clink-arg=-fuse-ld={ld}', '../foo/lib.rs',
'-o', 'foo.rlib'
])
self.assertEqual(r.use_ld, ld)
def test_rust_sysroot_default(self):
working_dir = Path('/home/project/build')
fake_default_sysroot = Path('/home/project/tools/fake/sysroot')
compiler = Path('../tools/rustc')
r = rustc.RustAction(
[str(compiler), '../foo/lib.rs', '-o', 'foo.rlib'],
working_dir=working_dir)
call_result = cl_utils.SubprocessResult(
returncode=0,
stdout=[f'{fake_default_sysroot}\n'],
)
with mock.patch.object(cl_utils, 'subprocess_call',
return_value=call_result) as mock_call:
self.assertEqual(
r.default_rust_sysroot(), Path('../tools/fake/sysroot'))
mock_call.assert_called_with(
[str(compiler), '--print', 'sysroot'], cwd=working_dir, quiet=True)
with mock.patch.object(
rustc.RustAction, 'default_rust_sysroot',
return_value=fake_default_sysroot) as mock_default:
self.assertEqual(r.rust_sysroot, fake_default_sysroot)
mock_default.assert_called_with()
def test_target(self):
target = f'--target=powerpc32-apple-darwin8'
r = rustc.RustAction(
[
'../tools/rustc', f'--target={target}', '../foo/lib.rs', '-o',
'foo.rlib'
])
self.assertEqual(r.target, target)
def test_save_analysis(self):
for opts in (
['-Zsave-analysis=yes'],
['-Z', 'save-analysis=yes'],
):
r = rustc.RustAction(
['../tools/rustc'] + opts +
['../foo/lib.rs', '-o', 'obj/foo.rlib'])
self.assertTrue(r.save_analysis)
self.assertIn(
Path('save-analysis-temp/foo.json'),
set(r.extra_output_files()))
def test_llvm_time_trace(self):
for opts in (
['-Zllvm-time-trace'],
['-Z', 'llvm-time-trace'],
):
r = rustc.RustAction(
['../tools/rustc'] + opts +
['../foo/lib.rs', '-o', 'obj/foo.rlib'])
self.assertTrue(r.llvm_time_trace)
self.assertIn(
Path('obj/foo.llvm_timings.json'), set(r.extra_output_files()))
def test_extra_filename(self):
suffix = 'cafef00d'
for opts in (
[f'-Cextra-filename={suffix}'],
['-C', f'extra-filename={suffix}'],
):
r = rustc.RustAction(
['../tools/rustc'] + opts + ['../foo/lib.rs', '-o', 'foo.rlib'])
self.assertEqual(r.extra_filename, suffix)
def test_linker(self):
linker = Path('../path/to/linky-mc-link-face')
for opts in (
[f'-Clinker={linker}'],
['-C', f'linker={linker}'],
):
r = rustc.RustAction(
['../tools/rustc'] + opts + ['../foo/lib.rs', '-o', 'foo.rlib'])
self.assertEqual(r.linker, linker)
def test_link_arg_files(self):
link_args = _paths(['obj/foo/this.so', 'obj/bar/that.so'])
for opts in (
[f'-Clink-arg={link_args[0]}', f'-Clink-arg={link_args[1]}'],
['-C', f'link-arg={link_args[0]}', '-C',
f'link-arg={link_args[1]}'],
):
r = rustc.RustAction(
['../tools/rustc'] + opts + ['../foo/lib.rs', '-o', 'foo.rlib'])
self.assertEqual(r.link_arg_files, link_args)
def test_link_map_output(self):
r = rustc.RustAction(
[
'../tools/rustc', '../foo/lib.rs', '-o', 'foo.rlib',
'-Clink-args=--Map=./obj/foo.rlib.map'
])
map_file = Path('obj/foo.rlib.map')
self.assertEqual(r.link_map_output, map_file)
self.assertIn(map_file, set(r.extra_output_files()))
def test_direct_inputs(self):
input1 = Path('obj/foo/this.a')
input2 = Path('../foo/lib.rs')
input3 = Path('obj/bar/that.so')
input4 = Path('obj/other/foo.dylib')
input5 = Path('obj/other/bar.so.debug')
input6 = Path('foo.ld')
output = Path('foo.rlib')
r = rustc.RustAction(
_strs(
[
'../tools/rustc',
input1,
input2,
input3,
'-o',
output,
input4,
input5,
input6,
]))
self.assertEqual(r.direct_sources, [input2])
self.assertEqual(
r.explicit_link_arg_files, [input1, input3, input4, input5, input6])
def test_Lnative_flag(self):
link_paths = _paths(['obj/foo/here', 'obj/bar/there'])
for opts in (
[f'-Lnative={link_paths[0]}', f'-Lnative={link_paths[1]}'],
['-L', f'native={link_paths[0]}', '-L', f'native={link_paths[1]}'],
):
r = rustc.RustAction(
['../tools/rustc'] + opts + ['../foo/lib.rs', '-o', 'foo.rlib'])
self.assertEqual(r.native, link_paths)
def test_externs(self):
libdir1 = Path('path/to/this_lib')
libdir2 = Path('path/to/that_lib')
r = rustc.RustAction(
[
'../tools/rustc', '--extern', f'this_lib={libdir1}',
'../foo/lib.rs', '--extern', f'that_lib={libdir2}', '-o',
'foo.rlib'
])
self.assertEqual(
r.externs, {
'this_lib': libdir1,
'that_lib': libdir2,
})
self.assertEqual(
set(r.extern_paths()),
_paths({'path/to/this_lib', 'path/to/that_lib'}))
def test_externs_last_wins(self):
libdir1 = Path('path/to/this_lib')
libdir2 = Path('path/to/that_lib')
r = rustc.RustAction(
[
'../tools/rustc', '--extern', f'this_lib={libdir1}',
'../foo/lib.rs', '--extern', f'this_lib={libdir2}', '-o',
'foo.rlib'
])
self.assertEqual(r.externs, {
'this_lib': libdir2,
})
self.assertEqual(set(r.extern_paths()), _paths({'path/to/that_lib'}))
def test_dep_only_command(self):
new_depfile = 'some/where/new-foo.rlib.d.other'
emit_opts = (
['--emit=link'],
['--emit=dep-info=not-going-to-use-this.d'],
['--emit=link', '--emit=dep-info=not-going-to-use-this.d'],
['--emit=link,dep-info=not-going-to-use-this.d'],
['--emit=dep-info=not-going-to-use-this.d,link'],
)
for opts in emit_opts:
r = rustc.RustAction(
[
'../tools/rustc',
] + opts + ['../foo/lib.rs', '-o', 'foo.rlib'])
self.assertEqual(
list(r.dep_only_command(new_depfile)), [
'../tools/rustc', f'--emit=dep-info={new_depfile}', '-Z',
'binary-dep-depinfo', '../foo/lib.rs', '-o', 'foo.rlib'
])
r = rustc.RustAction(
['../tools/rustc', '../foo/lib.rs', '-o', 'foo.rlib'])
self.assertEqual(
list(r.dep_only_command(new_depfile)), [
'../tools/rustc', '../foo/lib.rs', '-o', 'foo.rlib',
f'--emit=dep-info={new_depfile}', '-Z', 'binary-dep-depinfo'
])
if __name__ == '__main__':
unittest.main()