aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBob Haarman <inglorion@chromium.org>2020-12-15 19:28:45 +0000
committerCommit Bot <commit-bot@chromium.org>2021-01-06 23:40:08 +0000
commit9f99f6cc358c315eb5cffbf4614852bcd7ff7ced (patch)
treeaafe904ad9707dded07f59315de5ebbd72690f0e
parentd82842027768f07690a2ace2b6b387e929a61bfd (diff)
downloadtoolchain-utils-9f99f6cc358c315eb5cffbf4614852bcd7ff7ced.tar.gz
rust_uprev: also uprev rust-bootstrap
Since crrev.com/c/2436432, dev-lang/rust requires dev-lang/rust-bootstrap to build. Because of this, rust uprevs now require that rust-bootstrap be uprevved, too. This CL changes the rust_uprev script to do so. BUG=chromium:1159066 TEST=python3 ./rust_tools/rust_uprev_test.py Change-Id: I48482e780d9ebbc012142687f2edfcc8f83e9d56 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/2597493 Commit-Queue: Bob Haarman <inglorion@chromium.org> Tested-by: Bob Haarman <inglorion@chromium.org> Reviewed-by: George Burgess <gbiv@chromium.org>
-rwxr-xr-xrust_tools/rust_uprev.py320
-rwxr-xr-xrust_tools/rust_uprev_test.py321
2 files changed, 283 insertions, 358 deletions
diff --git a/rust_tools/rust_uprev.py b/rust_tools/rust_uprev.py
index b4020ea1..6d112420 100755
--- a/rust_tools/rust_uprev.py
+++ b/rust_tools/rust_uprev.py
@@ -36,7 +36,6 @@ See `--help` for all available options.
# pylint: disable=cros-logging-import
import argparse
-import glob
import pathlib
import json
import logging
@@ -45,11 +44,12 @@ import re
import shutil
import subprocess
import sys
-import tempfile
+from pathlib import Path
from typing import Any, Callable, Dict, List, NamedTuple, Optional, T, Tuple
from llvm_tools import chroot, git
-RUST_PATH = '/mnt/host/source/src/third_party/chromiumos-overlay/dev-lang/rust'
+RUST_PATH = Path(
+ '/mnt/host/source/src/third_party/chromiumos-overlay/dev-lang/rust')
def get_command_output(command: List[str], *args, **kwargs) -> str:
@@ -92,20 +92,33 @@ class RustVersion(NamedTuple):
int(m.group('major')), int(m.group('minor')), int(m.group('patch')))
-def find_virtual_rust_ebuild(version: RustVersion) -> str:
- """Finds the virtual/rust ebuild for a given RustVersion.
+def find_ebuild_path(directory: Path,
+ name: str,
+ version: Optional[RustVersion] = None) -> Path:
+ """Finds an ebuild in a directory.
- This finds 1.2.3 and also 1.2.3-r4. It expects that there will
- be exactly one match, and will assert if that is not the case.
+ Returns the path to the ebuild file. Asserts if there is not
+ exactly one match. The match is constrained by name and optionally
+ by version, but can match any patch level. E.g. "rust" version
+ 1.3.4 can match rust-1.3.4.ebuild but also rust-1.3.4-r6.ebuild.
"""
- virtual_rust_dir = os.path.join(RUST_PATH, '../../virtual/rust')
- pattern = os.path.join(virtual_rust_dir, f'rust-{version}*.ebuild')
- matches = glob.glob(pattern)
- # We expect exactly one match.
+ if version:
+ pattern = '%s-%s*.ebuild' % (name, version)
+ else:
+ pattern = '%s-*.ebuild' % (name,)
+ matches = list(Path(directory).glob(pattern))
assert len(matches) == 1, matches
return matches[0]
+def get_rust_bootstrap_version():
+ """Get the version of the current rust-bootstrap package."""
+ bootstrap_ebuild = find_ebuild_path(rust_bootstrap_path(), 'rust-bootstrap')
+ m = re.match(r'^rust-bootstrap-(\d+).(\d+).(\d+)', bootstrap_ebuild.name)
+ assert m, bootstrap_ebuild.name
+ return RustVersion(int(m.group(1)), int(m.group(2)), int(m.group(3)))
+
+
def parse_commandline_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
@@ -173,6 +186,18 @@ def parse_commandline_args() -> argparse.Namespace:
'specified, the tool will remove the oldest version in the chroot',
)
+ subparser_names.append('remove-bootstrap')
+ remove_bootstrap_parser = subparsers.add_parser(
+ 'remove-bootstrap',
+ help='Remove an old rust-bootstrap version',
+ )
+ remove_bootstrap_parser.add_argument(
+ '--version',
+ type=RustVersion.parse,
+ required=True,
+ help='rust-bootstrap version to remove',
+ )
+
subparser_names.append('roll')
roll_parser = subparsers.add_parser(
'roll',
@@ -225,25 +250,8 @@ def parse_commandline_args() -> argparse.Namespace:
return args
-def parse_stage0_file(new_version: RustVersion) -> Tuple[str, str, str]:
- # Find stage0 date, rustc and cargo
- stage0_file = get_command_output([
- 'curl', '-f', 'https://raw.githubusercontent.com/rust-lang/rust/'
- f'{new_version}/src/stage0.txt'
- ])
- regexp = re.compile(r'date:\s*(?P<date>\d+-\d+-\d+)\s+'
- r'rustc:\s*(?P<rustc>\d+\.\d+\.\d+)\s+'
- r'cargo:\s*(?P<cargo>\d+\.\d+\.\d+)')
- m = regexp.search(stage0_file)
- assert m, 'failed to parse stage0.txt file'
- stage0_date, stage0_rustc, stage0_cargo = m.groups()
- logging.info('Found stage0 file has date: %s, rustc: %s, cargo: %s',
- stage0_date, stage0_rustc, stage0_cargo)
- return stage0_date, stage0_rustc, stage0_cargo
-
-
def prepare_uprev(rust_version: RustVersion, template: Optional[RustVersion]
- ) -> Optional[Tuple[RustVersion, str]]:
+ ) -> Optional[Tuple[RustVersion, str, RustVersion]]:
if template is None:
ebuild_path = get_command_output(['equery', 'w', 'rust'])
ebuild_name = os.path.basename(ebuild_path)
@@ -252,6 +260,8 @@ def prepare_uprev(rust_version: RustVersion, template: Optional[RustVersion]
ebuild_path = find_ebuild_for_rust_version(template)
template_version = template
+ bootstrap_version = get_rust_bootstrap_version()
+
if rust_version <= template_version:
logging.info(
'Requested version %s is not newer than the template version %s.',
@@ -260,66 +270,69 @@ def prepare_uprev(rust_version: RustVersion, template: Optional[RustVersion]
logging.info('Template Rust version is %s (ebuild: %r)', template_version,
ebuild_path)
- return template_version, ebuild_path
+ logging.info('rust-bootstrap version is %s', bootstrap_version)
+ return template_version, ebuild_path, bootstrap_version
-def copy_patches(template_version: RustVersion,
+
+def copy_patches(directory: Path, template_version: RustVersion,
new_version: RustVersion) -> None:
- patch_path = os.path.join(RUST_PATH, 'files')
+ patch_path = directory.joinpath('files')
+ prefix = '%s-%s-' % (directory.name, template_version)
+ new_prefix = '%s-%s-' % (directory.name, new_version)
for f in os.listdir(patch_path):
- if f'rust-{template_version}' not in f:
+ if not f.startswith(prefix):
continue
- logging.info('Rename patch %s to new version', f)
+ logging.info('Copy patch %s to new version', f)
new_name = f.replace(str(template_version), str(new_version))
shutil.copyfile(
os.path.join(patch_path, f),
os.path.join(patch_path, new_name),
)
- subprocess.check_call(['git', 'add', f'files/rust-{new_version}-*.patch'],
- cwd=RUST_PATH)
+ subprocess.check_call(['git', 'add', f'{new_prefix}*.patch'], cwd=patch_path)
def create_ebuild(template_ebuild: str, new_version: RustVersion) -> str:
shutil.copyfile(template_ebuild,
- os.path.join(RUST_PATH, f'rust-{new_version}.ebuild'))
+ RUST_PATH.joinpath(f'rust-{new_version}.ebuild'))
subprocess.check_call(['git', 'add', f'rust-{new_version}.ebuild'],
cwd=RUST_PATH)
return os.path.join(RUST_PATH, f'rust-{new_version}.ebuild')
-def update_ebuild(ebuild_file: str, stage0_info: Tuple[str, str, str]) -> None:
- stage0_date, stage0_rustc, stage0_cargo = stage0_info
- with open(ebuild_file, encoding='utf-8') as f:
- contents = f.read()
- # Update STAGE0_DATE in the ebuild
- stage0_date_re = re.compile(r'STAGE0_DATE="(\d+-\d+-\d+)"')
- if not stage0_date_re.search(contents):
- raise RuntimeError('STAGE0_DATE not found in rust ebuild')
- new_contents = stage0_date_re.sub(f'STAGE0_DATE="{stage0_date}"', contents)
-
- # Update STAGE0_VERSION in the ebuild
- stage0_rustc_re = re.compile(r'STAGE0_VERSION="[^"]*"')
- if not stage0_rustc_re.search(new_contents):
- raise RuntimeError('STAGE0_VERSION not found in rust ebuild')
- new_contents = stage0_rustc_re.sub(f'STAGE0_VERSION="{stage0_rustc}"',
- new_contents)
-
- # Update STAGE0_VERSION_CARGO in the ebuild
- stage0_cargo_re = re.compile(r'STAGE0_VERSION_CARGO="[^"]*"')
- if not stage0_cargo_re.search(new_contents):
- raise RuntimeError('STAGE0_VERSION_CARGO not found in rust ebuild')
- new_contents = stage0_cargo_re.sub(f'STAGE0_VERSION_CARGO="{stage0_cargo}"',
- new_contents)
- with open(ebuild_file, 'w', encoding='utf-8') as f:
- f.write(new_contents)
- logging.info(
- 'Rust ebuild file has STAGE0_DATE, STAGE0_VERSION, STAGE0_VERSION_CARGO '
- 'updated to %s, %s, %s respectively', stage0_date, stage0_rustc,
- stage0_cargo)
-
-
-def flip_mirror_in_ebuild(ebuild_file: str, add: bool) -> None:
+def update_bootstrap_ebuild(new_bootstrap_version: RustVersion) -> None:
+ old_ebuild = find_ebuild_path(rust_bootstrap_path(), 'rust-bootstrap')
+ m = re.match(r'^rust-bootstrap-(\d+).(\d+).(\d+)', old_ebuild.name)
+ assert m, old_ebuild.name
+ old_version = RustVersion(m.group(1), m.group(2), m.group(3))
+ new_ebuild = old_ebuild.parent.joinpath(
+ f'rust-bootstrap-{new_bootstrap_version}.ebuild')
+ old_text = old_ebuild.read_text(encoding='utf-8')
+ new_text, changes = re.subn(
+ r'(RUSTC_FULL_BOOTSTRAP_SEQUENCE=\([^)]*)',
+ f'\\1\t{old_version}\n',
+ old_text,
+ flags=re.MULTILINE)
+ assert changes == 1, 'Failed to update RUSTC_FULL_BOOTSTRAP_SEQUENCE'
+ new_ebuild.write_text(new_text, encoding='utf-8')
+
+
+def update_ebuild(ebuild_file: str, new_bootstrap_version: RustVersion) -> None:
+ contents = open(ebuild_file, encoding='utf-8').read()
+ contents, subs = re.subn(
+ r'^BOOTSTRAP_VERSION=.*$',
+ 'BOOTSTRAP_VERSION="%s"' % (new_bootstrap_version,),
+ contents,
+ flags=re.MULTILINE)
+ if not subs:
+ raise RuntimeError('BOOTSTRAP_VERSION not found in rust ebuild')
+ open(ebuild_file, 'w', encoding='utf-8').write(contents)
+ logging.info('Rust ebuild file has BOOTSTRAP_VERSION updated to %s',
+ new_bootstrap_version)
+
+
+def flip_mirror_in_ebuild(ebuild_file: Path, add: bool) -> None:
restrict_re = re.compile(
r'(?P<before>RESTRICT=")(?P<values>"[^"]*"|.*)(?P<after>")')
with open(ebuild_file, encoding='utf-8') as f:
@@ -340,25 +353,27 @@ def flip_mirror_in_ebuild(ebuild_file: str, add: bool) -> None:
f.write(new_contents)
-def rust_ebuild_actions(actions: List[str], sudo: bool = False) -> None:
- ebuild_path_inchroot = get_command_output(['equery', 'w', 'rust'])
+def ebuild_actions(package: str, actions: List[str],
+ sudo: bool = False) -> None:
+ ebuild_path_inchroot = get_command_output(['equery', 'w', package])
cmd = ['ebuild', ebuild_path_inchroot] + actions
if sudo:
cmd = ['sudo'] + cmd
subprocess.check_call(cmd)
-def update_manifest(ebuild_file: str) -> None:
- logging.info('Added "mirror" to RESTRICT to Rust ebuild')
- flip_mirror_in_ebuild(ebuild_file, add=True)
- rust_ebuild_actions(['manifest'])
- logging.info('Removed "mirror" to RESTRICT from Rust ebuild')
- flip_mirror_in_ebuild(ebuild_file, add=False)
+def update_manifest(ebuild_file: os.PathLike) -> None:
+ ebuild = Path(ebuild_file)
+ logging.info('Added "mirror" to RESTRICT to %s', ebuild.name)
+ flip_mirror_in_ebuild(ebuild, add=True)
+ ebuild_actions(ebuild.parent.name, ['manifest'])
+ logging.info('Removed "mirror" to RESTRICT from %s', ebuild.name)
+ flip_mirror_in_ebuild(ebuild, add=False)
def update_rust_packages(rust_version: RustVersion, add: bool) -> None:
- package_file = os.path.join(
- RUST_PATH, '../../profiles/targets/chromeos/package.provided')
+ package_file = RUST_PATH.joinpath(
+ '../../profiles/targets/chromeos/package.provided')
with open(package_file, encoding='utf-8') as f:
contents = f.read()
if add:
@@ -382,91 +397,15 @@ def update_rust_packages(rust_version: RustVersion, add: bool) -> None:
def update_virtual_rust(template_version: RustVersion,
new_version: RustVersion) -> None:
- template_ebuild = find_virtual_rust_ebuild(template_version)
- virtual_rust_dir = os.path.dirname(template_ebuild)
+ template_ebuild = find_ebuild_path(
+ RUST_PATH.joinpath('../../virtual/rust'), 'rust', template_version)
+ virtual_rust_dir = template_ebuild.parent
new_name = f'rust-{new_version}.ebuild'
- new_ebuild = os.path.join(virtual_rust_dir, new_name)
+ new_ebuild = virtual_rust_dir.joinpath(new_name)
shutil.copyfile(template_ebuild, new_ebuild)
subprocess.check_call(['git', 'add', new_name], cwd=virtual_rust_dir)
-def upload_single_tarball(rust_url: str, tarfile_name: str,
- tempdir: str) -> None:
- rust_src = f'{rust_url}/{tarfile_name}'
- gsutil_location = f'gs://chromeos-localmirror/distfiles/{tarfile_name}'
-
- missing_file = subprocess.call(
- ['gsutil', 'ls', gsutil_location],
- stdout=subprocess.DEVNULL,
- stderr=subprocess.DEVNULL,
- )
- if not missing_file:
- logging.info('Rust artifact at %s already exists; skipping download',
- gsutil_location)
- return
-
- logging.info('Downloading Rust artifact from %s', rust_src)
-
- # Download Rust's source
- rust_file = os.path.join(tempdir, tarfile_name)
- subprocess.check_call(['curl', '-f', '-o', rust_file, rust_src])
-
- # Verify the signature of the source
- sig_file = os.path.join(tempdir, 'rustc_sig.asc')
- subprocess.check_call(['curl', '-f', '-o', sig_file, f'{rust_src}.asc'])
- try:
- subprocess.check_output(['gpg', '--verify', sig_file, rust_file],
- encoding='utf-8',
- stderr=subprocess.STDOUT)
- except subprocess.CalledProcessError as e:
- if "gpg: Can't check signature" not in e.output:
- raise RuntimeError(f'Failed to execute `gpg --verify`, {e.output}')
-
- # If it fails to verify the signature, try import rustc key, and retry.
- keys = get_command_output(
- ['curl', '-f', 'https://keybase.io/rust/pgp_keys.asc'])
- subprocess.run(['gpg', '--import'],
- input=keys,
- encoding='utf-8',
- check=True)
- subprocess.check_call(['gpg', '--verify', sig_file, rust_file])
-
- # Since we are using `-n` to skip an item if it already exists, there's no
- # need to check if the file exists on GS bucket or not.
- subprocess.check_call(
- ['gsutil', 'cp', '-n', '-a', 'public-read', rust_file, gsutil_location])
-
-
-def upload_to_localmirror(tempdir: str, rust_version: RustVersion,
- stage0_info: Tuple[str, str, str]) -> None:
- stage0_date, stage0_rustc, stage0_cargo = stage0_info
- rust_url = 'https://static.rust-lang.org/dist'
- # Upload rustc source
- upload_single_tarball(
- rust_url,
- f'rustc-{rust_version}-src.tar.gz',
- tempdir,
- )
- # Upload stage0 toolchain
- upload_single_tarball(
- f'{rust_url}/{stage0_date}',
- f'rust-std-{stage0_rustc}-x86_64-unknown-linux-gnu.tar.gz',
- tempdir,
- )
- # Upload stage0 source
- upload_single_tarball(
- rust_url,
- f'rustc-{stage0_rustc}-x86_64-unknown-linux-gnu.tar.gz',
- tempdir,
- )
- # Upload stage0 cargo
- upload_single_tarball(
- rust_url,
- f'cargo-{stage0_cargo}-x86_64-unknown-linux-gnu.tar.gz',
- tempdir,
- )
-
-
def perform_step(state_file: pathlib.Path,
tmp_state_file: pathlib.Path,
completed_steps: Dict[str, Any],
@@ -494,19 +433,18 @@ def perform_step(state_file: pathlib.Path,
return val
-def prepare_uprev_from_json(obj: Any) -> Optional[Tuple[RustVersion, str]]:
+def prepare_uprev_from_json(obj: Any
+ ) -> Optional[Tuple[RustVersion, str, RustVersion]]:
if not obj:
return None
- version, ebuild_path = obj
- return RustVersion(*version), ebuild_path
+ version, ebuild_path, bootstrap_version = obj
+ return RustVersion(*version), ebuild_path, RustVersion(*bootstrap_version)
def create_rust_uprev(rust_version: RustVersion,
maybe_template_version: Optional[RustVersion],
skip_compile: bool, run_step: Callable[[], T]) -> None:
- stage0_info = run_step(
- 'parse stage0 file', lambda: parse_stage0_file(rust_version))
- template_version, template_ebuild = run_step(
+ template_version, template_ebuild, old_bootstrap_version = run_step(
'prepare uprev',
lambda: prepare_uprev(rust_version, maybe_template_version),
result_from_json=prepare_uprev_from_json,
@@ -514,15 +452,22 @@ def create_rust_uprev(rust_version: RustVersion,
if template_ebuild is None:
return
- run_step('copy patches', lambda: copy_patches(template_version, rust_version))
+ run_step(
+ 'copy bootstrap patches', lambda: copy_patches(rust_bootstrap_path(
+ ), old_bootstrap_version, template_version))
+ run_step('update bootstrap ebuild', lambda: update_bootstrap_ebuild(
+ template_version))
+ run_step(
+ 'update bootstrap manifest', lambda: update_manifest(rust_bootstrap_path(
+ ).joinpath(f'rust-bootstrap-{template_version}.ebuild')))
+ run_step('copy patches', lambda: copy_patches(RUST_PATH, template_version,
+ rust_version))
ebuild_file = run_step(
'create ebuild', lambda: create_ebuild(template_ebuild, rust_version))
- run_step('update ebuild', lambda: update_ebuild(ebuild_file, stage0_info))
- with tempfile.TemporaryDirectory(dir='/tmp') as tempdir:
- run_step('upload_to_localmirror', lambda: upload_to_localmirror(
- tempdir, rust_version, stage0_info))
+ run_step(
+ 'update ebuild', lambda: update_ebuild(ebuild_file, template_version))
run_step('update manifest to add new version', lambda: update_manifest(
- ebuild_file))
+ Path(ebuild_file)))
if not skip_compile:
run_step('emerge rust', lambda: subprocess.check_call(
['sudo', 'emerge', 'dev-lang/rust']))
@@ -561,6 +506,19 @@ def remove_files(filename: str, path: str) -> None:
subprocess.check_call(['git', 'rm', filename], cwd=path)
+def remove_rust_bootstrap_version(version: RustVersion,
+ run_step: Callable[[], T]) -> None:
+ prefix = f'rust-bootstrap-{version}'
+ run_step(
+ 'remove old bootstrap patches', lambda: remove_files(
+ f'files/{prefix}-*.patch', rust_bootstrap_path()))
+ run_step('remove old bootstrap ebuild', lambda: remove_files(
+ f'{prefix}*.ebuild', rust_bootstrap_path()))
+ ebuild_file = get_command_output(['equery', 'w', 'rust-bootstrap'])
+ run_step('update bootstrap manifest to delete old version', lambda:
+ update_manifest(ebuild_file))
+
+
def remove_rust_uprev(rust_version: Optional[RustVersion],
run_step: Callable[[], T]) -> None:
@@ -569,10 +527,14 @@ def remove_rust_uprev(rust_version: Optional[RustVersion],
return rust_version, find_ebuild_for_rust_version(rust_version)
return find_oldest_rust_version_in_chroot()
+ def find_desired_rust_version_from_json(obj: Any) -> Tuple[RustVersion, str]:
+ version, ebuild_path = obj
+ return RustVersion(*version), ebuild_path
+
delete_version, delete_ebuild = run_step(
'find rust version to delete',
find_desired_rust_version,
- result_from_json=prepare_uprev_from_json,
+ result_from_json=find_desired_rust_version_from_json,
)
run_step(
'remove patches', lambda: remove_files(
@@ -587,8 +549,13 @@ def remove_rust_uprev(rust_version: Optional[RustVersion],
def remove_virtual_rust(delete_version: RustVersion) -> None:
- dirname, basename = os.path.split(find_virtual_rust_ebuild(delete_version))
- subprocess.check_call(['git', 'rm', basename], cwd=dirname)
+ ebuild = find_ebuild_path(
+ RUST_PATH.joinpath('../../virtual/rust'), 'rust', delete_version)
+ subprocess.check_call(['git', 'rm', str(ebuild.name)], cwd=ebuild.parent)
+
+
+def rust_bootstrap_path() -> Path:
+ return RUST_PATH.joinpath('../rust-bootstrap')
def create_new_repo(rust_version: RustVersion) -> None:
@@ -673,6 +640,8 @@ def main() -> None:
run_step)
elif args.subparser_name == 'remove':
remove_rust_uprev(args.rust_version, run_step)
+ elif args.subparser_name == 'remove-bootstrap':
+ remove_rust_bootstrap_version(args.version, run_step)
else:
# If you have added more subparser_name, please also add the handlers above
assert args.subparser_name == 'roll'
@@ -681,6 +650,9 @@ def main() -> None:
run_step('build cross compiler', build_cross_compiler)
create_rust_uprev(args.uprev, args.template, args.skip_compile, run_step)
remove_rust_uprev(args.remove, run_step)
+ bootstrap_version = prepare_uprev_from_json(
+ completed_steps['prepare uprev'])[2]
+ remove_rust_bootstrap_version(bootstrap_version, run_step)
if not args.no_upload:
run_step('create rust uprev CL', lambda: create_new_commit(args.uprev))
diff --git a/rust_tools/rust_uprev_test.py b/rust_tools/rust_uprev_test.py
index acf700b8..b35f1db7 100755
--- a/rust_tools/rust_uprev_test.py
+++ b/rust_tools/rust_uprev_test.py
@@ -7,11 +7,12 @@
"""Tests for rust_uprev.py"""
# pylint: disable=cros-logging-import
-import glob
import os
import shutil
import subprocess
+import tempfile
import unittest
+from pathlib import Path
from unittest import mock
from llvm_tools import git
@@ -19,6 +20,35 @@ from llvm_tools import git
import rust_uprev
+class FindEbuildPathTest(unittest.TestCase):
+ """Tests for rust_uprev.find_ebuild_path()"""
+
+ def test_exact_version(self):
+ with tempfile.TemporaryDirectory() as tmpdir:
+ ebuild = Path(tmpdir, 'test-1.3.4.ebuild')
+ ebuild.touch()
+ Path(tmpdir, 'test-1.2.3.ebuild').touch()
+ result = rust_uprev.find_ebuild_path(tmpdir, 'test',
+ rust_uprev.RustVersion(1, 3, 4))
+ self.assertEqual(result, ebuild)
+
+ def test_no_version(self):
+ with tempfile.TemporaryDirectory() as tmpdir:
+ ebuild = Path(tmpdir, 'test-1.2.3.ebuild')
+ ebuild.touch()
+ result = rust_uprev.find_ebuild_path(tmpdir, 'test')
+ self.assertEqual(result, ebuild)
+
+ def test_patch_version(self):
+ with tempfile.TemporaryDirectory() as tmpdir:
+ ebuild = Path(tmpdir, 'test-1.3.4-r3.ebuild')
+ ebuild.touch()
+ Path(tmpdir, 'test-1.2.3.ebuild').touch()
+ result = rust_uprev.find_ebuild_path(tmpdir, 'test',
+ rust_uprev.RustVersion(1, 3, 4))
+ self.assertEqual(result, ebuild)
+
+
class RustVersionTest(unittest.TestCase):
"""Tests for RustVersion class"""
@@ -50,6 +80,7 @@ class PrepareUprevTest(unittest.TestCase):
"""Tests for prepare_uprev step in rust_uprev"""
def setUp(self):
+ self.bootstrap_version = rust_uprev.RustVersion(1, 1, 0)
self.version_old = rust_uprev.RustVersion(1, 2, 3)
self.version_new = rust_uprev.RustVersion(1, 3, 5)
@@ -57,9 +88,15 @@ class PrepareUprevTest(unittest.TestCase):
rust_uprev,
'find_ebuild_for_rust_version',
return_value='/path/to/ebuild')
+ @mock.patch.object(rust_uprev, 'find_ebuild_path')
@mock.patch.object(rust_uprev, 'get_command_output')
- def test_success_with_template(self, mock_command, _mock_find_ebuild):
- expected = (self.version_old, '/path/to/ebuild')
+ def test_success_with_template(self, mock_command, mock_find_ebuild,
+ _ebuild_for_version):
+ bootstrap_ebuild_path = Path(
+ '/path/to/rust-bootstrap/',
+ f'rust-bootstrap-{self.bootstrap_version}.ebuild')
+ mock_find_ebuild.return_value = bootstrap_ebuild_path
+ expected = (self.version_old, '/path/to/ebuild', self.bootstrap_version)
actual = rust_uprev.prepare_uprev(
rust_version=self.version_new, template=self.version_old)
self.assertEqual(expected, actual)
@@ -77,12 +114,18 @@ class PrepareUprevTest(unittest.TestCase):
self.assertIsNone(ret)
mock_command.assert_not_called()
+ @mock.patch.object(rust_uprev, 'find_ebuild_path')
@mock.patch.object(os.path, 'exists')
@mock.patch.object(rust_uprev, 'get_command_output')
- def test_success_without_template(self, mock_command, mock_exists):
+ def test_success_without_template(self, mock_command, mock_exists,
+ mock_find_ebuild):
rust_ebuild_path = f'/path/to/rust/rust-{self.version_old}-r3.ebuild'
mock_command.return_value = rust_ebuild_path
- expected = (self.version_old, rust_ebuild_path)
+ bootstrap_ebuild_path = Path(
+ '/path/to/rust-bootstrap',
+ f'rust-bootstrap-{self.bootstrap_version}.ebuild')
+ mock_find_ebuild.return_value = bootstrap_ebuild_path
+ expected = (self.version_old, rust_ebuild_path, self.bootstrap_version)
actual = rust_uprev.prepare_uprev(
rust_version=self.version_new, template=None)
self.assertEqual(expected, actual)
@@ -100,8 +143,9 @@ class PrepareUprevTest(unittest.TestCase):
def test_prepare_uprev_from_json(self):
ebuild_path = '/path/to/the/ebuild'
- json_result = (list(self.version_new), ebuild_path)
- expected = (self.version_new, ebuild_path)
+ json_result = (list(self.version_new), ebuild_path,
+ list(self.bootstrap_version))
+ expected = (self.version_new, ebuild_path, self.bootstrap_version)
actual = rust_uprev.prepare_uprev_from_json(json_result)
self.assertEqual(expected, actual)
@@ -109,32 +153,30 @@ class PrepareUprevTest(unittest.TestCase):
class UpdateEbuildTest(unittest.TestCase):
"""Tests for update_ebuild step in rust_uprev"""
ebuild_file_before = """
- STAGE0_DATE="2019-01-01"
- STAGE0_VERSION="any.random.(number)"
- STAGE0_VERSION_CARGO="0.0.0"
+BOOTSTRAP_VERSION="1.2.0"
"""
ebuild_file_after = """
- STAGE0_DATE="2020-01-01"
- STAGE0_VERSION="1.1.1"
- STAGE0_VERSION_CARGO="0.1.0"
+BOOTSTRAP_VERSION="1.3.6"
"""
def test_success(self):
mock_open = mock.mock_open(read_data=self.ebuild_file_before)
+ # ebuild_file and new bootstrap version are deliberately different
ebuild_file = '/path/to/rust/rust-1.3.5.ebuild'
with mock.patch('builtins.open', mock_open):
- rust_uprev.update_ebuild(ebuild_file, ('2020-01-01', '1.1.1', '0.1.0'))
+ rust_uprev.update_ebuild(ebuild_file,
+ rust_uprev.RustVersion.parse('1.3.6'))
mock_open.return_value.__enter__().write.assert_called_once_with(
self.ebuild_file_after)
def test_fail_when_ebuild_misses_a_variable(self):
- ebuild_file = 'STAGE0_DATE="2019-01-01"'
- mock_open = mock.mock_open(read_data=ebuild_file)
+ mock_open = mock.mock_open(read_data='')
ebuild_file = '/path/to/rust/rust-1.3.5.ebuild'
with mock.patch('builtins.open', mock_open):
with self.assertRaises(RuntimeError) as context:
- rust_uprev.update_ebuild(ebuild_file, ('2020-01-01', '1.1.1', '0.1.0'))
- self.assertEqual('STAGE0_VERSION not found in rust ebuild',
+ rust_uprev.update_ebuild(ebuild_file,
+ rust_uprev.RustVersion.parse('1.2.0'))
+ self.assertEqual('BOOTSTRAP_VERSION not found in rust ebuild',
str(context.exception))
@@ -179,16 +221,56 @@ class UpdateManifestTest(unittest.TestCase):
expect_write=False)
@mock.patch.object(rust_uprev, 'flip_mirror_in_ebuild')
- @mock.patch.object(rust_uprev, 'rust_ebuild_actions')
+ @mock.patch.object(rust_uprev, 'ebuild_actions')
def test_update_manifest(self, mock_run, mock_flip):
- ebuild_file = '/path/to/rust/rust-1.1.1.ebuild'
+ ebuild_file = Path('/path/to/rust/rust-1.1.1.ebuild')
rust_uprev.update_manifest(ebuild_file)
- mock_run.assert_called_once_with(['manifest'])
+ mock_run.assert_called_once_with('rust', ['manifest'])
mock_flip.assert_has_calls(
[mock.call(ebuild_file, add=True),
mock.call(ebuild_file, add=False)])
+class UpdateBootstrapEbuildTest(unittest.TestCase):
+ """Tests for rust_uprev.update_bootstrap_ebuild()"""
+
+ def test_update_bootstrap_ebuild(self):
+ # The update should do two things:
+ # 1. Create a copy of rust-bootstrap's ebuild with the new version number.
+ # 2. Add the old PV to RUSTC_FULL_BOOTSTRAP_SEQUENCE.
+ with tempfile.TemporaryDirectory() as tmpdir_str, \
+ mock.patch.object(rust_uprev, 'find_ebuild_path') as mock_find_ebuild:
+ tmpdir = Path(tmpdir_str)
+ bootstrapdir = Path.joinpath(tmpdir, 'rust-bootstrap')
+ bootstrapdir.mkdir()
+ old_ebuild = bootstrapdir.joinpath('rust-bootstrap-1.45.2.ebuild')
+ old_ebuild.write_text(
+ encoding='utf-8',
+ data="""
+some text
+RUSTC_FULL_BOOTSTRAP_SEQUENCE=(
+\t1.43.1
+\t1.44.1
+)
+some more text
+""")
+ mock_find_ebuild.return_value = old_ebuild
+ rust_uprev.update_bootstrap_ebuild(rust_uprev.RustVersion(1, 46, 0))
+ new_ebuild = bootstrapdir.joinpath('rust-bootstrap-1.46.0.ebuild')
+ self.assertTrue(new_ebuild.exists())
+ text = new_ebuild.read_text()
+ self.assertEqual(
+ text, """
+some text
+RUSTC_FULL_BOOTSTRAP_SEQUENCE=(
+\t1.43.1
+\t1.44.1
+\t1.45.2
+)
+some more text
+""")
+
+
class UpdateRustPackagesTests(unittest.TestCase):
"""Tests for update_rust_packages step."""
@@ -224,103 +306,6 @@ class UpdateRustPackagesTests(unittest.TestCase):
package_after)
-class UploadToLocalmirrorTests(unittest.TestCase):
- """Tests for upload_to_localmirror"""
-
- def setUp(self):
- self.tempdir = '/tmp/any/dir'
- self.new_version = rust_uprev.RustVersion(1, 3, 5)
- self.rust_url = 'https://static.rust-lang.org/dist'
- self.tarfile_name = f'rustc-{self.new_version}-src.tar.gz'
- self.rust_src = f'https://static.rust-lang.org/dist/{self.tarfile_name}'
- self.gsurl = f'gs://chromeos-localmirror/distfiles/{self.tarfile_name}'
- self.rust_file = os.path.join(self.tempdir, self.tarfile_name)
- self.sig_file = os.path.join(self.tempdir, 'rustc_sig.asc')
-
- @mock.patch.object(subprocess, 'call', return_value=1)
- @mock.patch.object(subprocess, 'check_call')
- @mock.patch.object(subprocess, 'check_output')
- @mock.patch.object(subprocess, 'run')
- def test_pass_without_retry(self, mock_run, mock_output, mock_call,
- mock_raw_call):
- rust_uprev.upload_single_tarball(self.rust_url, self.tarfile_name,
- self.tempdir)
- mock_output.assert_called_once_with(
- ['gpg', '--verify', self.sig_file, self.rust_file],
- encoding='utf-8',
- stderr=subprocess.STDOUT)
- mock_raw_call.assert_has_calls([
- mock.call(['gsutil', 'ls', self.gsurl],
- stdout=subprocess.DEVNULL,
- stderr=subprocess.DEVNULL)
- ])
- mock_call.assert_has_calls([
- mock.call(['curl', '-f', '-o', self.rust_file, self.rust_src]),
- mock.call(['curl', '-f', '-o', self.sig_file, f'{self.rust_src}.asc']),
- mock.call([
- 'gsutil', 'cp', '-n', '-a', 'public-read', self.rust_file,
- self.gsurl
- ])
- ])
- mock_run.assert_not_called()
-
- @mock.patch.object(subprocess, 'call')
- @mock.patch.object(subprocess, 'check_call')
- @mock.patch.object(subprocess, 'check_output')
- @mock.patch.object(subprocess, 'run')
- @mock.patch.object(rust_uprev, 'get_command_output')
- def test_pass_with_retry(self, mock_output, mock_run, mock_check, mock_call,
- mock_raw_call):
- mock_check.side_effect = subprocess.CalledProcessError(
- returncode=2, cmd=None, output="gpg: Can't check signature")
- mock_output.return_value = 'some_gpg_keys'
- rust_uprev.upload_single_tarball(self.rust_url, self.tarfile_name,
- self.tempdir)
- mock_check.assert_called_once_with(
- ['gpg', '--verify', self.sig_file, self.rust_file],
- encoding='utf-8',
- stderr=subprocess.STDOUT)
- mock_output.assert_called_once_with(
- ['curl', '-f', 'https://keybase.io/rust/pgp_keys.asc'])
- mock_run.assert_called_once_with(['gpg', '--import'],
- input='some_gpg_keys',
- encoding='utf-8',
- check=True)
- mock_raw_call.assert_has_calls([
- mock.call(['gsutil', 'ls', self.gsurl],
- stdout=subprocess.DEVNULL,
- stderr=subprocess.DEVNULL)
- ])
- mock_call.assert_has_calls([
- mock.call(['curl', '-f', '-o', self.rust_file, self.rust_src]),
- mock.call(['curl', '-f', '-o', self.sig_file, f'{self.rust_src}.asc']),
- mock.call(['gpg', '--verify', self.sig_file, self.rust_file]),
- mock.call([
- 'gsutil', 'cp', '-n', '-a', 'public-read', self.rust_file,
- self.gsurl
- ])
- ])
-
- @mock.patch.object(rust_uprev, 'upload_single_tarball')
- def test_upload_to_mirror(self, mock_upload):
- stage0_info = '2020-01-01', '1.1.1', '0.1.0'
- rust_uprev.upload_to_localmirror(self.tempdir, self.new_version,
- stage0_info)
- mock_upload.assert_has_calls([
- mock.call(self.rust_url, f'rustc-{self.new_version}-src.tar.gz',
- self.tempdir),
- mock.call(f'{self.rust_url}/{stage0_info[0]}',
- f'rust-std-{stage0_info[1]}-x86_64-unknown-linux-gnu.tar.gz',
- self.tempdir),
- mock.call(self.rust_url,
- f'rustc-{stage0_info[1]}-x86_64-unknown-linux-gnu.tar.gz',
- self.tempdir),
- mock.call(self.rust_url,
- f'cargo-{stage0_info[2]}-x86_64-unknown-linux-gnu.tar.gz',
- self.tempdir),
- ])
-
-
class RustUprevOtherStagesTests(unittest.TestCase):
"""Tests for other steps in rust_uprev"""
@@ -331,25 +316,6 @@ class RustUprevOtherStagesTests(unittest.TestCase):
self.ebuild_file = os.path.join(rust_uprev.RUST_PATH,
'rust-{self.new_version}.ebuild')
- @mock.patch.object(rust_uprev, 'get_command_output')
- def test_parse_stage0_file(self, mock_get):
- stage0_file = """
- unrelated stuff before
- date: 2020-01-01
- rustc: 1.1.1
- cargo: 0.1.0
- unrelated stuff after
- """
- mock_get.return_value = stage0_file
- expected = '2020-01-01', '1.1.1', '0.1.0'
- rust_version = rust_uprev.RustVersion(1, 2, 3)
- actual = rust_uprev.parse_stage0_file(rust_version)
- self.assertEqual(expected, actual)
- mock_get.assert_called_once_with([
- 'curl', '-f', 'https://raw.githubusercontent.com/rust-lang/rust/'
- f'{rust_version}/src/stage0.txt'
- ])
-
@mock.patch.object(shutil, 'copyfile')
@mock.patch.object(os, 'listdir')
@mock.patch.object(subprocess, 'check_call')
@@ -360,7 +326,8 @@ class RustUprevOtherStagesTests(unittest.TestCase):
f'rust-{self.current_version}-patch-1.patch',
f'rust-{self.current_version}-patch-2-new.patch'
]
- rust_uprev.copy_patches(self.current_version, self.new_version)
+ rust_uprev.copy_patches(rust_uprev.RUST_PATH, self.current_version,
+ self.new_version)
mock_copy.assert_has_calls([
mock.call(
os.path.join(rust_uprev.RUST_PATH, 'files',
@@ -375,8 +342,8 @@ class RustUprevOtherStagesTests(unittest.TestCase):
f'rust-{self.new_version}-patch-2-new.patch'))
])
mock_call.assert_called_once_with(
- ['git', 'add', f'files/rust-{self.new_version}-*.patch'],
- cwd=rust_uprev.RUST_PATH)
+ ['git', 'add', f'rust-{self.new_version}-*.patch'],
+ cwd=rust_uprev.RUST_PATH.joinpath('files'))
@mock.patch.object(shutil, 'copyfile')
@mock.patch.object(subprocess, 'check_call')
@@ -385,65 +352,51 @@ class RustUprevOtherStagesTests(unittest.TestCase):
rust_uprev.create_ebuild(template_ebuild, self.new_version)
mock_copy.assert_called_once_with(
template_ebuild,
- os.path.join(rust_uprev.RUST_PATH, f'rust-{self.new_version}.ebuild'))
+ rust_uprev.RUST_PATH.joinpath(f'rust-{self.new_version}.ebuild'))
mock_call.assert_called_once_with(
['git', 'add', f'rust-{self.new_version}.ebuild'],
cwd=rust_uprev.RUST_PATH)
- @mock.patch.object(glob, 'glob')
@mock.patch.object(subprocess, 'check_call')
- def test_remove_virtual_rust(self, mock_call, mock_glob):
- virtual_rust_dir = os.path.join(rust_uprev.RUST_PATH, '../../virtual/rust')
- mock_glob.return_value = [
- os.path.join(virtual_rust_dir, f'rust-{self.old_version}.ebuild'),
- ]
- rust_uprev.remove_virtual_rust(self.old_version)
- mock_call.assert_called_once_with(
- ['git', 'rm', f'rust-{self.old_version}.ebuild'], cwd=virtual_rust_dir)
+ def test_remove_rust_bootstrap_version(self, mock_call):
+ bootstrap_path = os.path.join(rust_uprev.RUST_PATH, '..', 'rust-bootstrap')
+ rust_uprev.remove_rust_bootstrap_version(self.old_version, lambda *x: ())
+ mock_call.has_calls([
+ [
+ 'git', 'rm',
+ os.path.join(bootstrap_path, 'files',
+ f'rust-bootstrap-{self.old_version}-*.patch')
+ ],
+ [
+ 'git', 'rm',
+ os.path.join(bootstrap_path,
+ f'rust-bootstrap-{self.old_version}.ebuild')
+ ],
+ ])
- @mock.patch.object(glob, 'glob')
+ @mock.patch.object(rust_uprev, 'find_ebuild_path')
@mock.patch.object(subprocess, 'check_call')
- def test_remove_virtual_rust_patch(self, mock_call, mock_glob):
- virtual_rust_dir = os.path.join(rust_uprev.RUST_PATH, '../../virtual/rust')
- mock_glob.return_value = [
- os.path.join(virtual_rust_dir, f'rust-{self.old_version}-r3.ebuild'),
- ]
+ def test_remove_virtual_rust(self, mock_call, mock_find_ebuild):
+ ebuild_path = Path(f'/some/dir/virtual/rust/rust-{self.old_version}.ebuild')
+ mock_find_ebuild.return_value = Path(ebuild_path)
rust_uprev.remove_virtual_rust(self.old_version)
mock_call.assert_called_once_with(
- ['git', 'rm', f'rust-{self.old_version}-r3.ebuild'],
- cwd=virtual_rust_dir)
-
- @mock.patch.object(glob, 'glob')
- @mock.patch.object(shutil, 'copyfile')
- @mock.patch.object(subprocess, 'check_call')
- def test_update_virtual_rust(self, mock_call, mock_copy, mock_glob):
- virtual_rust_dir = os.path.join(rust_uprev.RUST_PATH, '../../virtual/rust')
- mock_glob.return_value = [
- os.path.join(virtual_rust_dir, f'rust-{self.current_version}.ebuild'),
- ]
- rust_uprev.update_virtual_rust(self.current_version, self.new_version)
- mock_call.assert_called_once_with(
- ['git', 'add', f'rust-{self.new_version}.ebuild'], cwd=virtual_rust_dir)
- mock_copy.assert_called_once_with(
- os.path.join(virtual_rust_dir, f'rust-{self.current_version}.ebuild'),
- os.path.join(virtual_rust_dir, f'rust-{self.new_version}.ebuild'))
+ ['git', 'rm', str(ebuild_path.name)], cwd=ebuild_path.parent)
- @mock.patch.object(glob, 'glob')
+ @mock.patch.object(rust_uprev, 'find_ebuild_path')
@mock.patch.object(shutil, 'copyfile')
@mock.patch.object(subprocess, 'check_call')
- def test_update_virtual_rust_patched(self, mock_call, mock_copy, mock_glob):
- virtual_rust_dir = os.path.join(rust_uprev.RUST_PATH, '../../virtual/rust')
- mock_glob.return_value = [
- os.path.join(virtual_rust_dir,
- f'rust-{self.current_version}-r3.ebuild'),
- ]
+ def test_update_virtual_rust(self, mock_call, mock_copy, mock_find_ebuild):
+ ebuild_path = Path(
+ f'/some/dir/virtual/rust/rust-{self.current_version}.ebuild')
+ mock_find_ebuild.return_value = Path(ebuild_path)
rust_uprev.update_virtual_rust(self.current_version, self.new_version)
mock_call.assert_called_once_with(
- ['git', 'add', f'rust-{self.new_version}.ebuild'], cwd=virtual_rust_dir)
+ ['git', 'add', f'rust-{self.new_version}.ebuild'],
+ cwd=ebuild_path.parent)
mock_copy.assert_called_once_with(
- os.path.join(virtual_rust_dir,
- f'rust-{self.current_version}-r3.ebuild'),
- os.path.join(virtual_rust_dir, f'rust-{self.new_version}.ebuild'))
+ ebuild_path.parent.joinpath(f'rust-{self.current_version}.ebuild'),
+ ebuild_path.parent.joinpath(f'rust-{self.new_version}.ebuild'))
@mock.patch.object(os, 'listdir')
def test_find_oldest_rust_version_in_chroot_pass(self, mock_ls):