mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-07 09:13:07 +03:00
e6f5186dc3
This has two major advantages. First, it makes us less dependent on the rustc shim, which is nice but not super important. More importantly, it gives us much nicer caching properties. Previously, `x build --warnings warn && x build --warnings deny` would rebuild all of bootstrap, and if there were any warnings emitted on the last build of the compiler, they would *not* fail the build, because cargo would cache the output rather than rerunning the shim. After this change, bootstrap rebuilds instantly, and cargo knows that it should fail the build but *without* invalidating the cache.
254 lines
10 KiB
Python
254 lines
10 KiB
Python
"""Bootstrap tests
|
|
|
|
Run these with `x test bootstrap`, or `python -m unittest src/bootstrap/bootstrap_test.py`."""
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
import os
|
|
import unittest
|
|
from unittest.mock import patch
|
|
import tempfile
|
|
import hashlib
|
|
import sys
|
|
|
|
from shutil import rmtree
|
|
|
|
# Allow running this from the top-level directory.
|
|
bootstrap_dir = os.path.dirname(os.path.abspath(__file__))
|
|
# For the import below, have Python search in src/bootstrap first.
|
|
sys.path.insert(0, bootstrap_dir)
|
|
import bootstrap # noqa: E402
|
|
import configure # noqa: E402
|
|
|
|
|
|
def serialize_and_parse(configure_args, bootstrap_args=None):
|
|
from io import StringIO
|
|
|
|
if bootstrap_args is None:
|
|
bootstrap_args = bootstrap.FakeArgs()
|
|
|
|
section_order, sections, targets = configure.parse_args(configure_args)
|
|
buffer = StringIO()
|
|
configure.write_config_toml(buffer, section_order, targets, sections)
|
|
build = bootstrap.RustBuild(config_toml=buffer.getvalue(), args=bootstrap_args)
|
|
|
|
try:
|
|
import tomllib
|
|
|
|
# Verify this is actually valid TOML.
|
|
tomllib.loads(build.config_toml)
|
|
except ImportError:
|
|
print(
|
|
"WARNING: skipping TOML validation, need at least python 3.11",
|
|
file=sys.stderr,
|
|
)
|
|
return build
|
|
|
|
|
|
class VerifyTestCase(unittest.TestCase):
|
|
"""Test Case for verify"""
|
|
|
|
def setUp(self):
|
|
self.container = tempfile.mkdtemp()
|
|
self.src = os.path.join(self.container, "src.txt")
|
|
self.bad_src = os.path.join(self.container, "bad.txt")
|
|
content = "Hello world"
|
|
|
|
self.expected = hashlib.sha256(content.encode("utf-8")).hexdigest()
|
|
|
|
with open(self.src, "w") as src:
|
|
src.write(content)
|
|
with open(self.bad_src, "w") as bad:
|
|
bad.write("Hello!")
|
|
|
|
def tearDown(self):
|
|
rmtree(self.container)
|
|
|
|
def test_valid_file(self):
|
|
"""Check if the sha256 sum of the given file is valid"""
|
|
self.assertTrue(bootstrap.verify(self.src, self.expected, False))
|
|
|
|
def test_invalid_file(self):
|
|
"""Should verify that the file is invalid"""
|
|
self.assertFalse(bootstrap.verify(self.bad_src, self.expected, False))
|
|
|
|
|
|
class ProgramOutOfDate(unittest.TestCase):
|
|
"""Test if a program is out of date"""
|
|
|
|
def setUp(self):
|
|
self.container = tempfile.mkdtemp()
|
|
os.mkdir(os.path.join(self.container, "stage0"))
|
|
self.build = bootstrap.RustBuild()
|
|
self.build.date = "2017-06-15"
|
|
self.build.build_dir = self.container
|
|
self.rustc_stamp_path = os.path.join(self.container, "stage0", ".rustc-stamp")
|
|
self.key = self.build.date + str(None)
|
|
|
|
def tearDown(self):
|
|
rmtree(self.container)
|
|
|
|
def test_stamp_path_does_not_exist(self):
|
|
"""Return True when the stamp file does not exist"""
|
|
if os.path.exists(self.rustc_stamp_path):
|
|
os.unlink(self.rustc_stamp_path)
|
|
self.assertTrue(self.build.program_out_of_date(self.rustc_stamp_path, self.key))
|
|
|
|
def test_dates_are_different(self):
|
|
"""Return True when the dates are different"""
|
|
with open(self.rustc_stamp_path, "w") as rustc_stamp:
|
|
rustc_stamp.write("2017-06-14None")
|
|
self.assertTrue(self.build.program_out_of_date(self.rustc_stamp_path, self.key))
|
|
|
|
def test_same_dates(self):
|
|
"""Return False both dates match"""
|
|
with open(self.rustc_stamp_path, "w") as rustc_stamp:
|
|
rustc_stamp.write("2017-06-15None")
|
|
self.assertFalse(
|
|
self.build.program_out_of_date(self.rustc_stamp_path, self.key)
|
|
)
|
|
|
|
|
|
class ParseArgsInConfigure(unittest.TestCase):
|
|
"""Test if `parse_args` function in `configure.py` works properly"""
|
|
|
|
@patch("configure.err")
|
|
def test_unknown_args(self, err):
|
|
# It should be print an error message if the argument doesn't start with '--'
|
|
configure.parse_args(["enable-full-tools"])
|
|
err.assert_called_with("Option 'enable-full-tools' is not recognized")
|
|
err.reset_mock()
|
|
# It should be print an error message if the argument is not recognized
|
|
configure.parse_args(["--some-random-flag"])
|
|
err.assert_called_with("Option '--some-random-flag' is not recognized")
|
|
|
|
@patch("configure.err")
|
|
def test_need_value_args(self, err):
|
|
"""It should print an error message if a required argument value is missing"""
|
|
configure.parse_args(["--target"])
|
|
err.assert_called_with("Option '--target' needs a value (--target=val)")
|
|
|
|
@patch("configure.err")
|
|
def test_option_checking(self, err):
|
|
# Options should be checked even if `--enable-option-checking` is not passed
|
|
configure.parse_args(["--target"])
|
|
err.assert_called_with("Option '--target' needs a value (--target=val)")
|
|
err.reset_mock()
|
|
# Options should be checked if `--enable-option-checking` is passed
|
|
configure.parse_args(["--enable-option-checking", "--target"])
|
|
err.assert_called_with("Option '--target' needs a value (--target=val)")
|
|
err.reset_mock()
|
|
# Options should not be checked if `--disable-option-checking` is passed
|
|
configure.parse_args(["--disable-option-checking", "--target"])
|
|
err.assert_not_called()
|
|
|
|
@patch("configure.parse_example_config", lambda known_args, _: known_args)
|
|
def test_known_args(self):
|
|
# It should contain known and correct arguments
|
|
known_args = configure.parse_args(["--enable-full-tools"])
|
|
self.assertTrue(known_args["full-tools"][0][1])
|
|
known_args = configure.parse_args(["--disable-full-tools"])
|
|
self.assertFalse(known_args["full-tools"][0][1])
|
|
# It should contain known arguments and their values
|
|
known_args = configure.parse_args(["--target=x86_64-unknown-linux-gnu"])
|
|
self.assertEqual(known_args["target"][0][1], "x86_64-unknown-linux-gnu")
|
|
known_args = configure.parse_args(["--target", "x86_64-unknown-linux-gnu"])
|
|
self.assertEqual(known_args["target"][0][1], "x86_64-unknown-linux-gnu")
|
|
|
|
|
|
class GenerateAndParseConfig(unittest.TestCase):
|
|
"""Test that we can serialize and deserialize a bootstrap.toml file"""
|
|
|
|
def test_no_args(self):
|
|
build = serialize_and_parse([])
|
|
self.assertEqual(build.get_toml("profile"), "dist")
|
|
self.assertIsNone(build.get_toml("llvm.download-ci-llvm"))
|
|
|
|
def test_set_section(self):
|
|
build = serialize_and_parse(["--set", "llvm.download-ci-llvm"])
|
|
self.assertEqual(build.get_toml("download-ci-llvm", section="llvm"), "true")
|
|
|
|
def test_set_target(self):
|
|
build = serialize_and_parse(["--set", "target.x86_64-unknown-linux-gnu.cc=gcc"])
|
|
self.assertEqual(
|
|
build.get_toml("cc", section="target.x86_64-unknown-linux-gnu"), "gcc"
|
|
)
|
|
|
|
def test_set_top_level(self):
|
|
build = serialize_and_parse(["--set", "profile=compiler"])
|
|
self.assertEqual(build.get_toml("profile"), "compiler")
|
|
|
|
def test_set_codegen_backends(self):
|
|
build = serialize_and_parse(["--set", "rust.codegen-backends=cranelift"])
|
|
self.assertNotEqual(
|
|
build.config_toml.find("codegen-backends = ['cranelift']"), -1
|
|
)
|
|
build = serialize_and_parse(["--set", "rust.codegen-backends=cranelift,llvm"])
|
|
self.assertNotEqual(
|
|
build.config_toml.find("codegen-backends = ['cranelift', 'llvm']"), -1
|
|
)
|
|
build = serialize_and_parse(["--enable-full-tools"])
|
|
self.assertNotEqual(build.config_toml.find("codegen-backends = ['llvm']"), -1)
|
|
|
|
|
|
class BuildBootstrap(unittest.TestCase):
|
|
"""Test that we generate the appropriate arguments when building bootstrap"""
|
|
|
|
def build_args(self, configure_args=None, args=None, env=None):
|
|
if configure_args is None:
|
|
configure_args = []
|
|
if args is None:
|
|
args = []
|
|
if env is None:
|
|
env = {}
|
|
|
|
# This test ends up invoking build_bootstrap_cmd, which searches for
|
|
# the Cargo binary and errors out if it cannot be found. This is not a
|
|
# problem in most cases, but there is a scenario where it would cause
|
|
# the test to fail.
|
|
#
|
|
# When a custom local Cargo is configured in bootstrap.toml (with the
|
|
# build.cargo setting), no Cargo is downloaded to any location known by
|
|
# bootstrap, and bootstrap relies on that setting to find it.
|
|
#
|
|
# In this test though we are not using the bootstrap.toml of the caller:
|
|
# we are generating a blank one instead. If we don't set build.cargo in
|
|
# it, the test will have no way to find Cargo, failing the test.
|
|
cargo_bin = os.environ.get("BOOTSTRAP_TEST_CARGO_BIN")
|
|
if cargo_bin is not None:
|
|
configure_args += ["--set", "build.cargo=" + cargo_bin]
|
|
rustc_bin = os.environ.get("BOOTSTRAP_TEST_RUSTC_BIN")
|
|
if rustc_bin is not None:
|
|
configure_args += ["--set", "build.rustc=" + rustc_bin]
|
|
|
|
env = env.copy()
|
|
env["PATH"] = os.environ["PATH"]
|
|
|
|
parsed = bootstrap.parse_args(args)
|
|
build = serialize_and_parse(configure_args, parsed)
|
|
# Make these optional so that `python -m unittest` works when run manually.
|
|
build_dir = os.environ.get("BUILD_DIR")
|
|
if build_dir is not None:
|
|
build.build_dir = build_dir
|
|
build_platform = os.environ.get("BUILD_PLATFORM")
|
|
if build_platform is not None:
|
|
build.build = build_platform
|
|
return build.build_bootstrap_cmd(env), env
|
|
|
|
def test_cargoflags(self):
|
|
args, _ = self.build_args(env={"CARGOFLAGS": "--timings"})
|
|
self.assertTrue("--timings" in args)
|
|
|
|
def test_warnings(self):
|
|
for toml_warnings in ["false", "true", None]:
|
|
configure_args = []
|
|
if toml_warnings is not None:
|
|
configure_args = ["--set", "rust.deny-warnings=" + toml_warnings]
|
|
|
|
args, env = self.build_args(configure_args, args=["--warnings=warn"])
|
|
self.assertFalse("CARGO_BUILD_WARNINGS" in env)
|
|
self.assertFalse("-Zwarnings" in args)
|
|
|
|
args, env = self.build_args(configure_args, args=["--warnings=deny"])
|
|
self.assertEqual("deny", env["CARGO_BUILD_WARNINGS"])
|
|
self.assertTrue("-Zwarnings" in args)
|