File: cargo_crate.gni

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (588 lines) | stat: -rw-r--r-- 22,099 bytes parent folder | download | duplicates (5)
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
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
# 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.

import("//build/rust/rust_executable.gni")
import("//build/rust/rust_macro.gni")
import("//build/rust/rust_static_library.gni")

# This template allows for building Cargo crates within gn.
#
# It is intended for use with pre-existing (third party) code, from `BUILD.gn`
# files auto-generated by the `gnrt` tool.
#
# First party code should prefer to use first-class gn targets, such as
# `//build/rust/rust_static_library.gni` or similar.  One reason is that the
# `cargo_crate` template is not very efficient (it will stall the build
# pipeline whilst it runs build scripts to work out what flags are needed).
#
# Arguments:
#  aliased_deps
#  allow_unsafe
#  build_native_rust_unit_tests
#  crate_name
#  crate_root
#  deps
#  edition
#  features
#  sources
#    All just as in rust_static_library.gni
#  library_configs/executable_configs
#    All just as in rust_target.gni
#
#  enabled (optional)
#    A boolean that controls whether the main target will be defined at all.
#    Can be used to avoid compiling some platform-specific crates on other
#    platforms.
#
#  epoch (optional)
#    The major version of the library, which is used to differentiate between
#    multiple versions of the same library name. This includes all leading 0s
#    and the first non-zero value in the crate's version. This should be left
#    as the default, which is "0", for first-party code unless there are
#    multiple versions of a crate present. For third-party code, the version
#    epoch (matching the directory it is found in) should be specified.
#
#    Examples:
#      1.0.2 => epoch = "1"
#      4.2.0 => epoch = "4"
#      0.2.7 => epoch = "0.2"
#      0.0.3 => epoch = "0.0.3"
#
#  dev_deps
#    Same meaning as test_deps in rust_static_library.gni, but called
#    dev_deps to match Cargo.toml better.
#
#  build_root (optional)
#    Filename of build.rs build script.
#
#  build_deps (optional)
#    Build script dependencies
#
#  build_sources (optional)
#    List of sources for build script. Must be specified if
#    build_root is specified.
#
#  build_script_outputs (optional)
#    List of .rs files generated by the build script, if any.
#    Fine to leave undefined even if you have a build script.
#    This doesn't directly correspond to any Cargo variable,
#    but unfortunately is necessary for gn to build its dependency
#    trees automatically.
#    Many build scripts just output --cfg directives, in which case
#    no source code is generated and this can remain empty.
#
#  build_script_inputs (optional)
#    If the build script reads any files generated by build_deps,
#    as opposed to merely linking against them, add a list of such
#    files here. Again, this doesn't correspond to a Cargo variable
#    but is necessary for gn.
#
#  native_libs (optional)
#    Paths to library files that need to be in the linking search path when
#    depending on the crate's library, as it links against them via #[link]
#    directives.
#
#  crate_type "bin", "proc-macro" or "rlib" (optional)
#    Whether to build an executable. The default is "rlib".
#    At present others are not supported.
#
#  cargo_pkg_authors
#  cargo_pkg_version
#  cargo_pkg_name
#  cargo_pkg_description
#    Strings as found within 'version' and similar fields within Cargo.toml.
#    Converted to environment variables passed to rustc, in case the crate
#    uses clap `crate_version!` or `crate_authors!` macros (fairly common in
#    command line tool help)

template("cargo_crate") {
  _orig_target_name = target_name

  _crate_name = _orig_target_name
  if (defined(invoker.crate_name)) {
    _crate_name = invoker.crate_name
  }

  # Construct metadata from the crate epoch or an explicitly provided metadata
  # field.
  _rustc_metadata = ""
  if (defined(invoker.rustc_metadata)) {
    _rustc_metadata = invoker.rustc_metadata
  } else if (defined(invoker.epoch)) {
    _rustc_metadata = "${_crate_name}-${invoker.epoch}"
  }

  _enabled = true
  if (defined(invoker.enabled)) {
    _enabled = invoker.enabled
  }

  _epochlabel = "vunknown"
  if (defined(invoker.epoch)) {
    _tempepoch = string_replace(invoker.epoch, ".", "_")
    _epochlabel = "v${_tempepoch}"
  }

  # Executables need to have unique names. Work out a prefix.
  if (defined(invoker.build_root)) {
    # This name includes the target name to ensure it's unique for each possible
    # build target in the same BUILD.gn file.
    _build_script_name =
        "${_crate_name}_${target_name}_${_epochlabel}_build_script"

    # Where the OUT_DIR will point when running the build script exe, and
    # compiling the crate library/binaries. This directory must include the
    # target name to avoid collisions between multiple GN targets that exist
    # in the same BUILD.gn.
    _build_script_env_out_dir = "$target_gen_dir/$target_name"
  }

  _rustenv = []
  if (defined(invoker.rustenv)) {
    _rustenv = invoker.rustenv
  }
  if (defined(invoker.cargo_pkg_authors)) {
    _rustenv += [ "CARGO_PKG_AUTHORS=${invoker.cargo_pkg_authors}" ]
  }
  if (defined(invoker.cargo_pkg_version)) {
    _rustenv += [ "CARGO_PKG_VERSION=${invoker.cargo_pkg_version}" ]
  }
  if (defined(invoker.cargo_pkg_name)) {
    _rustenv += [ "CARGO_PKG_NAME=${invoker.cargo_pkg_name}" ]
  }
  if (defined(invoker.cargo_pkg_description)) {
    _rustenv += [ "CARGO_PKG_DESCRIPTION=${invoker.cargo_pkg_description}" ]
  }

  # Try to determine the CARGO_MANIFEST_DIR, preferring the directory
  # with build.rs and otherwise assuming that the target contains a
  # `crate/` subdirectory.
  if (defined(invoker.build_root)) {
    manifest_dir = "."
  } else {
    build_gn_dir = get_label_info(target_name, "dir")
    manifest_dir = rebase_path(build_gn_dir + "/crate", root_build_dir)
  }
  _rustenv += [ "CARGO_MANIFEST_DIR=${manifest_dir}" ]

  # cargo_crate() should set library_configs, executable_configs,
  # proc_macro_configs. Not configs.
  assert(!defined(invoker.configs))

  # Work out what we're building.
  _crate_type = "rlib"
  if (defined(invoker.crate_type)) {
    _crate_type = invoker.crate_type
  }
  if (_crate_type == "bin") {
    _target_type = "rust_executable"
    assert(!defined(invoker.epoch))
    _configs = invoker.executable_configs
  } else if (_crate_type == "proc-macro") {
    _target_type = "rust_macro"
    _configs = invoker.proc_macro_configs
  } else {
    assert(_crate_type == "rlib")
    _target_type = "rust_static_library"
    _configs = invoker.library_configs
  }

  if (defined(invoker.output_name)) {
    _output_name = invoker.output_name
    not_needed([ "_epochlabel" ])
  } else if (_crate_type == "proc-macro") {
    # Proc macros are output to a top level directory (for the host toolchain).
    # Since multiple versions of the same crate may coexist, the output file
    # name must include the epoch to disambiguate.
    _output_name = "${_crate_name}_${_epochlabel}"
  } else if (_crate_type != "bin") {
    # Note that file names of libraries must start with the crate name in
    # order for the compiler to find transitive dependencies in the
    # directory search paths (since they are not all explicitly specified).
    #
    # For bin targets, we expect the target name to be unique, and the name
    # of the exe should not add magic stuff to it. And bin crates can not be
    # transitive dependencies.
    _output_name = "${_crate_name}_${_orig_target_name}"
    not_needed([ "_epochlabel" ])
  } else {
    not_needed([ "_epochlabel" ])
  }

  _testonly = false
  if (defined(invoker.testonly)) {
    _testonly = invoker.testonly
  }

  if (defined(invoker.native_libs)) {
    _native_libs_action = "copy_${target_name}_native_libs"
    _native_libs_config = "config_${target_name}_native_libs"
    _native_libs_dir =
        "${root_out_dir}/rustlib/${_crate_name}_${target_name}_${_epochlabel}"

    copy(_native_libs_action) {
      testonly = _testonly
      visibility = [ ":$target_name" ]
      sources = invoker.native_libs
      outputs = [ "${_native_libs_dir}/{{source_file_part}}" ]
    }
    config(_native_libs_config) {
      lib_dirs = [ "${_native_libs_dir}" ]
    }
  }

  # The main target, either a Rust source set or an executable.
  if (_enabled) {
    target(_target_type, target_name) {
      forward_variables_from(invoker,
                             "*",
                             TESTONLY_AND_VISIBILITY + [
                                   "build_root",
                                   "build_deps",
                                   "build_sources",
                                   "build_script_inputs",
                                   "build_script_outputs",
                                   "epoch",
                                   "unit_test_target",
                                   "configs",
                                   "executable_configs",
                                   "library_configs",
                                   "proc_macro_configs",
                                   "rustenv",
                                   "dev_deps",
                                   "native_libs_dir",
                                 ])

      testonly = _testonly
      if (defined(invoker.visibility)) {
        visibility = invoker.visibility
      }
      crate_name = _crate_name

      if (defined(_output_name)) {
        output_name = _output_name
      }

      # Don't import the `chromium` crate into third-party code.
      no_chromium_prelude = true

      # Depend by default on the `//build/rust/allocator` library, but avoid this
      # dependency in `no_std` mode.  This is desirable for two reasons:
      # 1. It gives users of `cargo_crate` an indirect way to control presence of
      #    this dependency.
      # 2. More importantly, it avoid cyclic dependencies like the one below:
      #       //build/rust/allocator:allocator ->
      #       ...
      #       //build/rust/std:std ->
      #       //build/rust/std/rules:addr2line (or another stdlib crate) ->
      #       //build/rust/allocator:allocator
      no_allocator_crate = false
      if (defined(invoker.no_std) && invoker.no_std) {
        no_allocator_crate = true
      }

      rustc_metadata = _rustc_metadata

      configs = []
      configs = _configs
      if (_crate_type == "rlib") {
        # Forward configs for unit tests.
        executable_configs = invoker.executable_configs
      }

      if (!defined(rustflags)) {
        rustflags = []
      }
      rustenv = _rustenv

      # When the `invoker` doesn't specify `allow_unsafe`, then fall back to
      # allowing `unsafe`.  This avoids creating additional friction when a new
      # team experiments with Rust.  OTOH we enforce that when crates actually
      # land in Chromium, then they need to explicitly declare their
      # `allow_unsafe` needs - this enforcement is done via presubmits under
      # `third_party/rust/chromium_crates_io` (see
      # `CheckExplicitAllowUnsafeForAllCrates` in a `.py` module over there).
      allow_unsafe = true
      if (defined(invoker.allow_unsafe)) {
        allow_unsafe = invoker.allow_unsafe
      }
      rustflags +=
          [ "-Awarnings" ]  # Suppress other warnings in 3rd-party crates.

      if (!defined(build_native_rust_unit_tests)) {
        build_native_rust_unit_tests = _crate_type != "proc-macro"
      }
      if (build_native_rust_unit_tests) {
        # Unit tests in a proc-macro crate type don't make sense, you can't
        # compile executables against the `proc_macro` crate.
        assert(_crate_type != "proc-macro")
      }

      # The unit tests for each target, if generated, should be unique as well.
      # a) It needs to be unique even if multiple build targets have the same
      #    `crate_name`, but different target names.
      # b) It needs to be unique even if multiple build targets have the same
      #    `crate_name` and target name, but different epochs.
      _unit_test_unique_target_name = ""
      if (_crate_name != _orig_target_name) {
        _unit_test_unique_target_name = "${_orig_target_name}_"
      }
      _unit_test_unique_epoch = ""
      if (defined(invoker.epoch)) {
        _epoch_str = string_replace(invoker.epoch, ".", "_")
        _unit_test_unique_epoch = "v${_epoch_str}_"
      }
      if (defined(output_dir) && output_dir != "") {
        unit_test_output_dir = output_dir
      }
      unit_test_target = "${_unit_test_unique_target_name}${_crate_name}_${_unit_test_unique_epoch}unittests"

      if ((!defined(output_dir) || output_dir == "") && _crate_type == "rlib") {
        # Cargo crate rlibs can be compiled differently for tests, and must not
        # collide with the production outputs. This does *not* override the
        # unit_test_output_dir, which is set above, as that target is not an rlib.
        output_dir = "$target_out_dir/$_orig_target_name"
      }

      if (defined(invoker.dev_deps)) {
        test_deps = invoker.dev_deps
      }

      if (defined(invoker.build_root)) {
        # Uh-oh, we have a build script
        if (!defined(deps)) {
          deps = []
        }
        if (!defined(sources)) {
          sources = []
        }
        if (!defined(inputs)) {
          inputs = []
        }

        # This... is a bit weird. We generate a file called cargo_flags.rs which
        # does not actually contain Rust code, but instead some flags to add
        # to the rustc command line. We need it to end in a .rs extension so that
        # we can include it in the 'sources' line and thus have dependency
        # calculation done correctly. data_deps won't work because targets don't
        # require them to be present until runtime.
        flags_file = "$_build_script_env_out_dir/cargo_flags.rs"
        rustflags += [ "@" + rebase_path(flags_file, root_build_dir) ]
        sources += [ flags_file ]
        if (defined(invoker.build_script_outputs)) {
          # Build scripts may output arbitrary files. They are usually included in
          # the main Rust target using include! or include_str! and therefore the
          # filename may be .rs or may be arbitrary. We want to educate ninja
          # about the dependency either way.
          foreach(extra_source,
                  filter_include(invoker.build_script_outputs, [ "*.rs" ])) {
            sources += [ "$_build_script_env_out_dir/$extra_source" ]
          }
          foreach(extra_source,
                  filter_exclude(invoker.build_script_outputs, [ "*.rs" ])) {
            inputs += [ "$_build_script_env_out_dir/$extra_source" ]
          }
        }
        deps += [ ":${_build_script_name}_output" ]
        if (defined(_native_libs_action)) {
          deps += [ ":${_native_libs_action}" ]
          configs += [ ":${_native_libs_config}" ]
        }
      }
    }
  } else {
    not_needed(invoker,
               "*",
               [
                 "build_root",
                 "build_deps",
                 "build_sources",
                 "build_script_inputs",
                 "build_script_outputs",
               ])
    not_needed("*")
  }

  if (defined(invoker.build_root)) {
    action("${_build_script_name}_write_rustflags") {
      _rustflags_txt = "$_build_script_env_out_dir/rustflags.txt"
      outputs = [ _rustflags_txt ]
      script = rebase_path("//build/rust/write_rustflags.py")
      args = [
        "--output",
        rebase_path(_rustflags_txt, root_build_dir),
        "--",
        "{{rustflags}}",
      ]

      # The configs are required to get `{{rustflags}}` so that the build script
      # is compiled with the same flags as the library/binary will be. The build
      # script is an executable so it also gets the executable configs. If this
      # is ever a problem we can add a separate build_script_configs to the
      # cargo_crate template and just have it default to the same thing as
      # executable_configs.
      configs = invoker.executable_configs
    }

    # Extra targets required to make build script work
    action("${_build_script_name}_output") {
      script = rebase_path("//build/rust/run_build_script.py")
      _write_rustflags_outputs =
          get_target_outputs(":${_build_script_name}_write_rustflags")
      _rustflags_txt = _write_rustflags_outputs[0]
      inputs = [
        "//build/action_helpers.py",
        "//build/gn_helpers.py",
        _rustflags_txt,
      ]
      build_script_target = ":${_build_script_name}($rust_macro_toolchain)"
      deps = [
        ":${_build_script_name}_write_rustflags",
        build_script_target,
      ]
      testonly = _testonly
      if (defined(invoker.visibility)) {
        visibility = invoker.visibility
      }

      # The build script may be built with a different toolchain when
      # cross-compiling (the host toolchain) so we must find the path relative
      # to that.
      _build_script_root_out_dir =
          get_label_info(build_script_target, "root_out_dir")
      _build_script_exe = "$_build_script_root_out_dir/$_build_script_name"

      # The executable is always built with the `rust_macro_toolchain` which
      # targets the `host_os`. The rule here is on the `target_toolchain` which
      # can be different (e.g. compiling on Linux, targeting Windows).
      if (host_os == "win") {
        _build_script_exe = "${_build_script_exe}.exe"
      }

      _flags_file = "$_build_script_env_out_dir/cargo_flags.rs"

      inputs += [ _build_script_exe ]
      outputs = [ _flags_file ]
      args = [
        "--build-script",
        rebase_path(_build_script_exe, root_build_dir),
        "--output",
        rebase_path(_flags_file, root_build_dir),
        "--rust-prefix",
        rebase_path("${rust_sysroot}/bin", root_build_dir),
        "--out-dir",
        rebase_path(_build_script_env_out_dir, root_build_dir),
        "--src-dir",
        rebase_path(get_path_info(invoker.build_root, "dir"), root_build_dir),
        "--target",
        rust_abi_target,
        "--rustflags",
        rebase_path(_rustflags_txt, root_build_dir),
      ]
      if (cargo_target_abi != "") {
        args += [
          "--target-abi",
          cargo_target_abi,
        ]
      }
      if (defined(invoker.features)) {
        args += [ "--features" ]
        args += invoker.features
      }
      if (defined(invoker.build_script_outputs)) {
        args += [ "--generated-files" ]
        args += invoker.build_script_outputs
        foreach(generated_file, invoker.build_script_outputs) {
          outputs += [ "$_build_script_env_out_dir/$generated_file" ]
        }
      }
      if (_rustenv != []) {
        args += [ "--env" ]
        args += _rustenv
      }
      if (defined(invoker.build_script_inputs)) {
        inputs += invoker.build_script_inputs
      }
    }

    if (toolchain_for_rust_host_build_tools) {
      # The build script is only available to be built on the host, and we use
      # the rust_macro_toolchain for it to unblock building them while the
      # Chromium stdlib is still being compiled.
      rust_executable(_build_script_name) {
        crate_name = _build_script_name
        sources = invoker.build_sources
        crate_root = invoker.build_root
        testonly = _testonly
        if (defined(invoker.visibility)) {
          visibility = invoker.visibility
        }
        if (defined(invoker.build_deps)) {
          deps = invoker.build_deps
        }
        if (defined(invoker.build_script_inputs)) {
          inputs = invoker.build_script_inputs
        }

        # Don't import the `chromium` crate into third-party code.
        no_chromium_prelude = true

        # Build scripts do not need to link to chrome's allocator.
        no_allocator_crate = true

        # The ${_build_script_name}_output target looks for the exe in this
        # location. Due to how the Windows component build works, this has to
        # be $root_out_dir for all EXEs. In component build, C++ links to the
        # CRT as a DLL, and if Rust does not match, we can't link mixed target
        # Rust EXE/DLLs, as the headers in C++ said something different than
        # what Rust links. Since the CRT DLL is placed in the $root_out_dir,
        # an EXE can find it if it's also placed in that dir.
        output_dir = root_out_dir
        rustenv = _rustenv
        forward_variables_from(invoker,
                               [
                                 "features",
                                 "edition",
                                 "rustflags",
                               ])
        configs -= [
          "//build/config/compiler:chromium_code",

          # Avoid generating profiling data for build scripts.
          #
          # TODO(crbug.com/40261306): determine for sure whether to remove this
          # config. I'm not sure of the overlap between PGO instrumentation and
          # code coverage instrumentation, but we definitely don't want build
          # script coverage for PGO, while we might for test coverage metrics.
          #
          # If we do include build script output in test metrics, it could be
          # misleading: exercising some code from a build script doesn't give us
          # the same signal as an actual test.
          "//build/config/coverage:default_coverage",
        ]
        configs += [ "//build/config/compiler:no_chromium_code" ]
      }
    } else {
      not_needed(invoker,
                 [
                   "build_sources",
                   "build_deps",
                   "build_root",
                   "build_script_inputs",
                   "build_script_outputs",
                 ])
    }
  } else {
    not_needed([
                 "_name_specific_output_dir",
                 "_orig_target_name",
               ])
  }
}

set_defaults("cargo_crate") {
  library_configs = default_compiler_configs
  executable_configs = default_executable_configs
  proc_macro_configs = default_rust_proc_macro_configs
}