# Copyright 2021 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # This file provides the Rust standard library for GN targets. # # For Rust targets, it either copies a prebuilt stdlib or builds a stdlib, and # then points rustc to it with `--sysroot`. # # When linking it ensures the libraries (and their C library dependencies) are # part of the linker line. If Rust drives the linking, this is redundant but if # Clang drives the linking it is required. # # Part of the standard library provided here is "remap_alloc" which maps # allocator functions to PartitionAlloc when `use_partition_alloc_as_malloc` is # true, so that Rust and C++ use the same allocator backend. import("//base/allocator/partition_allocator/partition_alloc.gni") import("//build/config/compiler/compiler.gni") import("//build/config/coverage/coverage.gni") import("//build/config/rust.gni") import("//build/config/sanitizers/sanitizers.gni") if (toolchain_has_rust) { # If clang performs the link step, we need to provide the allocator symbols # that are normally injected by rustc during linking. # # We also "happen to" use this to redirect allocations to PartitionAlloc, # though that would be better done through a #[global_allocator] crate (see # above). source_set("remap_alloc") { public_deps = [ "//base/allocator/partition_allocator:buildflags" ] if (use_partition_alloc_as_malloc) { public_deps += [ "//base/allocator/partition_allocator:partition_alloc" ] } sources = [ # `alias.*`, `compiler_specific.h`, and `immediate_crash.*` have been # copied from `//base`. # TODO(crbug.com/40279749): Avoid duplication / reuse code. "alias.cc", "alias.h", "compiler_specific.h", "immediate_crash.h", "remap_alloc.cc", ] } # List of Rust stdlib rlibs which are present in the official Rust toolchain # we are using from the Android team. This is usually a version or two behind # nightly. Generally this matches the toolchain we build ourselves, but if # they differ, append or remove libraries based on the # `use_chromium_rust_toolchain` GN variable. # # If the build fails due to missing symbols, it would be because of a missing # library that needs to be added here in a newer stdlib. stdlib_files = [ "std", # List first because it makes depfiles more debuggable (see below) "alloc", "cfg_if", "compiler_builtins", "core", "getopts", "hashbrown", "panic_abort", "panic_unwind", "rustc_demangle", "std_detect", "test", "unicode_width", "unwind", ] if (!is_win) { # These are no longer present in the Windows toolchain. stdlib_files += [ "addr2line", "adler", "gimli", "libc", "memchr", "miniz_oxide", "object", ] } if (toolchain_for_rust_host_build_tools) { # When building proc macros, include the proc_macro crate in what should be # copied with find_stdlib. Otherwise it is not copied since it will be # unused. stdlib_files += [ "proc_macro" ] } # Different Rust toolchains may add or remove files relative to the above # list. That can be specified in gn args for anyone using (for instance) # nightly or some other experimental toolchain, prior to it becoming official. stdlib_files -= removed_rust_stdlib_libs stdlib_files += added_rust_stdlib_libs # rlib files which are distributed alongside Rust's prebuilt stdlib, but we # don't need to pass to the C++ linker because they're used for specialized # purposes. skip_stdlib_files = [ "profiler_builtins", "rustc_std_workspace_alloc", "rustc_std_workspace_core", "rustc_std_workspace_std", ] config("stdlib_dependent_libs") { # TODO(crbug.com/40264561): These should really be `libs`, however that # breaks. Normally, we specify lib files with the `.lib` suffix but # then when rustc links an EXE, it invokes lld-link with `.lib.lib` # instead. # # Omitting the `.lib` suffix breaks linking as well, when clang drives # the linking step of a C++ EXE that depends on Rust. if (is_win) { # The libc crate tries to link in the Windows CRT, but we specify the CRT # library ourselves in //build/config/win:dynamic_crt and # //build/config/win:static_crt because Rustc does not allow us to specify # using the debug CRT: https://github.com/rust-lang/rust/issues/39016 # # As such, we have disabled all #[link] directives from the libc crate, # and we need to add any non-CRT libs here. ldflags = [ "legacy_stdio_definitions.lib" ] } } config("stdlib_public_dependent_libs") { # TODO(crbug.com/40264561): These should really be `libs`, however that # breaks. Normally, we specify lib files with the `.lib` suffix but # then when rustc links an EXE, it invokes lld-link with `.lib.lib` # instead. # # Omitting the `.lib` suffix breaks linking as well, when clang drives # the linking step of a C++ EXE that depends on Rust. if (is_win) { # These libs provide functions that are used by the stdlib. Rust crates # will try to link them in with #[link] directives. However these don't # get propagated to the linker if Rust isn't driving the linking (a C++ # target that depends on a Rust rlib). So these need to be specified # explicitly. ldflags = [ "advapi32.lib", "bcrypt.lib", "kernel32.lib", "ntdll.lib", "synchronization.lib", "userenv.lib", "ws2_32.lib", ] } # From rust/library/std/src/sys/unix/mod.rs. # TODO(danakj): We should generate this list somehow when building or rolling # the Rust toolchain? if (is_android) { libs = [ "dl" ] } else if (target_os == "freebsd") { libs = [ "execinfo", "pthread", ] } else if (target_os == "netbsd") { libs = [ "rt", "pthread", ] } else if (is_mac) { libs = [ "System" ] } else if (is_ios) { libs = [ "System", "objc", ] frameworks = [ "Security.framework", "Foundation.framework", ] } else if (is_fuchsia) { libs = [ "zircon", "fdio", ] } } # Construct sysroots for rustc invocations to better control what libraries # are linked. We have two: one with copied prebuilt libraries, and one with # our locally-built std. Both reside in root_out_dir: we must only have one of # each per GN toolchain anyway. sysroot_lib_subdir = "lib/rustlib/$rust_abi_target/lib" if (!rust_prebuilt_stdlib) { local_rustc_sysroot = "$root_out_dir/local_rustc_sysroot" # All std targets starting with core build with our sysroot. It starts empty # and is incrementally built. The directory must exist at the start. generated_file("empty_sysroot_for_std_build") { outputs = [ "$local_rustc_sysroot/$sysroot_lib_subdir/.empty" ] contents = "" visibility = [ ":*" ] } # Target to be depended on by std build targets. Creates the initially empty # sysroot. group("std_build_deps") { deps = [ ":empty_sysroot_for_std_build" ] public_configs = [ ":local_stdlib_sysroot" ] visibility = [ "rules:*" ] } profiler_builtins_crates = [ "core", "compiler_builtins", "profiler_builtins", ] # When using instrumentation, profiler_builtins and its deps must be built # before other std crates. Other crates depend on this target so they are # built in the right order. group("profiler_builtins_group") { deps = [] foreach(libname, profiler_builtins_crates) { deps += [ "rules:$libname" ] } visibility = [ "rules:*" ] } config("local_stdlib_sysroot") { sysroot = rebase_path(local_rustc_sysroot, root_build_dir) rustflags = [ "--sysroot=$sysroot" ] visibility = [ ":*" ] } # When given -Zsanitize=..., rustc insists on passing a sanitizer runtime to # the linker it invokes. Unfortunately, our C++ ldflags already tell the # linker to link against a C++ sanitizer runtime - which contains the same # symbols. So, make a blank library. # The list of relevant sanitizers here is taken from # https://github.com/rust-lang/rust/blob/7e7483d26e3cec7a44ef00cf7ae6c9c8c918bec6/compiler/rustc_codegen_ssa/src/back/link.rs#L1148 template("rustc_sanitizer_runtime") { rt_name = target_name not_needed([ "invoker" ]) static_library("sanitizer_rt_$rt_name") { sources = [] output_name = "librustc-${rust_channel}_rt.$rt_name" output_dir = "$local_rustc_sysroot/$sysroot_lib_subdir" if (is_win) { arflags = [ "/llvmlibempty" ] } } } rustc_sanitizer_runtimes = [] if (is_asan) { rustc_sanitizer_runtime("asan") { } rustc_sanitizer_runtimes += [ ":sanitizer_rt_asan" ] } if (is_lsan) { rustc_sanitizer_runtime("lsan") { } rustc_sanitizer_runtimes += [ ":sanitizer_rt_lsan" ] } if (is_msan) { rustc_sanitizer_runtime("msan") { } rustc_sanitizer_runtimes += [ ":sanitizer_rt_msan" ] } if (is_tsan) { rustc_sanitizer_runtime("tsan") { } rustc_sanitizer_runtimes += [ ":sanitizer_rt_tsan" ] } if (is_hwasan) { rustc_sanitizer_runtime("hwasan") { } rustc_sanitizer_runtimes += [ ":sanitizer_rt_hwasan" ] } # Builds and links against the Rust stdlib. Both Rust and C++ targets should # depend on this, as it provides the path to the library and includes the # allocator hooks. group("std") { assert(toolchain_has_rust, "Some C++ target is depending on Rust code even though " + "toolchain_has_rust=false. Usually this would mean" + "a NaCl target is depending on Rust, as there's no Rust " + "toolchain targetting NaCl.") all_dependent_configs = [ ":stdlib_public_dependent_libs", ":local_stdlib_sysroot", ":stdlib_dependent_libs", ] deps = [] foreach(libname, stdlib_files + skip_stdlib_files) { deps += [ "rules:$libname" ] } deps += rustc_sanitizer_runtimes public_deps = [ ":remap_alloc" ] } } else { action("find_stdlib") { # Collect prebuilt Rust libraries from toolchain package and copy to a # known location. # # The Rust toolchain contains prebuilt rlibs for the standard library and # its dependencies. However, they have unstable names: an unpredictable # metadata hash is appended to the known crate name. # # We must depend on these rlibs explicitly when rustc is not in charge of # linking. However, it is difficult to construct GN rules to do so when # the names can't be known statically. # # This action copies the prebuilt rlibs to a known location, removing the # metadata part of the name. In the process it verifies we have all the # libraries we expect and none that we don't. A depfile is generated so # this step is re-run when any libraries change. The action script # additionally verifies rustc matches the expected version, which is # unrelated but this is a convenient place to do so. # # The action refers to `stdlib_files`, `skip_stdlib_files`, and the # associated //build/config/rust.gni vars `removed_rust_stdlib_libs` and # `added_rust_stdlib_libs` for which rlib files to expect. # `extra_sysroot_libs` is also used to copy non-std libs, if any. script = "find_std_rlibs.py" depfile = "$target_out_dir/stdlib.d" out_libdir = rebase_path(target_out_dir, root_build_dir) out_depfile = rebase_path(depfile, root_build_dir) # For the rustc sysroot we must include even the rlibs we don't pass to # the C++ linker. all_stdlibs_to_copy = stdlib_files + skip_stdlib_files args = [ "--rust-bin-dir", rebase_path("${rust_sysroot}/bin", root_build_dir), "--output", out_libdir, "--depfile", out_depfile, # Due to limitations in Ninja's handling of .d files, we have to pick # *the first* of our outputs. To make diagnostics more obviously # related to the Rust standard library, we ensure libstd.rlib is first. "--depfile-target", stdlib_files[0], # Create a dependency on the rustc version so this action is re-run when # it changes. This argument is not actually read by the script. "--rustc-revision", rustc_revision, ] if (extra_sysroot_libs != []) { args += [ "--extra-libs", string_join(",", extra_sysroot_libs), ] } args += [ "--target", rust_abi_target, ] outputs = [] foreach(lib, all_stdlibs_to_copy) { outputs += [ "$target_out_dir/lib$lib.rlib" ] } foreach(lib, extra_sysroot_libs) { outputs += [ "$target_out_dir/$lib" ] } visibility = [ ":*" ] } prebuilt_rustc_sysroot = "$root_out_dir/prebuilt_rustc_sysroot" copy("prebuilt_rustc_copy_to_sysroot") { assert(enable_rust, "Some C++ target is including Rust code even though " + "enable_rust=false") deps = [ ":find_stdlib" ] sources = get_target_outputs(":find_stdlib") outputs = [ "$prebuilt_rustc_sysroot/$sysroot_lib_subdir/{{source_file_part}}" ] visibility = [ ":*" ] } config("prebuilt_stdlib_sysroot") { # Match the output directory of :prebuilt_rustc_copy_to_sysroot sysroot = rebase_path(prebuilt_rustc_sysroot, root_build_dir) rustflags = [ "--sysroot=$sysroot" ] visibility = [ ":*" ] } config("prebuilt_stdlib_libs") { ldflags = [] lib_dir = rebase_path("$prebuilt_rustc_sysroot/$sysroot_lib_subdir", root_build_dir) # We're unable to make these files regular gn dependencies because # they're prebuilt. Instead, we'll pass them in the ldflags. This doesn't # work for all types of build because ldflags propagate differently from # actual dependencies and therefore can end up in different targets from # the remap_alloc.cc above. For example, in a component build, we might # apply the remap_alloc.cc file and these ldlags to shared object A, # while shared object B (that depends upon A) might get only the ldflags # but not remap_alloc.cc, and thus the build will fail. There is # currently no known solution to this for the prebuilt stdlib - this # problem does not apply with configurations where we build the stdlib # ourselves, which is what we'll use in production. foreach(lib, stdlib_files) { this_file = "$lib_dir/lib$lib.rlib" ldflags += [ this_file ] } visibility = [ ":*" ] } group("std") { all_dependent_configs = [ ":prebuilt_stdlib_libs", ":stdlib_public_dependent_libs", ] deps = [ ":prebuilt_rustc_copy_to_sysroot" ] # The host builds tools toolchain supports Rust only and does not use # the allocator remapping to point it to PartitionAlloc. if (!toolchain_for_rust_host_build_tools) { deps += [ ":remap_alloc" ] } } } }