1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294
  295
  296
  297
  298
  299
  300
  301
  302
  303
  304
  305
  306
  307
  308
  309
  310
  311
  312
  313
  314
  315
  316
  317
  318
  319
  320
  321
  322
  323
  324
  325
  326
  327
  328
  329
  330
  331
  332
  333
  334
  335
  336
  337
  338
  339
  340
  341
  342
  343
  344
  345
  346
  347
  348
  349
  350
  351
  352
  353
  354
  355
  356
  357
  358
  359
  360
  361
  362
  363
  364
  365
  366
  367
  368
  369
  370
  371
  372
  373
  374
  375
  376
  377
  378
  379
  380
  381
  382
  383
  384
  385
  386
  387
  388
  389
  390
  391
  392
  393
  394
  395
  396
  397
  398
  399
  400
  401
  402
  403
  404
  405
  406
  407

build / rust / std / BUILD.gn [blame]

# 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("//build/buildflag_header.gni")
import("//build/config/compiler/compiler.gni")
import("//build/config/coverage/coverage.gni")
import("//build/config/rust.gni")
import("//build/config/sanitizers/sanitizers.gni")

rust_allocator_uses_partition_alloc = false
if (build_with_chromium) {
  import("//base/allocator/partition_allocator/partition_alloc.gni")
  rust_allocator_uses_partition_alloc = use_partition_alloc_as_malloc
}

buildflag_header("buildflags") {
  header = "buildflags.h"
  flags = [
    "RUST_ALLOCATOR_USES_PARTITION_ALLOC=$rust_allocator_uses_partition_alloc",
  ]
  visibility = [ ":*" ]
}

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 = []
    if (rust_allocator_uses_partition_alloc) {
      public_deps += [ "//base/allocator/partition_allocator:partition_alloc" ]
    }
    deps = [ ":buildflags" ]
    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.
    #
    # libs = [ "System" ] (meaning /usr/lib/libSystem.B.dylib) is intentionally
    # omitted here on Apple platforms, because the linker driver is responsible
    # for adding it, much like libc on most other POSIX platforms. It can be
    # ordering-sensitive, and including it here can alter its order relative to
    # other libraries. https://crbug.com/367764848 is an example of a bug caused
    # by specifying libSystem too early.
    #
    # 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_ios) {
      libs = [ "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 = [ ":*" ]
    }

    # 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" ]
      }

      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" ]
      }
    }
  }
}