blob: d0b409abfb0834b402044a053ad5b94538e25260 [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 tempfile
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.response_files, set())
self.assertEqual(r.emit, {})
self.assertIsNone(r.target)
self.assertFalse(r.emit_metadata)
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_args_from_response_file(self):
compiler = Path("../tools/rustc")
source = Path("../foo/lib.rs")
output = Path("foo.rlib")
with tempfile.TemporaryDirectory() as td:
tdp = Path(td)
rsp = tdp / "args.rsp"
rsp.write_text(
f"# generated by build system\n\n{source}\n-o\n{output}\n"
)
r = rustc.RustAction(_strs([compiler, f"@{rsp}"]))
self.assertEqual(r.response_files, {rsp})
self.assertEqual(r.output_file, output)
self.assertEqual(r.direct_sources, [source])
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_env_references_other_source_file(self):
r = rustc.RustAction(
[
"A=foo/other.rs",
"C=D",
"../tools/rustc",
"../foo/lib.rs",
"-o",
"foo.rlib",
]
)
self.assertEqual(r.env, ["A=foo/other.rs", "C=D"])
self.assertEqual(r.direct_sources, [Path("../foo/lib.rs")])
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_link_and_unnamed_rmeta(self):
rmeta = Path("foo.rmeta")
r = rustc.RustAction(
[
"../tools/rustc",
"--emit=metadata,link",
"../foo/lib.rs",
"-o",
"foo.rlib",
]
)
self.assertEqual(r.emit, {"link": [], "metadata": []})
self.assertEqual(r.rmeta, rmeta)
self.assertIn(rmeta, set(r.extra_output_files()))
def test_emit_link_and_named_rmeta(self):
rmeta = Path("bar.rmeta")
r = rustc.RustAction(
[
"../tools/rustc",
f"--emit=metadata={rmeta},link",
"../foo/lib.rs",
"-o",
"foo.rlib",
]
)
self.assertEqual(r.emit, {"link": [], "metadata": [str(rmeta)]})
self.assertEqual(r.rmeta, rmeta)
self.assertIn(rmeta, set(r.extra_output_files()))
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_soname(self):
soname = "foo.so"
for opts in (
[f"-Clink-arg=--soname={soname}"],
["-C", f"link-arg=--soname={soname}"],
):
r = rustc.RustAction(
["../tools/rustc"]
+ opts
+ [
"../foo/lib.rs",
"-o",
"foo.rlib",
]
)
self.assertNotIn(soname, r.link_arg_files)
self.assertEqual([], r.link_arg_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_without_metadata(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.assertFalse(r.emit_metadata)
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",
],
)
r2 = rustc.RustAction(
["../tools/rustc", "../foo/lib.rs", "-o", "foo.rlib"]
)
self.assertFalse(r2.emit_metadata)
self.assertEqual(
list(r2.dep_only_command(new_depfile)),
[
"../tools/rustc",
"../foo/lib.rs",
"-o",
"foo.rlib",
f"--emit=dep-info={new_depfile}",
"-Z",
"binary-dep-depinfo",
],
)
def test_dep_only_command_with_metadata(self):
new_depfile = "some/where/new-foo.rlib.d.other"
rmeta_output = "foo34.rmeta"
emit_opts = (
["--emit=link,metadata=unused.rmeta"],
["--emit=metadata,dep-info=not-going-to-use-this.d"],
[
"--emit=metadata,link",
"--emit=dep-info=not-going-to-use-this.d,metadata=discarded.rmeta",
],
["--emit=link,metadata,dep-info=not-going-to-use-this.d"],
[
"--emit=dep-info=not-going-to-use-this.d,metadata=ignore.rmeta,link"
],
)
for opts in emit_opts:
r = rustc.RustAction(
[
"../tools/rustc",
]
+ opts
+ ["../foo/lib.rs", "-o", "foo34.rlib"]
)
self.assertTrue(r.emit_metadata)
self.assertEqual(
list(r.dep_only_command(new_depfile)),
[
"../tools/rustc",
f"--emit=metadata,dep-info={new_depfile}",
"-Z",
"binary-dep-depinfo",
"../foo/lib.rs",
"-o",
rmeta_output,
],
)
def test_dep_only_command_no_metadata_with_externs(self):
new_depfile = "some/where/new-foo.rlib.d.other"
emit_opts = ["--emit=link"]
r = rustc.RustAction(
[
"../tools/rustc",
]
+ emit_opts
+ ["../foo/lib.rs", "-o", "foo35.rlib", "--extern", "extdep"]
)
self.assertTrue(r.emit_metadata)
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",
"foo35.rlib",
"--extern",
"extdep",
],
)
def test_dep_only_command_no_metadata_with_externs(self):
new_depfile = "some/where/new-foo.rlib.d.other"
rmeta_output = "foo36.rmeta"
emit_opts = ["--emit=link,metadata=unused.rmeta"]
r = rustc.RustAction(
[
"../tools/rustc",
]
+ emit_opts
+ ["../foo/lib.rs", "-o", "foo36.rlib", "--extern", "extdep"]
)
self.assertTrue(r.emit_metadata)
self.assertEqual(
list(r.dep_only_command(new_depfile)),
[
"../tools/rustc",
f"--emit=metadata,dep-info={new_depfile}",
"-Z",
"binary-dep-depinfo",
"../foo/lib.rs",
"-o",
rmeta_output,
"--extern",
"extdep",
],
)
def test_dep_only_command_no_metadata_with_extern_rlib(self):
new_depfile = "some/where/new-foo.rlib.d.other"
rmeta_output = "foo37.rmeta"
emit_opts = ["--emit=link,metadata=unused.rmeta"]
r = rustc.RustAction(
[
"../tools/rustc",
]
+ emit_opts
+ [
"../foo/lib.rs",
"-o",
"foo37.rlib",
"--extern",
"extdep=build/extdep.rlib",
]
)
self.assertTrue(r.emit_metadata)
with mock.patch.object(
Path, "exists", return_value=True
) as mock_rlib_exists:
dep_only_command = list(r.dep_only_command(new_depfile))
mock_rlib_exists.assert_called_once_with()
self.assertEqual(
dep_only_command,
[
"../tools/rustc",
f"--emit=metadata,dep-info={new_depfile}",
"-Z",
"binary-dep-depinfo",
"../foo/lib.rs",
"-o",
rmeta_output,
"--extern",
"extdep=build/extdep.rmeta", # transformed to .rmeta
],
)
def test_dep_only_command_no_metadata_with_extern_proc_macro(self):
new_depfile = "some/where/new-foo.rlib.d.other"
rmeta_output = "foo38.rmeta"
emit_opts = ["--emit=link,metadata=unused.rmeta"]
r = rustc.RustAction(
[
"../tools/rustc",
]
+ emit_opts
+ [
"../foo/lib.rs",
"-o",
"foo38.rlib",
"--extern",
"extdep=build/extdep.so",
]
)
self.assertTrue(r.emit_metadata)
dep_only_command = list(r.dep_only_command(new_depfile))
self.assertEqual(
dep_only_command,
[
"../tools/rustc",
f"--emit=metadata,dep-info={new_depfile}",
"-Z",
"binary-dep-depinfo",
"../foo/lib.rs",
"-o",
rmeta_output,
"--extern",
"extdep=build/extdep.so", # preserved as .so
],
)
if __name__ == "__main__":
unittest.main()