blob: a8e9bb41c4c1f84fe78d6380c200fdcce338e43a [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.
'''Builds the Crubit tools.
Builds the Crubit tools for generating Rust/C++ FFI bindings.
This script must be run after //tools/rust/build_rust.py as it uses the outputs
of that script in the compilation of Crubit. In particular it uses:
- The rust toolchain binaries and libraries in `RUST_TOOLCHAIN_OUT_DIR`.
- In the future (if/when building `rs_bindings_from_cc`) it may also use:
The LLVM and Clang libraries and headers in `RUST_HOST_LLVM_INSTALL_DIR`.
This script:
- Clones the Crubit repository, checks out a defined revision.
- Builds Crubit's `cc_bindings_from_rs` using Cargo.
- Copies cc_bindings_from_rs into `RUST_TOOLCHAIN_OUT_DIR`.
The `rs_bindings_from_cs` binary is not yet built,
as Cargo builds of `rs_bindings_from_cs` are not yet
officially supported by the Crubit team.
'''
import argparse
import json
import os
import platform
import shutil
import sys
import tempfile
import urllib
from pathlib import Path
# Get variables and helpers from `//tools/clang/scripts/build.py`.
sys.path.append(
os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'clang',
'scripts'))
from build import (AddZlibToPath, GetLibXml2Dirs, CheckoutGitRepo)
from update_rust import (CHROMIUM_DIR, CRUBIT_REVISION, RUST_TOOLCHAIN_OUT_DIR)
# Get `RunCargo` from `//tools/crates/run_cargo.py`.
sys.path.append(
os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'crates'))
from run_cargo import RunCargo
CRUBIT_GIT = 'https://chromium.googlesource.com/external/github.com/google/crubit'
CRUBIT_SRC_DIR = os.path.join(CHROMIUM_DIR, 'third_party',
'rust-toolchain-intermediate', 'crubit')
CC_BINDINGS_FROM_RS_CARGO_TOML_PATH = os.path.join(CRUBIT_SRC_DIR, "cargo",
"cc_bindings_from_rs",
"cc_bindings_from_rs",
"Cargo.toml")
EXE = '.exe' if sys.platform == 'win32' else ''
def GetLatestCrubitCommit():
"""Get the latest commit hash in the Crubit repo."""
url = CRUBIT_GIT + '/+/refs/heads/upstream/main?format=JSON'
main = json.loads(
urllib.request.urlopen(url).read().decode("utf-8").replace(")]}'", ""))
return main['commit']
def GetCcBindingsFromRsRustFlags():
# Need to help the runtime linker find the path to
# `librustc_driver-xxxxxxxxxxxxxxxx.so`. This mimics how `rustc` is built
# as seen in
# https://github.com/rust-lang/rust/blob/b889870082dd0b0e3594bbfbebb4545d54710829/src/bootstrap/src/core/builder/cargo.rs#L285-L306
# See also https://crbug.com/460482110#comment14 - #comment16
if sys.platform == 'darwin':
return [
"-Zosx-rpath-install-name",
"-Clink-args=-Wl,-rpath,@loader_path/../lib"
]
elif sys.platform != 'win32':
return [
"-Clink-args=-Wl,-z,origin",
"-Clink-args=-Wl,-rpath,$ORIGIN/../lib"
]
else:
return []
def GetNativeLibsRustFlags():
if sys.platform == 'win32':
# See https://crbug.com/481661885 to learn why adding `zlib.lib` and
# `libxml2s.lib` paths is required to build `cc_bindings_from_rs` on
# Windows when using Chromium-built Rust sysroot.
#
# Note that some of the calls below may be expensive (e.g. downloading
# zlib sources and building it) so `GetNativeLibsRustFlags` probably
# shouldn't be called in incremental builds (e.g. when
# `--skip-checkout` is present).
libxml2_lib_path = GetLibXml2Dirs().lib_dir
zlib_lib_path = AddZlibToPath()
return [
f'-Clink-arg=/LIBPATH:{libxml2_lib_path}',
f'-Clink-arg=/LIBPATH:{zlib_lib_path}'
]
# No native libs needed on other platforms:
return []
def BuildCrubit(rust_sysroot, out_dir, skip_checkout):
target_dir = os.path.abspath(os.path.join(out_dir, 'target'))
release_dir = os.path.join(target_dir, 'release')
home_dir = os.path.join(target_dir, 'cargo_home')
print(f'Building cc_bindings_from_rs ...')
cargo_args = ['build', '--release', '--verbose']
cargo_args += ['--bin', 'cc_bindings_from_rs']
cargo_args += ['--target-dir', target_dir]
cargo_args += ['--manifest-path', CC_BINDINGS_FROM_RS_CARGO_TOML_PATH]
extra_rustflags = GetCcBindingsFromRsRustFlags()
if not skip_checkout:
extra_rustflags += GetNativeLibsRustFlags()
cargo_result = RunCargo(rust_sysroot, home_dir, cargo_args,
extra_rustflags)
print(f'Building cc_bindings_from_rs ... done. Result: {cargo_result}')
if cargo_result:
return cargo_result
print(f'Installing Crubit to {RUST_TOOLCHAIN_OUT_DIR} ...')
CRUBIT_BINS = ['cc_bindings_from_rs']
for bin in CRUBIT_BINS:
bin = bin + EXE
print(f' Copying {bin} ...')
shutil.copy(os.path.join(release_dir, bin),
os.path.join(RUST_TOOLCHAIN_OUT_DIR, 'bin', bin))
print(f'Installing `crubit/support` to {RUST_TOOLCHAIN_OUT_DIR} ...')
crubit_support_install_dir = os.path.join(RUST_TOOLCHAIN_OUT_DIR, 'lib',
'crubit', 'support')
os.makedirs(crubit_support_install_dir, exist_ok=True)
crubit_support_src_dir = os.path.join(CRUBIT_SRC_DIR, 'support')
shutil.copytree(crubit_support_src_dir,
crubit_support_install_dir,
dirs_exist_ok=True)
return 0
def main():
parser = argparse.ArgumentParser(
description='Build and package Crubit tools')
parser.add_argument(
'--skip-checkout',
action='store_true',
help=('skip checking out source code. Useful for trying local'
'changes'))
parser.add_argument(
'--out-dir',
help='cache artifacts in specified directory instead of a temp dir.')
parser.add_argument('--debug',
action='store_true',
help=('build Crubit in debug mode'))
parser.add_argument('--crubit-force-head-revision',
action='store_true',
help=('build the most recent commit of crubit '
'instead of the current pinned version'))
args = parser.parse_args()
if args.crubit_force_head_revision:
crubit_revision = GetLatestCrubitCommit()
else:
crubit_revision = CRUBIT_REVISION
if not args.skip_checkout:
CheckoutGitRepo("crubit", CRUBIT_GIT, crubit_revision, CRUBIT_SRC_DIR)
if args.out_dir:
return BuildCrubit(RUST_TOOLCHAIN_OUT_DIR, args.out_dir,
args.skip_checkout)
else:
with tempfile.TemporaryDirectory() as out_dir:
return BuildCrubit(RUST_TOOLCHAIN_OUT_DIR, out_dir,
args.skip_checkout)
if __name__ == '__main__':
sys.exit(main())