blob: 997e75f939524e9ae6e7a8c2de2e1e6c02f1cc59 [file]
#!/usr/bin/env python3
# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
'''Update in-tree checkout of Rust toolchain
When run without arguments, it fetches and unzips the Rust toolchain package
specieid by the `RUST_REVISION` and `RUST_SUB_REVISION` along with the clang
version specified in //tools/clang/scripts/update.py.
Specify --output-dir to override the location for the Rust toolchain package,
which otherwise defaults to //third_party/rust-toolchain.
(Note that the output dir may be deleted and re-created if it exists.)
'''
import argparse
import glob
import os
import re
import shutil
import sys
import time
import urllib
from pathlib import Path
# Add Clang scripts to path so we can import them later (if running within a
# Chromium checkout.)
# Note: Imports cannot be done until after the --print-revision flag
# has been processed, since that needs to work when running this script
# in isolation.
sys.path.append(
os.path.join(
os.path.dirname(os.path.abspath(__file__)), '..', 'clang', 'scripts'
)
)
# The revisions of rustc, crubit, and bindgen to use, from
# https://github.com/google/rust-lang/rust,
# https://github.com/google/crubit, and
# https://github.com/rust-lang/rust-bindgen/tags
# respectively.
#
# Do NOT CHANGE these fields if you don't know what you're doing -- see
# https://chromium.googlesource.com/chromium/src/+/main/docs/updating_clang.md
# Reverting problematic toolchain rolls is safe, though.
RUST_REVISION = '1edd55dcfcd573872c727fa3e086369a71661ee0'
CRUBIT_REVISION = 'a355b02da81bc9f350925c73ec0322ce4d5140f1'
BINDGEN_REVISION = '73c69d681eec90b84ffba4f993b5fb2f19580781'
# If you change the above without changing RUST_REVISION, increment this.
RUST_SUB_REVISION = 2
# Hash of src/stage0.json, which itself contains the stage0 toolchain hashes.
# We trust the Rust build system checks, but to ensure it is not tampered with
# itself check the hash.
STAGE0_JSON_SHA256 = (
'960197f9f1c3929b815c76fb34aef300571f734204d7cb61bcf3559baa4624b9'
)
THIS_DIR = os.path.abspath(os.path.dirname(__file__))
CHROMIUM_DIR = os.path.abspath(os.path.join(THIS_DIR, '..', '..'))
THIRD_PARTY_DIR = os.path.join(CHROMIUM_DIR, 'third_party')
RUST_TOOLCHAIN_OUT_DIR = os.path.join(THIRD_PARTY_DIR, 'rust-toolchain')
# Filename and path to the VERSION file stored in the archive.
VERSION_SRC_FILENAME = 'VERSION'
VERSION_SRC_PATH = os.path.join(RUST_TOOLCHAIN_OUT_DIR, VERSION_SRC_FILENAME)
def GetRustClangRevision():
from update import CLANG_REVISION
return f'{RUST_REVISION}-{RUST_SUB_REVISION}-{CLANG_REVISION}'
# Get the version of the toolchain package we already have.
def GetStampVersion():
if os.path.exists(VERSION_SRC_PATH):
with open(VERSION_SRC_PATH) as version_file:
existing_stamp = version_file.readline().rstrip()
version_re = re.compile(r'rustc [0-9.]+ [0-9a-f]+ \((.+?) chromium\)')
match = version_re.fullmatch(existing_stamp)
if match is None:
return None
return match.group(1)
return None
def main():
parser = argparse.ArgumentParser(
description='Update Rust package',
formatter_class=argparse.RawTextHelpFormatter,
)
parser.add_argument(
'--print-revision',
nargs='?',
const='all',
choices=[
'all',
'rust',
'crubit',
'bindgen',
'stage0',
'installed',
'validate',
],
help='Print the revision then quit. Possible formats:\n'
'- all (default): print all expected revisions.\n'
'- rust: print the expected rust revision (without clang).\n'
'- crubit: print the expected crubit revision.\n'
'- bindgen: print the expected bindgen revision.\n'
'- stage0: print the expected stage0.json hash.\n'
'- installed: print the installed rust version (including both\n'
' rust and clang revisions), without checking that it matches the\n'
' expected version in this file.\n'
'- validate: print the expected rust version, and ensure it\n'
' matches the installed rust version.\n'
'All options besides `installed` and `validate` can be run outside\n'
'of a Chromium checkout.',
)
parser.add_argument('--output-dir', help='Where to extract the package.')
args = parser.parse_args()
revisions = {
'rust': f'{RUST_REVISION}-{RUST_SUB_REVISION}',
'crubit': CRUBIT_REVISION,
'bindgen': BINDGEN_REVISION,
'stage0': STAGE0_JSON_SHA256,
}
if args.print_revision == 'all':
for name, rev in revisions.items():
print(f'{name}: {rev}')
return 0
elif args.print_revision in revisions:
print(f'{args.print_revision}: {revisions[args.print_revision]}')
return 0
elif args.print_revision:
stamp_version = GetStampVersion()
if (
args.print_revision == 'validate'
and stamp_version != GetRustClangRevision()
):
print(
f'The expected Rust version is {GetRustClangRevision()} '
f'but the actual version is {stamp_version}'
)
print('Did you run "gclient sync"?')
return 1
print(stamp_version)
return 0
output_dir = RUST_TOOLCHAIN_OUT_DIR
if args.output_dir:
global VERSION_SRC_PATH
output_dir = os.path.abspath(args.output_dir)
VERSION_SRC_PATH = os.path.join(output_dir, VERSION_SRC_FILENAME)
from update import DownloadAndUnpack, GetDefaultHostOs, GetPlatformUrlPrefix
platform_prefix = GetPlatformUrlPrefix(GetDefaultHostOs())
version = GetRustClangRevision()
# Exit early if the existing package is up-to-date. Note that we cannot
# simply call DownloadAndUnpack() every time: aside from unnecessarily
# downloading the toolchain if it hasn't changed, it also leads to multiple
# versions of the same rustlibs. build/rust/std/find_std_rlibs.py chokes in
# this case.
# .*_is_first_class_gcs file is created by first class GCS deps when rust
# hooks are migrated to be first class deps. In case we need to go back to
# using a hook, this file will indicate that the previous download was
# from the first class dep and the dir needs to be cleared.
if os.path.exists(output_dir):
if version == GetStampVersion() and not glob.glob(
os.path.join(output_dir, '.*_is_first_class_gcs')
):
return 0
if os.path.exists(output_dir):
shutil.rmtree(output_dir)
try:
url = f'{platform_prefix}rust-toolchain-{version}.tar.xz'
DownloadAndUnpack(url, output_dir)
except urllib.error.HTTPError as e:
print(f'error: Failed to download Rust package')
return 1
# Ensure the newly extracted package has the correct version.
assert version == GetStampVersion()
if __name__ == '__main__':
sys.exit(main())