Skip to content

Commit 96986d3

Browse files
danakjAravind Vasudevan
authored andcommitted
Pull rust compiler sources from Git and ship them with the toolchain
Working with tarballs makes it harder to sheriff as you can't specify a git hash to work with. Shipping the sources with the toolchain ensures devs can build the correct stdlib for the compiler at all times. Bug: 1401042 Change-Id: I5094016392d268aafe746d8852a7544fc8d11f4f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4190357 Reviewed-by: Hans Wennborg <[email protected]> Commit-Queue: danakj <[email protected]> Reviewed-by: Collin Baker <[email protected]> Cr-Commit-Position: refs/heads/main@{#1097651} NOKEYCHECK=True GitOrigin-RevId: fed1e1b696e015961c7ddf834c4ce0623e55cafd
1 parent cdfda44 commit 96986d3

File tree

5 files changed

+172
-35
lines changed

5 files changed

+172
-35
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,3 +301,18 @@ Crubit is built on the bots, the tests are commented out in
301301
`//build/rust/tests/BUILD.gn`, but they should still be built and run before
302302
rolling Crubit. TODO(https://crbug.com/1329611): Rephrase this paragraph
303303
after Crubit is built and tested on the bots.
304+
305+
## Possible Failures
306+
307+
### Missing dependencies
308+
309+
`build_rust.py` will vendor all dependencies before starting the build. To do
310+
this it first initializes git submodules. Then it runs `cargo vendor`. However
311+
some parts of the compiler build are excluded from the top level Cargo.toml
312+
workspace. Thus it passes `--sync dir` for a number of subdirectories, based
313+
on [dist.rs, the nightly tarball packager](
314+
https://github.com/rust-lang/rust/blob/master/src/bootstrap/dist.rs#L986-L995).
315+
316+
If another Cargo.toml is required in the future, and not part of the workspace
317+
it would produce missing dependencies, and the set of directories in
318+
`build_rust.py` would need to be updated.

build_rust.py

Lines changed: 137 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,18 @@
3333
'''
3434

3535
import argparse
36+
import base64
3637
import collections
3738
import hashlib
39+
import json
3840
import platform
3941
import os
4042
import pipes
4143
import shutil
4244
import string
4345
import subprocess
4446
import sys
47+
import urllib
4548

4649
from pathlib import Path
4750

@@ -51,23 +54,25 @@
5154
'scripts'))
5255

5356
from build import (AddCMakeToPath, AddOpenSSLToEnv, AddZlibToPath,
54-
GetLibXml2Dirs, RunCommand)
55-
from update import (CLANG_REVISION, CLANG_SUB_REVISION, LLVM_BUILD_DIR,
56-
GetDefaultHostOs, RmTree, UpdatePackage)
57+
CheckoutGitRepo, GetLibXml2Dirs, LLVM_BUILD_TOOLS_DIR,
58+
RunCommand)
59+
from update import (CLANG_REVISION, CLANG_SUB_REVISION, DownloadAndUnpack,
60+
LLVM_BUILD_DIR, GetDefaultHostOs, RmTree, UpdatePackage)
5761
import build
5862

5963
from update_rust import (CHROMIUM_DIR, RUST_REVISION, RUST_SUB_REVISION,
6064
RUST_TOOLCHAIN_OUT_DIR, STAGE0_JSON_SHA256,
6165
THIRD_PARTY_DIR, VERSION_STAMP_PATH,
6266
GetPackageVersionForBuild)
6367

64-
RUST_GIT_URL = 'https://github.com/rust-lang/rust/'
68+
RUST_GIT_URL = ('https://chromium.googlesource.com/external/' +
69+
'github.com/rust-lang/rust')
6570

6671
RUST_SRC_DIR = os.path.join(THIRD_PARTY_DIR, 'rust_src', 'src')
6772
STAGE0_JSON_PATH = os.path.join(RUST_SRC_DIR, 'src', 'stage0.json')
6873
# Download crates.io dependencies to rust-src subdir (rather than $HOME/.cargo)
6974
CARGO_HOME_DIR = os.path.join(RUST_SRC_DIR, 'cargo-home')
70-
RUST_SRC_VERSION_FILE_PATH = os.path.join(RUST_SRC_DIR, 'version')
75+
RUST_SRC_VERSION_FILE_PATH = os.path.join(RUST_SRC_DIR, 'src', 'version')
7176
RUST_SRC_GIT_COMMIT_INFO_FILE_PATH = os.path.join(RUST_SRC_DIR,
7277
'git-commit-info')
7378
RUST_TOOLCHAIN_LIB_DIR = os.path.join(RUST_TOOLCHAIN_OUT_DIR, 'lib')
@@ -77,6 +82,8 @@
7782
'vendor')
7883
RUST_CONFIG_TEMPLATE_PATH = os.path.join(
7984
os.path.dirname(os.path.abspath(__file__)), 'config.toml.template')
85+
RUST_CARGO_CONFIG_TEMPLATE_PATH = os.path.join(
86+
os.path.dirname(os.path.abspath(__file__)), 'cargo-config.toml.template')
8087
RUST_SRC_VENDOR_DIR = os.path.join(RUST_SRC_DIR, 'vendor')
8188

8289
if sys.platform == 'win32':
@@ -93,8 +100,8 @@
93100
# Which test suites to run. Any failure will fail the build.
94101
TEST_SUITES = [
95102
'library/std',
96-
'src/test/codegen',
97-
'src/test/ui',
103+
'tests/codegen',
104+
'tests/ui',
98105
]
99106

100107
EXCLUDED_TESTS = [
@@ -142,6 +149,82 @@ def quote_string(s: str):
142149
output.write(template.substitute(subs))
143150

144151

152+
def FetchCargo(rust_git_hash):
153+
'''Downloads the beta Cargo package specified for the compiler build
154+
155+
Unpacks the package and returns the path to the binary itself.
156+
'''
157+
if sys.platform == 'win32':
158+
target = 'cargo-beta-x86_64-pc-windows-msvc'
159+
elif sys.platform == 'darwin':
160+
if platform.machine() == 'arm64':
161+
target = 'rust-std-beta-aarch64-apple-darwin'
162+
else:
163+
target = 'cargo-beta-x86_64-apple-darwin'
164+
else:
165+
target = 'cargo-beta-x86_64-unknown-linux-gnu'
166+
167+
# Pull the stage0 JSON to find the Cargo binary intended to be used to
168+
# build this version of the Rust compiler.
169+
STAGE0_JSON_URL = (
170+
'https://chromium.googlesource.com/external/github.com/'
171+
'rust-lang/rust/+/{GIT_HASH}/src/stage0.json?format=TEXT')
172+
base64_text = urllib.request.urlopen(
173+
STAGE0_JSON_URL.format(GIT_HASH=rust_git_hash)).read().decode("utf-8")
174+
stage0 = json.loads(base64.b64decode(base64_text))
175+
176+
# The stage0 JSON contains the path to all tarballs it uses binaries from,
177+
# including cargo.
178+
for k in stage0['checksums_sha256'].keys():
179+
if k.endswith(target + '.tar.gz'):
180+
cargo_tgz = k
181+
182+
server = stage0['config']['dist_server']
183+
DownloadAndUnpack(f'{server}/{cargo_tgz}', LLVM_BUILD_TOOLS_DIR)
184+
185+
bin_path = os.path.join(LLVM_BUILD_TOOLS_DIR, target, 'cargo', 'bin')
186+
if sys.platform == 'win32':
187+
return os.path.join(bin_path, 'cargo.exe')
188+
else:
189+
return os.path.join(bin_path, 'cargo')
190+
191+
192+
def CargoVendor(cargo_bin):
193+
'''Runs `cargo vendor` to pull down dependencies.'''
194+
os.chdir(RUST_SRC_DIR)
195+
196+
# Some Submodules are part of the workspace and need to exist (so we can
197+
# read their Cargo.toml files) before we can vendor their deps.
198+
RunCommand([
199+
'git', 'submodule', 'update', '--init', '--recursive', '--depth', '1'
200+
])
201+
202+
# From https://github.com/rust-lang/rust/blob/master/src/bootstrap/dist.rs#L986-L995
203+
# The additional `--sync` Cargo.toml files are not part of the top level
204+
# workspace.
205+
RunCommand([
206+
cargo_bin,
207+
'vendor',
208+
'--locked',
209+
'--versioned-dirs',
210+
'--sync',
211+
'src/tools/rust-analyzer/Cargo.toml',
212+
'--sync',
213+
'compiler/rustc_codegen_cranelift/Cargo.toml',
214+
'--sync',
215+
'src/bootstrap/Cargo.toml',
216+
])
217+
218+
# Make a `.cargo/config.toml` the points to the `vendor` directory for all
219+
# dependency crates.
220+
try:
221+
os.mkdir(os.path.join(RUST_SRC_DIR, '.cargo'))
222+
except FileExistsError:
223+
pass
224+
shutil.copyfile(RUST_CARGO_CONFIG_TEMPLATE_PATH,
225+
os.path.join(RUST_SRC_DIR, '.cargo', 'config.toml'))
226+
227+
145228
def RunXPy(sub, args, llvm_bins_path, zlib_path, libxml2_dirs, build_mac_arm,
146229
gcc_toolchain_path, verbose):
147230
''' Run x.py, Rust's build script'''
@@ -152,6 +235,7 @@ def RunXPy(sub, args, llvm_bins_path, zlib_path, libxml2_dirs, build_mac_arm,
152235
'LDFLAGS',
153236
'RUSTFLAGS_BOOTSTRAP',
154237
'RUSTFLAGS_NOT_BOOTSTRAP',
238+
'RUSTDOCFLAGS',
155239
]
156240

157241
RUSTENV = collections.defaultdict(str, os.environ)
@@ -220,6 +304,10 @@ def RunXPy(sub, args, llvm_bins_path, zlib_path, libxml2_dirs, build_mac_arm,
220304
RUSTENV['RUSTFLAGS_NOT_BOOTSTRAP'] += (
221305
f' -L native={gcc_toolchain_path}/lib64')
222306

307+
# Rustdoc should use our clang linker as well, as we pass flags that
308+
# the system linker may not understand.
309+
RUSTENV['RUSTDOCFLAGS'] += f' -Clinker={RUSTENV["CC"]}'
310+
223311
# Cargo normally stores files in $HOME. Override this.
224312
RUSTENV['CARGO_HOME'] = CARGO_HOME_DIR
225313

@@ -243,16 +331,27 @@ def GetTestArgs():
243331
return args
244332

245333

246-
def GetVersionStamp():
247-
# We must generate a version stamp that contains the expected `rustc
248-
# --version` output. This contains the Rust release version, git commit data
249-
# that the nightly tarball was generated from, and chromium-specific package
250-
# information.
334+
def MakeVersionStamp(git_hash):
335+
# We must generate a version stamp that contains the full version of the
336+
# built Rust compiler:
337+
# * The version number returned from `rustc --version`.
338+
# * The git hash.
339+
# * The chromium revision name of the compiler build, which includes the
340+
# associated clang/llvm version.
251341
with open(RUST_SRC_VERSION_FILE_PATH) as version_file:
252342
rust_version = version_file.readline().rstrip()
343+
return (f'rustc {rust_version} {git_hash}'
344+
f' ({GetPackageVersionForBuild()} chromium)\n')
345+
253346

254-
return ('rustc %s (%s chromium)\n' %
255-
(rust_version, GetPackageVersionForBuild()))
347+
def GetLatestRustCommit():
348+
"""Get the latest commit hash in the LLVM monorepo."""
349+
main = json.loads(
350+
urllib.request.urlopen('https://chromium.googlesource.com/external/' +
351+
'github.com/rust-lang/rust/' +
352+
'+/refs/heads/main?format=JSON').read().decode(
353+
"utf-8").replace(")]}'", ""))
354+
return main['commit']
256355

257356

258357
def main():
@@ -271,6 +370,9 @@ def main():
271370
help=
272371
'checkout Rust, verify the stage0 hash, then quit without building. '
273372
'Will print the actual hash if different than expected.')
373+
parser.add_argument('--skip-checkout',
374+
action='store_true',
375+
help='do not create or update any checkouts')
274376
parser.add_argument('--skip-clean',
275377
action='store_true',
276378
help='skip x.py clean step')
@@ -280,6 +382,9 @@ def main():
280382
parser.add_argument('--skip-install',
281383
action='store_true',
282384
help='do not install to RUST_TOOLCHAIN_OUT_DIR')
385+
parser.add_argument('--rust-force-head-revision',
386+
action='store_true',
387+
help='build the latest revision')
283388
parser.add_argument(
284389
'--fetch-llvm-libs',
285390
action='store_true',
@@ -324,6 +429,14 @@ def main():
324429
else:
325430
llvm_bins_path = os.path.join(build.LLVM_BOOTSTRAP_DIR, 'bin')
326431

432+
if args.rust_force_head_revision:
433+
checkout_revision = GetLatestRustCommit()
434+
else:
435+
checkout_revision = RUST_REVISION
436+
437+
if not args.skip_checkout:
438+
CheckoutGitRepo('Rust', RUST_GIT_URL, checkout_revision, RUST_SRC_DIR)
439+
327440
VerifyStage0JsonHash()
328441
if args.verify_stage0_hash:
329442
# The above function exits and prints the actual hash if verification
@@ -344,6 +457,9 @@ def main():
344457
# Set up config.toml in Rust source tree to configure build.
345458
Configure(llvm_bins_path, llvm_libs_root)
346459

460+
cargo_bin = FetchCargo(checkout_revision)
461+
CargoVendor(cargo_bin)
462+
347463
AddCMakeToPath()
348464

349465
# Require zlib compression.
@@ -404,19 +520,18 @@ def main():
404520
shutil.rmtree(RUST_TOOLCHAIN_OUT_DIR)
405521

406522
RunXPy('install', DISTRIBUTION_ARTIFACTS, llvm_bins_path, zlib_path,
407-
libxml2_dirs, args.gcc_toolchain, args.verbose)
523+
libxml2_dirs, args.build_mac_arm, args.gcc_toolchain, args.verbose)
524+
525+
# Copy additional sources required for building stdlib out of
526+
# RUST_TOOLCHAIN_SRC_DIST_DIR.
527+
print(f'Copying vendored dependencies to {RUST_TOOLCHAIN_OUT_DIR} ...')
528+
shutil.copytree(RUST_SRC_VENDOR_DIR, RUST_TOOLCHAIN_SRC_DIST_VENDOR_DIR)
408529

409530
with open(VERSION_STAMP_PATH, 'w') as stamp:
410-
stamp.write(GetVersionStamp())
531+
stamp.write(MakeVersionStamp(checkout_revision))
411532

412533
return 0
413534

414-
# TODO(crbug.com/1342708): fix vendoring and re-enable.
415-
# x.py installed library sources to our toolchain directory. We also need to
416-
# copy the vendor directory so Chromium checkouts have all the deps needed
417-
# to build std.
418-
# shutil.copytree(RUST_SRC_VENDOR_DIR, RUST_TOOLCHAIN_SRC_DIST_VENDOR_DIR)
419-
420535

421536
if __name__ == '__main__':
422537
sys.exit(main())

cargo-config.toml.template

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright 2022 The Chromium Authors
2+
# Use of this source code is governed by a BSD-style license that can be
3+
# found in the LICENSE file.
4+
5+
# This file is placed in `.cargo/config/` when building rustc.
6+
7+
[source.crates-io]
8+
replace-with = "vendored-sources"
9+
10+
[source.vendored-sources]
11+
directory = "vendor"

config.toml.template

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,15 @@ channel = "nightly"
1818
description = "$PACKAGE_VERSION chromium"
1919

2020
[build]
21-
# Vendor crates.io dependencies to rust-src/vendor and check they match root
22-
# Cargo.lock. In the future we will probably want to vendor these ourself like
23-
# we do with third_party/rust dependencies. For now, the Rust build script auto
24-
# fetches the dependencies.
25-
#
26-
# This is added now to work around a permissions bug in Rust's bootstrap:
27-
# it fails to run under sudo even if the sudo user is not root, unless sources
28-
# are vendored in tree.
29-
locked-deps = true
21+
# We pull all dependencies into the src/vendor/ directory.
3022
vendor = true
3123

24+
# Check that vendored dependencies match the root Cargo.lock.
25+
locked-deps = true
26+
27+
# Don't build documentation for the stdlib.
28+
docs = false
29+
3230
# We enable profiler so that PGO and coverage build options are available
3331
# with the rust compiler we build.
3432
profiler = true

update_rust.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@
2929

3030
# These fields are written by //tools/clang/scripts/upload_revision.py, and
3131
# should not be changed manually.
32-
RUST_REVISION_TAG = '2'
33-
RUST_REVISION = '20221209'
32+
RUST_REVISION = 'c8e6a9e8b6251bbc8276cb78cabe1998deecbed7'
3433
RUST_SUB_REVISION = 1
3534

3635
# Trunk on 2022-10-15.
@@ -98,8 +97,7 @@ def GetStampVersion():
9897
if os.path.exists(RUST_TOOLCHAIN_OUT_DIR):
9998
with open(VERSION_STAMP_PATH) as version_file:
10099
existing_stamp = version_file.readline().rstrip()
101-
version_re = re.compile(
102-
r'rustc [0-9.]+-nightly \([0-9a-f -]+\) \((.+?) chromium\)')
100+
version_re = re.compile(r'rustc [0-9.]+ [0-9a-f]+ \((.+?) chromium\)')
103101
match = version_re.fullmatch(existing_stamp)
104102
if match is None:
105103
return None

0 commit comments

Comments
 (0)