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 _crate_name = target_name 37 if (defined(invoker.crate_name)) { 38 _crate_name = invoker.crate_name 39 } 40 _generate_crate_root = 41 defined(invoker.generate_crate_root) && invoker.generate_crate_root 42 43 # Only one of `crate_root` or `generate_crate_root` can be specified, or 44 # neither. 45 assert(!defined(invoker.crate_root) || !_generate_crate_root) 46 47 if (defined(invoker.output_dir) && invoker.output_dir != "") { 48 # This is where the build target (.exe, .rlib, etc) goes. 49 _output_dir = invoker.output_dir 50 } 51 52 # This is where the OUT_DIR environment variable points to when running a 53 # build script and when compiling the build target, for consuming generated 54 # files. 55 _env_out_dir = "$target_gen_dir/$_target_name" 56 57 _allow_unsafe = false 58 if (defined(invoker.allow_unsafe)) { 59 _allow_unsafe = invoker.allow_unsafe 60 } 61 62 if (_generate_crate_root) { 63 generated_file("${_target_name}_crate_root") { 64 outputs = [ "${target_gen_dir}/${target_name}.rs" ] 65 contents = [ 66 "// Generated crate root for ${_target_name}.", 67 "// @generated", 68 "", 69 ] 70 foreach(rs, invoker.sources) { 71 rs_path_from_root = rebase_path(rs, target_gen_dir) 72 contents += [ "#[path = \"${rs_path_from_root}\"]" ] 73 74 # Drop the file extension from the module name. 75 rs_modname = string_replace(rs, ".rs", "") 76 77 # Replace invalid "/" chars in the source file path. 78 rs_modname = string_replace(rs_modname, "/", "_") 79 80 # Since source files are specified relative to the BUILD.gn they may 81 # also have ".." path components. 82 rs_modname = string_replace(rs_modname, "..", "dotdot") 83 contents += [ 84 "mod ${rs_modname};", 85 "", 86 ] 87 } 88 } 89 _generated_crate_root = get_target_outputs(":${_target_name}_crate_root") 90 _crate_root = _generated_crate_root[0] 91 } else if (defined(invoker.crate_root)) { 92 _crate_root = invoker.crate_root 93 } else if (invoker.target_type == "executable") { 94 _crate_root = "src/main.rs" 95 } else { 96 _crate_root = "src/lib.rs" 97 } 98 99 _testonly = false 100 if (defined(invoker.testonly)) { 101 _testonly = invoker.testonly 102 } 103 if (defined(invoker.visibility)) { 104 _visibility = invoker.visibility 105 } 106 107 _rustflags = [] 108 if (defined(invoker.rustflags)) { 109 _rustflags += invoker.rustflags 110 } 111 if (defined(invoker.features)) { 112 foreach(i, invoker.features) { 113 _rustflags += [ "--cfg=feature=\"${i}\"" ] 114 } 115 } 116 _edition = "2021" 117 if (defined(invoker.edition)) { 118 _edition = invoker.edition 119 } 120 121 assert(!defined(configs)) 122 _configs = [ "//build/rust:edition_${_edition}" ] 123 _test_configs = [] 124 if (invoker.target_type == "executable") { 125 _configs += invoker.executable_configs 126 } else if (invoker.target_type == "rust_proc_macro") { 127 _configs += invoker.proc_macro_configs 128 _test_configs += [ "//build/rust:proc_macro_extern" ] 129 } else if (invoker.target_type == "shared_library") { 130 _configs += invoker.shared_library_configs 131 } else { 132 _configs += invoker.library_configs 133 } 134 135 if (invoker.target_type == "rust_proc_macro") { 136 _main_target_suffix = "${target_name}__proc_macro" 137 } else { 138 _main_target_suffix = "__rlib" 139 } 140 141 _deps = [] 142 if (defined(invoker.deps)) { 143 _deps += invoker.deps 144 } 145 _public_deps = [] 146 if (defined(invoker.public_deps)) { 147 _public_deps += invoker.public_deps 148 } 149 if (defined(invoker.aliased_deps)) { 150 _aliased_deps = invoker.aliased_deps 151 } else { 152 _aliased_deps = { 153 } 154 } 155 156 _is_data_dep = defined(invoker.is_data_dep) && invoker.is_data_dep 157 158 _build_unit_tests = false 159 if (defined(invoker.build_native_rust_unit_tests)) { 160 _build_unit_tests = 161 invoker.build_native_rust_unit_tests && can_build_rust_unit_tests 162 } 163 164 # Declares that the Rust crate generates bindings between C++ and Rust via the 165 # Cxx crate. It may generate C++ headers and/or use the cxx crate macros to 166 # generate Rust code internally, depending on what bindings are declared. If 167 # set, it's a set of rust files that include Cxx bindings declarations. 168 _cxx_bindings = [] 169 assert(!defined(invoker.cxx_bindings) || enable_cxx, 170 "cxx bindings are not supported when building rust targets " + 171 "outside the Chromium build.") 172 if (defined(invoker.cxx_bindings)) { 173 _cxx_bindings = invoker.cxx_bindings 174 } 175 _rustenv = [ "OUT_DIR=" + 176 rebase_path(_env_out_dir, get_path_info(_crate_root, "dir")) ] 177 if (defined(invoker.rustenv)) { 178 _rustenv += invoker.rustenv 179 } 180 181 # We require that all source files are listed, even though this is 182 # not a requirement for rustc. The reason is to ensure that tools 183 # such as `gn deps` give the correct answer, and thus we trigger 184 # the right test suites etc. on code change. 185 # TODO(crbug.com/1256930) - verify this is correct 186 assert(defined(invoker.sources), "sources must be listed") 187 188 if (invoker.target_type == "rust_proc_macro" && 189 !toolchain_for_rust_host_build_tools) { 190 # Redirect to the proc macro toolchain, which uses prebuilt stdlib libraries 191 # that are not built with panic=abort. 192 group(_target_name) { 193 testonly = _testonly 194 if (defined(_visibility)) { 195 visibility = _visibility 196 } 197 public_deps = 198 [ ":${_target_name}${_main_target_suffix}($rust_macro_toolchain)" ] 199 } 200 201 not_needed(invoker, "*") 202 not_needed([ 203 "_allow_unsafe", 204 "_build_unit_tests", 205 "_crate_root", 206 "_crate_name", 207 "_cxx_bindings", 208 "_deps", 209 "_aliased_deps", 210 "_is_data_dep", 211 "_rustc_metadata", 212 "_out_dir", 213 "_public_deps", 214 "_rustenv", 215 "_rustflags", 216 "_support_use_from_cpp", 217 "_testonly", 218 "_visibility", 219 ]) 220 } else { 221 _rustc_metadata = "" 222 if (defined(invoker.rustc_metadata)) { 223 _rustc_metadata = invoker.rustc_metadata 224 } 225 226 # Add a metadata-influenced suffix to the output name for libraries only. 227 _output_suffix = "" 228 if (invoker.target_type == "rust_library" && _rustc_metadata != "") { 229 _output_suffix = "-${_rustc_metadata}" 230 } 231 232 group(_target_name) { 233 testonly = _testonly 234 if (defined(_visibility)) { 235 visibility = _visibility 236 } 237 238 # Both the C++ bindings (if present) and the Rust crate should be treated 239 # like direct dependencies, so we expose them both in public_deps. 240 public_deps = [ ":${_target_name}${_main_target_suffix}" ] 241 242 # TODO(danakj): This would not be needed if we stopped forwarding through 243 # a group in the common (non-procmacro) case. 244 if (_is_data_dep) { 245 data_deps = [ ":${_target_name}${_main_target_suffix}" ] 246 } 247 248 if (_cxx_bindings != []) { 249 public_deps += [ ":${_target_name}_cxx_generated" ] 250 251 # Additionally, C++ bindings generated by Cxx can include C++ types 252 # that come from the Cxx library, such as `rust::Str`. So any C++ 253 # target that depends on a rust target directly may need access to Cxx 254 # as well, which means it must appear in public_deps. 255 public_deps += [ "//build/rust:cxx_cppdeps" ] 256 } else if (!defined(invoker.no_std) || !invoker.no_std) { 257 # If C++ depends on and links in the library, we need to make sure C++ 258 # links in the Rust stdlib. This is orthogonal to if the library exports 259 # bindings for C++ to use. 260 deps = [ "//build/rust/std:stdlib_for_clang" ] 261 } 262 } 263 264 _rust_deps = _deps 265 _rust_aliased_deps = _aliased_deps 266 _rust_public_deps = _public_deps 267 _cxx_deps = _deps 268 269 # The Rust target (and unit tests) need the Cxx crate when using it to 270 # generate bindings. 271 if (_cxx_bindings != []) { 272 _rust_deps += [ "//build/rust:cxx_rustdeps" ] 273 274 # C++ targets can depend on the Rust target from the BUILD.gn file to 275 # access the headers generated from it 276 _rust_public_deps += [ ":${_target_name}_cxx_generated" ] 277 } 278 279 if (!defined(invoker.no_std) || !invoker.no_std) { 280 _rust_deps += [ "//build/rust/std:stdlib_for_rustc" ] 281 } 282 283 # You must go through the groups above to get to these targets. 284 _visibility = [] 285 _visibility = [ ":${_target_name}" ] 286 287 if (_build_unit_tests) { 288 _unit_test_target = "${_target_name}_unittests" 289 if (defined(invoker.unit_test_target)) { 290 _unit_test_target = invoker.unit_test_target 291 } 292 293 rust_unit_test(_unit_test_target) { 294 testonly = true 295 crate_root = _crate_root 296 sources = invoker.sources + [ crate_root ] 297 rustflags = _rustflags 298 env_out_dir = _env_out_dir 299 if (defined(invoker.unit_test_output_dir)) { 300 output_dir = invoker.unit_test_output_dir 301 } 302 deps = _rust_deps + _public_deps 303 aliased_deps = _rust_aliased_deps 304 public_deps = [ ":${_target_name}" ] 305 if (defined(invoker.test_deps)) { 306 deps += invoker.test_deps 307 } 308 inputs = [] 309 if (defined(invoker.inputs)) { 310 inputs += invoker.inputs 311 } 312 if (defined(invoker.test_inputs)) { 313 inputs += invoker.test_inputs 314 } 315 if (defined(invoker.executable_configs)) { 316 configs = [] 317 configs += invoker.executable_configs 318 } 319 configs += _test_configs 320 rustenv = _rustenv 321 322 if (!_allow_unsafe) { 323 configs += [ "//build/rust:forbid_unsafe" ] 324 } 325 } 326 } else { 327 not_needed([ 328 "_crate_root", 329 "_crate_name", 330 "_rustc_metadata", 331 "_test_configs", 332 ]) 333 not_needed(invoker, [ "executable_configs" ]) 334 } 335 336 target(invoker.target_type, "${_target_name}${_main_target_suffix}") { 337 forward_variables_from(invoker, 338 "*", 339 TESTONLY_AND_VISIBILITY + [ 340 "features", 341 "deps", 342 "aliased_deps", 343 "public_deps", 344 "rustflags", 345 "rustenv", 346 "configs", 347 "unit_test_output_dir", 348 "unit_test_target", 349 "test_inputs", 350 ]) 351 352 testonly = _testonly 353 visibility = _visibility 354 crate_name = _crate_name 355 crate_root = _crate_root 356 configs = [] 357 configs = _configs 358 deps = _rust_deps 359 aliased_deps = _rust_aliased_deps 360 public_deps = _rust_public_deps 361 rustflags = _rustflags 362 if (_rustc_metadata != "") { 363 rustflags += [ "-Cmetadata=${_rustc_metadata}" ] 364 } 365 rustenv = _rustenv 366 367 if (_generate_crate_root) { 368 deps += [ ":${_target_name}_crate_root" ] 369 sources += [ _crate_root ] 370 } 371 372 # The Rust tool() declarations, like C++ ones, use the output_name and 373 # output_dir, so that GN targets can override these if needed. Here we 374 # give them their default values, or allow them to be overridden. 375 if (defined(_output_dir)) { 376 output_dir = _output_dir 377 } 378 if (!defined(output_name) || output_name == "") { 379 output_name = "${crate_name}${_output_suffix}" 380 } 381 382 if (!_allow_unsafe) { 383 configs += [ "//build/rust:forbid_unsafe" ] 384 } 385 } 386 387 if (_cxx_bindings != []) { 388 rust_cxx("${_target_name}_cxx_generated") { 389 testonly = _testonly 390 visibility = [ ":${_target_name}${_main_target_suffix}" ] 391 if (defined(_visibility)) { 392 visibility += _visibility 393 } 394 sources = _cxx_bindings 395 deps = _cxx_deps + _public_deps 396 configs = _configs 397 398 if (is_component_build) { 399 # In a component_build the cxx bindings may be linked into a shared 400 # library at any point up the dependency tree, so always export. 401 export_symbols = true 402 } else if (invoker.target_type == "shared_library") { 403 export_symbols = true 404 } else { 405 export_symbols = false 406 } 407 } 408 } else { 409 not_needed([ "_cxx_deps" ]) 410 } 411 } 412} 413