| # 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 into the shim provided by PartitionAlloc 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", |
| "//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", |
| "libc", |
| "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", |
| "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" ] |
| } |
| } |
| } |
| } |