Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test the cargo args generated by bootstrap.py #112281

Merged
merged 6 commits into from
Jun 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 79 additions & 67 deletions src/bootstrap/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,23 +458,51 @@ def unpack_component(download_info):
verbose=download_info.verbose,
)

class RustBuild(object):
"""Provide all the methods required to build Rust"""
class FakeArgs:
"""Used for unit tests to avoid updating all call sites"""
def __init__(self):
self.checksums_sha256 = {}
self.stage0_compiler = None
self.download_url = ''
self.build = ''
self.build_dir = ''
self.clean = False
self.config_toml = ''
self.rust_root = ''
self.use_locked_deps = False
self.use_vendored_sources = False
self.verbose = False
self.json_output = False
self.color = 'auto'
self.warnings = 'default'

class RustBuild(object):
"""Provide all the methods required to build Rust"""
def __init__(self, config_toml="", args=FakeArgs()):
self.git_version = None
self.nix_deps_dir = None
self._should_fix_bins_and_dylibs = None
self.rust_root = os.path.abspath(os.path.join(__file__, '../../..'))

self.config_toml = config_toml

self.clean = args.clean
self.json_output = args.json_output
self.verbose = args.verbose
self.color = args.color
self.warnings = args.warnings

config_verbose_count = self.get_toml('verbose', 'build')
if config_verbose_count is not None:
self.verbose = max(self.verbose, int(config_verbose_count))

self.use_vendored_sources = self.get_toml('vendor', 'build') == 'true'
self.use_locked_deps = self.get_toml('locked-deps', 'build') == 'true'

build_dir = args.build_dir or self.get_toml('build-dir', 'build') or 'build'
self.build_dir = os.path.abspath(build_dir)

with open(os.path.join(self.rust_root, "src", "stage0.json")) as f:
data = json.load(f)
self.checksums_sha256 = data["checksums_sha256"]
self.stage0_compiler = Stage0Toolchain(data["compiler"])
self.download_url = os.getenv("RUSTUP_DIST_SERVER") or data["config"]["dist_server"]

self.build = args.build or self.build_triple()


def download_toolchain(self):
"""Fetch the build system for Rust, written in Rust
Expand Down Expand Up @@ -704,9 +732,10 @@ def rustc_stamp(self):
"""Return the path for .rustc-stamp at the given stage

>>> rb = RustBuild()
>>> rb.build = "host"
>>> rb.build_dir = "build"
>>> rb.rustc_stamp() == os.path.join("build", "stage0", ".rustc-stamp")
True
>>> expected = os.path.join("build", "host", "stage0", ".rustc-stamp")
>>> assert rb.rustc_stamp() == expected, rb.rustc_stamp()
"""
return os.path.join(self.bin_root(), '.rustc-stamp')

Expand All @@ -721,15 +750,9 @@ def bin_root(self):
"""Return the binary root directory for the given stage

>>> rb = RustBuild()
>>> rb.build_dir = "build"
>>> rb.bin_root() == os.path.join("build", "stage0")
True

When the 'build' property is given should be a nested directory:

>>> rb.build = "devel"
>>> rb.bin_root() == os.path.join("build", "devel", "stage0")
True
>>> expected = os.path.abspath(os.path.join("build", "devel", "stage0"))
>>> assert rb.bin_root() == expected, rb.bin_root()
"""
subdir = "stage0"
return os.path.join(self.build_dir, self.build, subdir)
Expand Down Expand Up @@ -761,9 +784,12 @@ def get_toml(self, key, section=None):
>>> rb.get_toml("key1")
'true'
"""
return RustBuild.get_toml_static(self.config_toml, key, section)

@staticmethod
def get_toml_static(config_toml, key, section=None):
cur_section = None
for line in self.config_toml.splitlines():
for line in config_toml.splitlines():
section_match = re.match(r'^\s*\[(.*)\]\s*$', line)
if section_match is not None:
cur_section = section_match.group(1)
Expand All @@ -772,7 +798,7 @@ def get_toml(self, key, section=None):
if match is not None:
value = match.group(1)
if section is None or section == cur_section:
return self.get_string(value) or value.strip()
return RustBuild.get_string(value) or value.strip()
return None

def cargo(self):
Expand Down Expand Up @@ -835,13 +861,23 @@ def bootstrap_binary(self):
"""
return os.path.join(self.build_dir, "bootstrap", "debug", "bootstrap")

def build_bootstrap(self, color, warnings, verbose_count):
def build_bootstrap(self):
"""Build bootstrap"""
env = os.environ.copy()
if "GITHUB_ACTIONS" in env:
print("::group::Building bootstrap")
else:
print("Building bootstrap", file=sys.stderr)

args = self.build_bootstrap_cmd(env)
# Run this from the source directory so cargo finds .cargo/config
run(args, env=env, verbose=self.verbose, cwd=self.rust_root)

if "GITHUB_ACTIONS" in env:
print("::endgroup::")

def build_bootstrap_cmd(self, env):
"""For tests."""
build_dir = os.path.join(self.build_dir, "bootstrap")
if self.clean and os.path.exists(build_dir):
shutil.rmtree(build_dir)
Expand Down Expand Up @@ -894,10 +930,10 @@ def build_bootstrap(self, color, warnings, verbose_count):
if target_linker is not None:
env["RUSTFLAGS"] += " -C linker=" + target_linker
env["RUSTFLAGS"] += " -Wrust_2018_idioms -Wunused_lifetimes"
if warnings == "default":
if self.warnings == "default":
deny_warnings = self.get_toml("deny-warnings", "rust") != "false"
else:
deny_warnings = warnings == "deny"
deny_warnings = self.warnings == "deny"
if deny_warnings:
env["RUSTFLAGS"] += " -Dwarnings"

Expand All @@ -908,7 +944,7 @@ def build_bootstrap(self, color, warnings, verbose_count):
self.cargo()))
args = [self.cargo(), "build", "--manifest-path",
os.path.join(self.rust_root, "src/bootstrap/Cargo.toml")]
args.extend("--verbose" for _ in range(verbose_count))
args.extend("--verbose" for _ in range(self.verbose))
if self.use_locked_deps:
args.append("--locked")
if self.use_vendored_sources:
Expand All @@ -918,20 +954,16 @@ def build_bootstrap(self, color, warnings, verbose_count):
args.append("build-metrics")
if self.json_output:
args.append("--message-format=json")
if color == "always":
if self.color == "always":
args.append("--color=always")
elif color == "never":
elif self.color == "never":
args.append("--color=never")
try:
args += env["CARGOFLAGS"].split()
except KeyError:
pass

# Run this from the source directory so cargo finds .cargo/config
run(args, env=env, verbose=self.verbose, cwd=self.rust_root)

if "GITHUB_ACTIONS" in env:
print("::endgroup::")
return args

def build_triple(self):
"""Build triple as in LLVM
Expand Down Expand Up @@ -981,7 +1013,7 @@ def check_vendored_status(self):
if os.path.exists(cargo_dir):
shutil.rmtree(cargo_dir)

def parse_args():
def parse_args(args):
"""Parse the command line arguments that the python script needs."""
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('-h', '--help', action='store_true')
Expand All @@ -994,16 +1026,11 @@ def parse_args():
parser.add_argument('--warnings', choices=['deny', 'warn', 'default'], default='default')
parser.add_argument('-v', '--verbose', action='count', default=0)

return parser.parse_known_args(sys.argv)[0]
return parser.parse_known_args(args)[0]

def bootstrap(args):
"""Configure, fetch, build and run the initial bootstrap"""
# Configure initial bootstrap
build = RustBuild()
build.rust_root = os.path.abspath(os.path.join(__file__, '../../..'))
build.verbose = args.verbose != 0
build.clean = args.clean
build.json_output = args.json_output
rust_root = os.path.abspath(os.path.join(__file__, '../../..'))

# Read from `--config`, then `RUST_BOOTSTRAP_CONFIG`, then `./config.toml`,
# then `config.toml` in the root directory.
Expand All @@ -1012,52 +1039,37 @@ def bootstrap(args):
if using_default_path:
toml_path = 'config.toml'
if not os.path.exists(toml_path):
toml_path = os.path.join(build.rust_root, toml_path)
toml_path = os.path.join(rust_root, toml_path)

# Give a hard error if `--config` or `RUST_BOOTSTRAP_CONFIG` are set to a missing path,
# but not if `config.toml` hasn't been created.
if not using_default_path or os.path.exists(toml_path):
with open(toml_path) as config:
build.config_toml = config.read()
config_toml = config.read()
else:
config_toml = ''

profile = build.get_toml('profile')
profile = RustBuild.get_toml_static(config_toml, 'profile')
if profile is not None:
include_file = 'config.{}.toml'.format(profile)
include_dir = os.path.join(build.rust_root, 'src', 'bootstrap', 'defaults')
include_dir = os.path.join(rust_root, 'src', 'bootstrap', 'defaults')
include_path = os.path.join(include_dir, include_file)
# HACK: This works because `build.get_toml()` returns the first match it finds for a
# HACK: This works because `self.get_toml()` returns the first match it finds for a
# specific key, so appending our defaults at the end allows the user to override them
with open(include_path) as included_toml:
build.config_toml += os.linesep + included_toml.read()

verbose_count = args.verbose
config_verbose_count = build.get_toml('verbose', 'build')
if config_verbose_count is not None:
verbose_count = max(args.verbose, int(config_verbose_count))

build.use_vendored_sources = build.get_toml('vendor', 'build') == 'true'
build.use_locked_deps = build.get_toml('locked-deps', 'build') == 'true'
config_toml += os.linesep + included_toml.read()

# Configure initial bootstrap
build = RustBuild(config_toml, args)
build.check_vendored_status()

build_dir = args.build_dir or build.get_toml('build-dir', 'build') or 'build'
build.build_dir = os.path.abspath(build_dir)

with open(os.path.join(build.rust_root, "src", "stage0.json")) as f:
data = json.load(f)
build.checksums_sha256 = data["checksums_sha256"]
build.stage0_compiler = Stage0Toolchain(data["compiler"])
build.download_url = os.getenv("RUSTUP_DIST_SERVER") or data["config"]["dist_server"]

build.build = args.build or build.build_triple()

if not os.path.exists(build.build_dir):
os.makedirs(build.build_dir)

# Fetch/build the bootstrap
build.download_toolchain()
sys.stdout.flush()
build.build_bootstrap(args.color, args.warnings, verbose_count)
build.build_bootstrap()
sys.stdout.flush()

# Run the bootstrap
Expand All @@ -1077,7 +1089,7 @@ def main():
if len(sys.argv) > 1 and sys.argv[1] == 'help':
sys.argv[1] = '-h'

args = parse_args()
args = parse_args(sys.argv)
help_triggered = args.help or len(sys.argv) == 1

# If the user is asking for help, let them know that the whole download-and-build
Expand Down
Loading