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