Skip to content

Commit 6f719b2

Browse files
committed
install-build-deps: add rust toolchain support
Also adds rust formatting support as it demonstrates how to use the installed toolchain.
1 parent d857fe8 commit 6f719b2

File tree

8 files changed

+154
-3
lines changed

8 files changed

+154
-3
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
.aider*
22
.android_config
33
.cache
4+
.cargo
45
.ccls
56
.ccls-cache
67
.clangd

buildtools/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ buildtools_hash_revision_for_cache_on_ci.txt
3939
/open_csd/
4040
/pigweed/
4141
/protobuf/
42+
/rustup/
4243
/sqlglot/
4344
/sqlite_src/
4445
/sqlite/

contrib/rust-sdk/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
debug
2+
target

python/tools/code_format_all.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from code_format_gn import GnFormat
2121
from code_format_python import Yapf
2222
from code_format_sql import SqlGlot
23+
from code_format_rust import RustFormat
2324
from code_format_ui import UI_CODE_FORMATTERS
2425

2526
if __name__ == '__main__':
@@ -28,5 +29,6 @@
2829
GnFormat(),
2930
Yapf(),
3031
SqlGlot(),
32+
RustFormat(),
3133
] + UI_CODE_FORMATTERS
3234
sys.exit(run_code_formatters(formatters))

python/tools/code_format_rust.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/usr/bin/env python3
2+
# Copyright (C) 2025 The Android Open Source Project
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
import os
17+
import sys
18+
19+
from code_format_utils import ROOT_DIR, CodeFormatterBase, run_code_formatters
20+
21+
RUSTUP_HOME = os.path.join(ROOT_DIR, 'buildtools/rustup')
22+
CARGO_HOME = os.path.join(ROOT_DIR, '.cargo')
23+
24+
25+
class RustFormat(CodeFormatterBase):
26+
27+
def __init__(self):
28+
super().__init__(name='rustfmt', exts=['.rs'])
29+
30+
def run_formatter(self, repo_root: str, check_only: bool, files: list[str]):
31+
tool = '.cargo/bin/rustfmt'
32+
if not os.path.exists(tool):
33+
err = f'Cannot find {tool}\nRun tools/install-build-deps --rust-toolchain'
34+
print(err, file=sys.stderr)
35+
return 127
36+
cmd = [tool, '--edition', '2024', '--unstable-features', '--skip-children']
37+
if check_only:
38+
cmd += ['--check']
39+
cmd += files
40+
env = os.environ.copy()
41+
env['RUSTUP_HOME'] = RUSTUP_HOME
42+
env['CARGO_HOME'] = CARGO_HOME
43+
return self.check_call(cmd, env=env)
44+
45+
def print_fix_hint(self):
46+
print('Run tools/format-rust-sources to fix', file=sys.stderr)
47+
48+
49+
if __name__ == '__main__':
50+
sys.exit(run_code_formatters([RustFormat()]))

python/tools/code_format_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,9 @@ def build_file_list(args):
9191
cmd = ['git', 'diff', '--name-only', '--diff-filter=crd', upstream_branch]
9292
return subprocess.check_output(cmd, text=True).strip().splitlines()
9393

94-
def check_call(self, cmd, **kwargs):
94+
def check_call(self, cmd, env=None, **kwargs):
9595
try:
96-
subprocess.check_call(cmd, **kwargs)
96+
subprocess.check_call(cmd, env=env, **kwargs)
9797
return 0
9898
except subprocess.CalledProcessError as ex:
9999
print('`%s` returned %d' % (' '.join(cmd)[:128], ex.returncode))

tools/format-rust-sources

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/usr/bin/env bash
2+
# Copyright (C) 2025 The Android Open Source Project
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
set -eu
17+
ROOT_DIR="$(dirname $(cd -P ${BASH_SOURCE[0]%/*}; pwd))"
18+
exec /usr/bin/env python3 $ROOT_DIR/python/tools/code_format_rust.py "$@"

tools/install-build-deps

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,14 @@ BIGTRACE_DEPS = [
495495
'6c3e8482f7b4e3b307bb42afbb85fd8771da86b8', 'all', 'all', True)
496496
]
497497

498+
# Dependencies to build Rust SDK.
499+
BUILD_DEPS_RUST = [
500+
Dependency(
501+
'buildtools/rustup/rustup-init.sh', 'https://sh.rustup.rs',
502+
'17247e4bcacf6027ec2e11c79a72c494c9af69ac8d1abcc1b271fa4375a106c2',
503+
'all', 'all'),
504+
]
505+
498506
# Sysroots required to cross-compile Linux targets (linux-arm{,64}).
499507
# These are taken from Chromium's build/linux/sysroot_scripts/sysroots.json.
500508
BUILD_DEPS_LINUX_CROSS_SYSROOTS = [
@@ -513,7 +521,7 @@ BUILD_DEPS_LINUX_CROSS_SYSROOTS = [
513521
ALL_DEPS = (
514522
BUILD_DEPS_HOST + BUILD_DEPS_BAZEL + BUILD_DEPS_ANDROID +
515523
BUILD_DEPS_LINUX_CROSS_SYSROOTS + TEST_DEPS_ANDROID +
516-
EMULATOR_DEPS_ANDROID + UI_DEPS)
524+
EMULATOR_DEPS_ANDROID + UI_DEPS + BUILD_DEPS_RUST)
517525

518526
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
519527
UI_DIR = os.path.join(ROOT_DIR, 'ui')
@@ -526,6 +534,11 @@ PYTHON_VENV_BIN_DIR = os.path.join(
526534
PYTHON_STATUS_FILE = os.path.join(PYTHON_VENV_DIR, '.last_install')
527535
PYTHON_REQUIREMENTS = os.path.join(ROOT_DIR, 'python', 'requirements.txt')
528536
TEST_DATA_SCRIPT = os.path.join(TOOLS_DIR, 'test_data')
537+
RUST_TOOLCHAIN = os.environ.get("RUST_TOOLCHAIN", "nightly-2025-01-03")
538+
RUSTUP_HOME = os.path.join(ROOT_DIR, 'buildtools/rustup')
539+
RUSTUP_INIT_FILE = os.path.join(RUSTUP_HOME, 'rustup-init.sh')
540+
CARGO_HOME = os.path.join(ROOT_DIR, '.cargo')
541+
RUSTUP_FILE = os.path.join(CARGO_HOME, 'bin/rustup')
529542

530543

531544
def CheckCallRetry(*args, **kwargs):
@@ -693,6 +706,55 @@ def CheckNodeModules():
693706
return expected == actual
694707

695708

709+
def InstallRustToolchain(force_clean=False):
710+
if force_clean:
711+
logging.info('Clearing %s', RUSTUP_HOME)
712+
subprocess.check_call(
713+
['git', 'clean', '-qxffd', RUSTUP_HOME, '--exclude=rustup-init.sh'],
714+
cwd=ROOT_DIR)
715+
logging.info('Clearing %s', CARGO_HOME)
716+
subprocess.check_call(['git', 'clean', '-qxffd', CARGO_HOME], cwd=ROOT_DIR)
717+
718+
cmd = [
719+
'sh', RUSTUP_INIT_FILE, '-y', f'--default-toolchain={RUST_TOOLCHAIN}',
720+
'--no-modify-path', '--profile=minimal', '--component=rustfmt,clippy'
721+
]
722+
env = os.environ.copy()
723+
env['RUSTUP_HOME'] = RUSTUP_HOME
724+
env['CARGO_HOME'] = CARGO_HOME
725+
logging.info(f'Installing rust toolchain using `{" ".join(cmd)}`')
726+
subprocess.check_call(cmd, cwd=ROOT_DIR, env=env)
727+
728+
729+
def CheckRustToolchain():
730+
"""Returns True if the toolchain is up-to-date.
731+
732+
Checks if Rust toolchains are up to date using `rustup check`
733+
and returns True if up to date, False otherwise.
734+
"""
735+
if not os.path.exists(RUSTUP_FILE):
736+
return False
737+
738+
# Run rustup check and capture its output and exit code.
739+
env = os.environ.copy()
740+
env['RUSTUP_HOME'] = RUSTUP_HOME
741+
env['CARGO_HOME'] = CARGO_HOME
742+
result = subprocess.run([RUSTUP_FILE, "check"],
743+
capture_output=True,
744+
text=True,
745+
check=False)
746+
if result.returncode == 0:
747+
if "update available" not in result.stdout.lower(
748+
) and "update available" not in result.stderr.lower():
749+
return True
750+
logging.info("rustup check stdout:")
751+
logging.info(result.stdout)
752+
logging.info("rustup check stderr:")
753+
logging.info(result.stderr)
754+
logging.info(f"rustup check exit code: {result.returncode}")
755+
return False
756+
757+
696758
def CheckPythonVenv():
697759
"""Returns True if the python venv is up-to-date."""
698760
if not os.path.exists(PYTHON_STATUS_FILE):
@@ -799,6 +861,10 @@ def Main():
799861
default=GetArch(),
800862
choices=['arm64', 'x64'],
801863
help='Override the autodetected build CPU architecture')
864+
parser.add_argument(
865+
'--rust-toolchain',
866+
action='store_true',
867+
help='Rust toolchain to build Rust SDK')
802868
args = parser.parse_args()
803869
if args.verify:
804870
CheckHashes()
@@ -830,8 +896,11 @@ def Main():
830896
# TODO(b/360084012) Change the arg name to bigtrace
831897
if args.grpc:
832898
deps += BIGTRACE_DEPS
899+
if args.rust_toolchain:
900+
deps += BUILD_DEPS_RUST
833901
deps_updated = False
834902
nodejs_updated = False
903+
rustup_updated = False
835904

836905
for old_dir in CLEANUP_OLD_DIRS:
837906
RmtreeIfExists(os.path.join(ROOT_DIR, old_dir))
@@ -877,6 +946,8 @@ def Main():
877946
shutil.move(download_path, local_path)
878947
if 'nodejs' in dep.target_folder:
879948
nodejs_updated = True
949+
if 'rustup' in dep.target_folder:
950+
rustup_updated = True
880951

881952
assert (HashLocalFile(local_path) == dep.checksum)
882953

@@ -935,6 +1006,12 @@ def Main():
9351006
elif venv_needs_update:
9361007
InstallPythonVenv()
9371008

1009+
if args.rust_toolchain:
1010+
if args.check_only:
1011+
deps_updated |= not CheckRustToolchain()
1012+
else:
1013+
InstallRustToolchain(force_clean=rustup_updated)
1014+
9381015
# Install the pre-push hook if the .git/hooks directory exists and it's a
9391016
# non check-only invocation. Not on windows (symlinks are not supported
9401017
# there).

0 commit comments

Comments
 (0)