1# Copyright 2021 The Chromium Authors 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import("//build/config/rust.gni") 6import("//build/rust/rust_unit_test.gni") 7 8# The //build directory is re-used for non-Chromium projects. Do not support 9# cxx bindings in such contexts by default, because //third_party may be 10# missing. Projects that wish to use cxx bindings must explicitly set the 11# enable_rust_cxx GN arg to true. 12if (enable_rust_cxx) { 13 import("//third_party/rust/cxx/chromium_integration/rust_cxx.gni") 14} 15 16# Creates a Rust target (rlib, executable, proc macro etc.) with ability to 17# understand some handy variables such as "edition" and "features" and also to 18# build any associated unit tests. 19# 20# `bindgen_deps` field exists only for 3P cargo sys crates that uses 21# `rust_bindgen_generator` templates. For 1P code, `rust_bindgen` should 22# be used and should go directly in the `deps` field. 23# 24# Normally, you should not use this directly. Use either 25# - cargo_crate.gni - for 3p crates only 26# - rust_static_library.gni - for 1p Rust code 27# 28# Because the common use of this is rust_static_library, all the documentation 29# for the supported options is given in rust_static_library.gni. Please refer 30# over there. 31# 32# If you're using rust_target directly, you will also need to specify: 33# target_type executable, rust_library etc. per GN norms 34# 35# There is one area where this differs from `rust_static_library`: configs. 36# Here, you must specify `executable_configs` or `library_configs` depending on 37# the type of thing you're generating. This is so that different defaults can 38# be provided. 39 40template("rust_target") { 41 _target_name = target_name 42 43 # NOTE: TargetName=>CrateName mangling algorithm should be updated 44 # simultaneously in 3 places: here, //build/rust/rust_static_library.gni, 45 # //build/rust/chromium_prelude/import_attribute.rs 46 if (defined(invoker.crate_name)) { 47 _crate_name = invoker.crate_name 48 } else { 49 # Not using `get_label_info(..., "label_no_toolchain")` to consistently 50 # use `//foo/bar:baz` instead of the alternative shorter `//foo/bar` form. 51 _dir = get_label_info(":${_target_name}", "dir") 52 _dir = string_replace(_dir, "//", "") 53 _crate_name = "${_dir}:${_target_name}" 54 55 # The `string_replace` calls below replicate the escaping algorithm 56 # from the `escape_non_identifier_chars` function in 57 # //build/rust/chromium_prelude/import_attribute.rs. Note that the 58 # ordering of `match` branches within the Rust function doesn't matter, 59 # but the ordering of `string_replace` calls *does* matter - the escape 60 # character `_` needs to be handled first to meet the injectivity 61 # requirement (otherwise we would get `/` => `_s` => `_us` and the same 62 # result for `_s` => `_us`). 63 _crate_name = string_replace(_crate_name, "_", "_u") 64 _crate_name = string_replace(_crate_name, "/", "_s") 65 _crate_name = string_replace(_crate_name, ":", "_c") 66 _crate_name = string_replace(_crate_name, "-", "_d") 67 } 68 _generate_crate_root = 69 defined(invoker.generate_crate_root) && invoker.generate_crate_root 70 71 # Only one of `crate_root` or `generate_crate_root` can be specified, or 72 # neither. 73 assert(!defined(invoker.crate_root) || !_generate_crate_root) 74 75 # This is where the OUT_DIR environment variable points to when running a 76 # build script and when compiling the build target, for consuming generated 77 # files. 78 _env_out_dir = "$target_gen_dir/$_target_name" 79 80 _allow_unsafe = false 81 if (defined(invoker.allow_unsafe)) { 82 _allow_unsafe = invoker.allow_unsafe 83 } 84 85 if (_generate_crate_root) { 86 generated_file("${_target_name}_crate_root") { 87 outputs = [ "${target_gen_dir}/${target_name}.rs" ] 88 contents = [ 89 "// Generated crate root for ${_target_name}.", 90 "// @generated", 91 "", 92 ] 93 foreach(rs, invoker.sources) { 94 rs_path_from_root = rebase_path(rs, target_gen_dir) 95 contents += [ "#[path = \"${rs_path_from_root}\"]" ] 96 97 # Drop the file extension from the module name. 98 rs_modname = string_replace(rs, ".rs", "") 99 100 # Replace invalid "/" chars in the source file path. 101 rs_modname = string_replace(rs_modname, "/", "_") 102 103 # Since source files are specified relative to the BUILD.gn they may 104 # also have ".." path components. 105 rs_modname = string_replace(rs_modname, "..", "dotdot") 106 contents += [ 107 "mod ${rs_modname};", 108 "", 109 ] 110 } 111 } 112 _generated_crate_root = get_target_outputs(":${_target_name}_crate_root") 113 _crate_root = _generated_crate_root[0] 114 } else if (defined(invoker.crate_root)) { 115 _crate_root = invoker.crate_root 116 } else if (invoker.target_type == "executable") { 117 _crate_root = "src/main.rs" 118 } else { 119 _crate_root = "src/lib.rs" 120 } 121 122 _testonly = false 123 if (defined(invoker.testonly)) { 124 _testonly = invoker.testonly 125 } 126 if (defined(invoker.visibility)) { 127 _visibility = invoker.visibility 128 } 129 130 _rustflags = [] 131 if (defined(invoker.rustflags)) { 132 _rustflags += invoker.rustflags 133 } 134 if (defined(invoker.features)) { 135 foreach(i, invoker.features) { 136 _rustflags += [ "--cfg=feature=\"${i}\"" ] 137 } 138 } 139 _edition = "2021" 140 if (defined(invoker.edition)) { 141 _edition = invoker.edition 142 } 143 144 assert(!defined(configs)) 145 _configs = [ "//build/rust:edition_${_edition}" ] 146 _test_configs = [] 147 if (invoker.target_type == "executable") { 148 _configs += invoker.executable_configs 149 } else if (invoker.target_type == "rust_proc_macro") { 150 _configs += invoker.proc_macro_configs 151 _test_configs += [ "//build/rust:proc_macro_extern" ] 152 } else if (invoker.target_type == "shared_library") { 153 _configs += invoker.shared_library_configs 154 } else { 155 _configs += invoker.library_configs 156 } 157 158 if (invoker.target_type == "rust_proc_macro") { 159 _main_target_suffix = "__proc_macro" 160 } else if (invoker.target_type == "shared_library") { 161 _main_target_suffix = "__proc_macro" 162 } else { 163 _main_target_suffix = "" 164 } 165 166 _deps = [] 167 if (defined(invoker.deps)) { 168 _deps += invoker.deps 169 } 170 if (defined(invoker.bindgen_deps)) { 171 _bindgen_inputs = [] 172 173 # This iteration assumes that no targets have the same name which is 174 # very rare to happen and if it does. An error will be thrown as we 175 # try to create two targets with the same name, the error might not 176 # be descriptive enough so maybe adding a check action would be better. 177 foreach(bindgen_dep, invoker.bindgen_deps) { 178 _copy_target_name = 179 target_name + "_" + get_label_info(bindgen_dep, "name") + "_copy" 180 copy(_copy_target_name) { 181 _bindgen_output_files = get_target_outputs(bindgen_dep) 182 183 # `rust_bindgen_generator` promises that the first output file is always .rs. 184 sources = [ _bindgen_output_files[0] ] 185 outputs = [ "$_env_out_dir/{{source_name_part}}.rs" ] 186 deps = [ bindgen_dep ] 187 } 188 189 # The bindgen-generated rs files are inputs to this library for the library 190 # to `include!` them. 191 # The output of the copy action is always a single file so just copy everything. 192 _bindgen_inputs += get_target_outputs(":$_copy_target_name") 193 194 # Depend on the bindgen generation to make the above `_bindgen_inputs`. 195 _deps += [ ":$_copy_target_name" ] 196 } 197 } 198 _public_deps = [] 199 if (defined(invoker.public_deps)) { 200 _public_deps += invoker.public_deps 201 } 202 if (defined(invoker.aliased_deps)) { 203 _aliased_deps = invoker.aliased_deps 204 } else { 205 _aliased_deps = { 206 } 207 } 208 209 _build_unit_tests = false 210 if (defined(invoker.build_native_rust_unit_tests)) { 211 _build_unit_tests = 212 invoker.build_native_rust_unit_tests && can_build_rust_unit_tests 213 } 214 215 # Declares that the Rust crate generates bindings between C++ and Rust via the 216 # Cxx crate. It may generate C++ headers and/or use the cxx crate macros to 217 # generate Rust code internally, depending on what bindings are declared. If 218 # set, it's a set of rust files that include Cxx bindings declarations. 219 _cxx_bindings = [] 220 assert(!defined(invoker.cxx_bindings) || enable_rust_cxx, 221 "cxx bindings are not supported when building rust targets " + 222 "outside the Chromium build.") 223 if (defined(invoker.cxx_bindings)) { 224 _cxx_bindings = invoker.cxx_bindings 225 } 226 _rustenv = [ "OUT_DIR=" + 227 rebase_path(_env_out_dir, get_path_info(_crate_root, "dir")) ] 228 if (defined(invoker.rustenv)) { 229 _rustenv += invoker.rustenv 230 } 231 232 # We require that all source files are listed, even though this is 233 # not a requirement for rustc. The reason is to ensure that tools 234 # such as `gn deps` give the correct answer, and thus we trigger 235 # the right test suites etc. on code change. 236 # TODO(crbug.com/40200431) - verify this is correct 237 assert(defined(invoker.sources), "sources must be listed") 238 239 if (invoker.target_type == "rust_proc_macro" && 240 !toolchain_for_rust_host_build_tools) { 241 # Redirect to the proc macro toolchain, which uses prebuilt stdlib libraries 242 # that are not built with panic=abort. 243 group(_target_name) { 244 testonly = _testonly 245 if (defined(_visibility)) { 246 visibility = _visibility 247 } 248 public_deps = 249 [ ":${_target_name}${_main_target_suffix}($rust_macro_toolchain)" ] 250 } 251 252 not_needed(invoker, "*") 253 not_needed([ 254 "_aliased_deps", 255 "_allow_unsafe", 256 "_build_unit_tests", 257 "_crate_root", 258 "_crate_name", 259 "_cxx_bindings", 260 "_deps", 261 "_rustc_metadata", 262 "_out_dir", 263 "_public_deps", 264 "_rustenv", 265 "_rustflags", 266 "_support_use_from_cpp", 267 "_testonly", 268 ]) 269 } else { 270 # These are dependencies that must be included into the C++ target that 271 # depends on this Rust one, and into the Rust target itself, respectively. 272 # 273 # For an rlib or exe, it's enough to add all these as dependencies of the 274 # Rust target alone, and they will get included into the final link step. 275 # 276 # But when then Rust target is a shared library, the C++ target needs to 277 # link the C++ thunks that are used to call the cxx bridge functions. And 278 # Cxx library itself needs to be in both. 279 _cxx_generated_deps_for_cpp = [] 280 _cxx_generated_deps_for_rust = [] 281 if (_cxx_bindings != []) { 282 _cxx_generated_deps_for_cpp += [ 283 # The Cxx-generated thunks, which have the public C++ names and bounce 284 # over to the Rust code. 285 ":${_target_name}_cxx_generated", 286 287 # Additionally, C++ bindings generated by Cxx can include C++ types 288 # that come from the Cxx library, such as `rust::Str`. The header and 289 # implementation of these types are provided in the cxx_cppdeps target. 290 # The C++ targets depending on this Rust target need the headers, while 291 # the Rust target needs the implementation. 292 "//build/rust:cxx_cppdeps", 293 ] 294 _cxx_generated_deps_for_rust = [ 295 # The implementation of the Cxx library needs to be in the Rust target. 296 "//build/rust:cxx_cppdeps", 297 ] 298 } 299 300 # Proc macros and shared libraries have a group for the target name and 301 # redirect to a suffixed target for the actual library. 302 if (_main_target_suffix != "") { 303 group(_target_name) { 304 testonly = _testonly 305 if (defined(_visibility)) { 306 visibility = _visibility 307 } 308 public_deps = [ ":${_target_name}${_main_target_suffix}" ] 309 public_deps += _cxx_generated_deps_for_cpp 310 } 311 } 312 313 _rustc_metadata = "" 314 if (defined(invoker.rustc_metadata)) { 315 _rustc_metadata = invoker.rustc_metadata 316 } 317 318 _rust_deps = _deps 319 _rust_aliased_deps = _aliased_deps 320 _rust_public_deps = _public_deps 321 _cxx_deps = _deps 322 323 # Include the `chromium` crate in all first-party code. Third-party code 324 # (and the `chromium` crate itself) opts out by setting 325 # `no_chromium_prelude`. 326 if (!defined(invoker.no_chromium_prelude) || !invoker.no_chromium_prelude) { 327 if (enable_chromium_prelude) { 328 _rust_deps += [ "//build/rust/chromium_prelude" ] 329 } 330 } 331 332 if (_cxx_bindings != []) { 333 # The Rust target (and unit tests) need the Cxx crate when using it to 334 # generate bindings. 335 _rust_deps += [ "//build/rust:cxx_rustdeps" ] 336 } 337 338 if (!defined(invoker.no_std) || !invoker.no_std) { 339 _rust_deps += [ "//build/rust/std" ] 340 } 341 342 if (_build_unit_tests) { 343 _unit_test_target = "${_target_name}_unittests" 344 if (defined(invoker.unit_test_target)) { 345 _unit_test_target = invoker.unit_test_target 346 } 347 348 rust_unit_test(_unit_test_target) { 349 testonly = true 350 crate_name = _unit_test_target 351 crate_root = _crate_root 352 sources = invoker.sources + [ crate_root ] 353 rustflags = _rustflags 354 env_out_dir = _env_out_dir 355 if (defined(invoker.unit_test_output_dir)) { 356 output_dir = invoker.unit_test_output_dir 357 } 358 deps = _rust_deps + _public_deps 359 aliased_deps = _rust_aliased_deps 360 public_deps = [ ":${_target_name}" ] 361 if (defined(invoker.test_deps)) { 362 deps += invoker.test_deps 363 } 364 inputs = [] 365 if (defined(invoker.inputs)) { 366 inputs += invoker.inputs 367 } 368 if (defined(_bindgen_inputs)) { 369 inputs += _bindgen_inputs 370 } 371 if (defined(invoker.test_inputs)) { 372 inputs += invoker.test_inputs 373 } 374 if (defined(invoker.executable_configs)) { 375 configs = [] 376 configs += invoker.executable_configs 377 } 378 configs += _test_configs 379 rustenv = _rustenv 380 381 if (!_allow_unsafe) { 382 configs += [ "//build/rust:forbid_unsafe" ] 383 } 384 } 385 } else { 386 not_needed([ 387 "_crate_root", 388 "_crate_name", 389 "_rustc_metadata", 390 "_test_configs", 391 ]) 392 not_needed(invoker, [ "executable_configs" ]) 393 } 394 395 target(invoker.target_type, "${_target_name}${_main_target_suffix}") { 396 forward_variables_from(invoker, 397 "*", 398 TESTONLY_AND_VISIBILITY + [ 399 "features", 400 "deps", 401 "aliased_deps", 402 "public_deps", 403 "rustflags", 404 "rustenv", 405 "configs", 406 "unit_test_output_dir", 407 "unit_test_target", 408 "test_inputs", 409 ]) 410 411 if (_main_target_suffix != "") { 412 # There's a group that depends on this target, and dependencies must 413 # be through that group. 414 visibility = [ ":$_target_name" ] 415 not_needed([ "_visibility" ]) 416 } else if (defined(_visibility)) { 417 visibility = _visibility 418 } 419 420 testonly = _testonly 421 crate_name = _crate_name 422 crate_root = _crate_root 423 configs = [] 424 configs = _configs 425 deps = _rust_deps + _cxx_generated_deps_for_rust 426 aliased_deps = _rust_aliased_deps 427 public_deps = _rust_public_deps 428 if (_main_target_suffix == "") { 429 # When these are not provided by a wrapper group target, they are added 430 # to the Rust target itself. 431 public_deps += _cxx_generated_deps_for_cpp 432 } 433 rustflags = _rustflags 434 if (_rustc_metadata != "") { 435 rustflags += [ "-Cmetadata=${_rustc_metadata}" ] 436 } 437 rustenv = _rustenv 438 439 if (_generate_crate_root) { 440 deps += [ ":${_target_name}_crate_root" ] 441 sources += [ _crate_root ] 442 } 443 444 if (!defined(inputs)) { 445 inputs = [] 446 } 447 448 if (defined(_bindgen_inputs)) { 449 inputs += _bindgen_inputs 450 } 451 452 if (!defined(output_name)) { 453 # Note that file names of libraries must start with the crate name in 454 # order for the compiler to find transitive dependencies in the 455 # directory search paths (since they are not all explicitly specified). 456 # 457 # For bin targets, we expect the target name to be unique, and the name 458 # of the exe should not add magic stuff to it. And bin crates can not be 459 # transitive dependencies. 460 if (invoker.target_type == "executable") { 461 output_name = _target_name 462 } else { 463 # TODO(danakj): Since the crate name includes the whole path for 1p 464 # libraries, we could move the output_dir to `root_out_dir` here for 465 # them, which would make for shorter file paths. But we need to not 466 # do the same for 3p crates or those with a `crate_name` set 467 # explicitly. 468 output_name = _crate_name 469 } 470 } 471 472 if (!_allow_unsafe) { 473 configs += [ "//build/rust:forbid_unsafe" ] 474 } 475 } 476 477 if (_cxx_bindings != []) { 478 rust_cxx("${_target_name}_cxx_generated") { 479 testonly = _testonly 480 visibility = [ ":${_target_name}" ] 481 if (defined(_visibility)) { 482 visibility += _visibility 483 } 484 sources = _cxx_bindings 485 deps = _cxx_deps + _public_deps 486 configs = _configs 487 488 # In a component_build the cxx bindings may be linked into a shared 489 # library at any point up the dependency tree, so always export. 490 export_symbols = is_component_build 491 } 492 } else { 493 not_needed([ "_cxx_deps" ]) 494 } 495 } 496} 497