blob: d013e72c42b9a1af7a51654db286dedbc1663135 [file] [log] [blame]
# Copyright 2018 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/rust/config.gni") # for rust_config
import("//build/rust/toolchain.gni")
# BEGIN constants {{{
# file locations
first_party_crate_root = "${root_out_dir}/rust_crates"
third_party_search_path_config = "//third_party/rust_crates:search_path_config"
third_party_deps_data = "${root_out_dir}/rust_third_party_crates/deps_data.json"
third_party_crate_root = "${root_out_dir}/rust_third_party_crates"
if (host_os == "mac") {
proc_macro_ext = ".dylib"
} else {
proc_macro_ext = ".so"
}
# }}} END constants
# Defines a Rust artifact to be built directly with rustc (rather than using cargo)
#
# Only for internal use, supporting rustc_{binary,library,macro,staticlib,test}.
#
# The arguments are the same as for rustc_library and rustc_binary, with the exception
# of `type`, which must be one of bin/lib/staticlib/proc-macro. This is used to determine
# the kind of artifact that is produced by rustc.
#
# Parameters
#
# name
# type
# version
# visibility
# edition
# force_opt
# with_lto
# output_filename_suffix
# only_unit_tests
# with_unit_tests
# target_name
# deps
# non_rust_deps
# test_environments
# source_root
# __unstable_recovery_netstack_only_specialization_bypass
# features
# testonly
# test_deps
# sdk_category
# sdk_deps
# test
template("rustc_artifact") {
forward_variables_from(invoker, [ "visibility" ])
# BEGIN invoker.type -> type {{{
assert(defined(invoker.type),
"Must specify an artifact type (bin/lib/staticlib/proc-macro)")
type = invoker.type
# bin: executable binary application
# lib: intermediate artifact to be used in other Rust programs
# staticlib: a statically-linked system library, generally used for linking Rust into C
# proc-macro: a procedural macro (such as a custom-derive)
assert(type == "bin" || type == "lib",
"Artifact type must be one of: \"bin\", or \"lib\"")
if (type == "lib") {
# For now, lib == rlib, but this could change in future versions of rustc.
# If/when this changes, we will likely want to transition manually rather
# than being automatically changed as a result of a toolchain upgrade.
type = "rlib"
}
# }}} END invoker.type -> type
# BEGIN invoker.name -> package_name, crate_name {{{
if (defined(invoker.name)) {
package_name = invoker.name
} else {
package_name = target_name
}
crate_name = string_replace(package_name, "-", "_")
# }}} END invoker.name -> package_name, crate_name
# BEGIN invoker.version -> version {{{
if (defined(invoker.version)) {
version = invoker.version
} else {
version = "0.1.0"
}
# }}} END invoker.version -> version
# BEGIN invoker.edition -> edition {{{
assert(defined(invoker.edition), "Must specify an edition. Preferably 2018")
# }}} END invoker.edition -> edition
# BEGIN calculate rust_opt_level {{{
if (rust_override_opt != "") {
rust_opt_level = rust_override_opt
if (defined(invoker.force_opt)) {
not_needed(invoker, [ "force_opt" ])
}
} else if (defined(invoker.force_opt)) {
rust_opt_level = invoker.force_opt
} else {
if (is_debug) {
rust_opt_level = "0"
} else {
rust_opt_level = "z"
}
}
rust_opt_level_arg = "-Copt-level=$rust_opt_level"
# }}} END calculate rust_opt_level
# BEGIN calculate with_lto, rust_lto_args {{{
if (type == "bin" || type == "staticlib") {
if (rust_override_lto != "") {
with_lto = rust_override_lto
if (defined(invoker.with_lto)) {
not_needed(invoker, [ "with_lto" ])
}
} else if (defined(invoker.with_lto)) {
with_lto = invoker.with_lto
} else if (rust_lto != "") {
with_lto = rust_lto
} else if (is_debug) {
with_lto = "none"
} else {
# Release builds default to "thin" lto
with_lto = "thin"
}
} else {
with_lto = "none"
}
assert(with_lto == "none" || with_lto == "thin" || with_lto == "fat",
"with_lto was neither none, thin, or fat")
rust_lto_args = []
if (with_lto != "none") {
rust_lto_args += [ "-Clto=$with_lto" ]
}
# }}} END calculate with_lto, rust_lto_args
# BEGIN calculate output_file, root_file {{{
# Determine the prefix and extension for the output file based on the crate type
if (type == "bin") {
prefix = ""
extension = ""
root_file_default = "src/main.rs"
} else if (type == "rlib") {
prefix = "lib"
extension = ".rlib"
root_file_default = "src/lib.rs"
}
only_unit_tests = defined(invoker.only_unit_tests) && invoker.only_unit_tests
if (defined(invoker.output_filename_suffix)) {
output_filename = "${crate_name}${invoker.output_filename_suffix}"
} else {
output_filename = crate_name
}
if (type == "bin") {
output_file_dir = root_out_dir
} else if (type == "staticlib") {
output_file_dir = root_out_dir
} else {
output_file_dir = first_party_crate_root
}
output_file = "${output_file_dir}/${prefix}${output_filename}${extension}"
if (defined(invoker.source_root)) {
not_needed([ "root_file_default" ])
root_file = rebase_path(invoker.source_root)
} else {
root_file = rebase_path(root_file_default)
}
# }}} END calculate output_file, root_file
if (defined(invoker.non_rust_deps)) {
non_rust_deps = invoker.non_rust_deps
} else {
non_rust_deps = []
}
group_deps = []
if (only_unit_tests) {
not_needed([
"output_filename",
"output_file",
])
} else {
build_target_name = "${target_name}_build"
group_deps += [ ":${build_target_name}" ]
}
with_unit_tests = only_unit_tests || (defined(invoker.with_unit_tests) &&
invoker.with_unit_tests)
# BEGIN calculate test_output_file, build_test_target_name {{{
if (with_unit_tests) {
if (only_unit_tests) {
test_filename = crate_name
} else {
test_filename = "${crate_name}_${invoker.type}_test"
}
test_output_file = "${root_out_dir}/tests/rust/${test_filename}"
not_needed([ "test_output_file" ])
build_test_target_name = "${target_name}_test_build"
test_group_deps = [ ":${build_test_target_name}" ]
}
# }}} END calculate test_output_file, build_test_target_name
# BEGIN calculate unstable_rust_features {{{
# The set of unstable features permitted as per
# https://fuchsia.googlesource.com/fuchsia/+/refs/heads/master/docs/development/languages/rust/unstable.md
unstable_rust_features = []
# Specifically enable specialization for use in netstack3.
# This flag is not allowed to be introduced in new code.
if (defined(invoker.__unstable_netstack3_only_specialization_bypass)) {
unstable_rust_features +=
invoker.__unstable_netstack3_only_specialization_bypass
}
rust_unstable_feature_arg = "-Zallow-features="
foreach(unstable_rust_feature, unstable_rust_features) {
rust_unstable_feature_arg += unstable_rust_feature + ","
}
# }}} END calculate unstable_rust_features
# BEGIN calculate cfg_feature_args {{{
cfg_feature_args = []
if (defined(invoker.features)) {
foreach(feature, invoker.features) {
cfg_feature_args += [
"--cfg",
"feature=\"" + feature + "\"",
]
}
}
# }}} END calculate cfg_feature_args
cargo_toml_dir = "$target_gen_dir/$target_name"
# BEGIN write info.json {{{
info_target_name = "${target_name}_info"
generated_file(info_target_name) {
# Write out static information about the target to be used by
# reverse-dependencies to determine the content of their Cargo.toml
# files and --extern invocations.
outputs = [
"${target_out_dir}/${target_name}.json",
]
contents = {
cargo_toml_dir = rebase_path(cargo_toml_dir)
crate_name = crate_name
lib_path = rebase_path(output_file)
package_name = package_name
third_party = false
version = version
}
output_conversion = "json"
}
group_deps += [ ":${info_target_name}" ]
# }}} END write info.json
build_target_deps = []
if (defined(invoker.deps)) {
build_target_deps += invoker.deps
}
# from build/rust/config.gni
build_target_deps += std_deps
build_target_deps += sysroot_deps
shared_rflags = [
rust_opt_level_arg,
rust_unstable_feature_arg,
"--edition=${invoker.edition}",
# from build/rust/config.gni
"--target",
target,
"--cap-lints",
rust_cap_lints,
] + rust_lto_args + cfg_feature_args
if (is_mac) {
# For mac_sdk_min:
import("//build/config/mac/mac_sdk.gni")
shared_rflags += [ "-Clink-arg=-mmacosx-version-min=" + mac_sdk_min ]
}
if (is_fuchsia) {
shared_rflags += [
"-L",
sysroot + "/lib",
"-Clinker=$clang_prefix/lld",
"-Clink-arg=--pack-dyn-relocs=relr",
"-Clink-arg=--sysroot=" + sysroot,
"-Clink-arg=-L" + sysroot + "/lib",
"-Clink-arg=-L" + clang_resource_dir + "/" + target + "/lib",
"-Clink-arg=--threads",
"-Clink-arg=-dynamic-linker=ld.so.1",
"-Clink-arg=--icf=all",
]
if (current_cpu == "arm64") {
shared_rflags += [ "-Clink-arg=--fix-cortex-a53-843419" ]
}
} else {
shared_rflags += [ "-Clinker=$clang_prefix/clang" ]
if (current_cpu == "arm64") {
shared_rflags += [ "-Clink-arg=-Wl,--fix-cortex-a53-843419" ]
}
if (current_os == "linux") {
shared_rflags += [ "-Clink-arg=-Wl,--build-id" ]
}
if (current_os != "mac") {
shared_rflags += [
"-Clink-arg=-Wl,--threads",
"-Clink-arg=-Wl,--icf=all",
]
}
}
if (defined(invoker.allow_deprecated) && invoker.allow_deprecated) {
shared_rflags += [
"-A",
"deprecated",
]
}
# BEGIN build target {{{
if (!only_unit_tests) {
rflags = shared_rflags
if (type == "bin") {
executable(build_target_name) {
forward_variables_from(invoker, [ "testonly" ])
crate_root = root_file
crate_name = crate_name
deps = build_target_deps
output_name = output_filename
public_deps = non_rust_deps
rustflags = rflags
}
} else if (type == "rlib") {
rust_library(build_target_name) {
forward_variables_from(invoker, [ "testonly" ])
crate_root = root_file
crate_name = crate_name
deps = build_target_deps
output_name = output_filename
public_deps = non_rust_deps
rustflags = rflags
}
} else {
assert(
false,
"unsupported crate type, should've been caught above-- report this issue to the toolchain team")
}
}
# }}} END buildtarget
# BEGIN test target {{{
if (with_unit_tests) {
test_target_deps = []
if (defined(invoker.test_deps)) {
test_target_deps += invoker.test_deps
}
executable(build_test_target_name) {
crate_root = root_file
crate_name = crate_name
testonly = true
output_dir = "$root_out_dir/tests/rust"
output_name = test_filename
rustflags = shared_rflags + [
"--test",
"--crate-type",
type,
]
deps = build_target_deps + test_target_deps + non_rust_deps
if (defined(invoker.test_deps)) {
deps += invoker.test_deps
}
}
}
# }}} END test target
# BEGIN strip binary and create sdk atom {{{
if (type == "bin" && !only_unit_tests) {
group_deps += [ ":${build_target_name}" ]
# if appropriate, create an SDK atom for the binary that we just stripped
if (defined(invoker.sdk_category) && invoker.sdk_category != "excluded" &&
!is_fuchsia && !(defined(invoker.test) && invoker.test)) {
output_name = target_name
file_base = "tools/$output_name"
sdk_atom("${target_name}_sdk") {
id = "sdk://tools/${output_name}"
category = invoker.sdk_category
meta = {
dest = "$file_base-meta.json"
schema = "host_tool"
value = {
type = "host_tool"
name = output_name
root = "tools"
files = [ file_base ]
}
}
files = [
{
source = "$root_out_dir/$output_filename"
dest = file_base
},
]
if (defined(invoker.sdk_deps)) {
deps = invoker.sdk_deps
}
non_sdk_deps = [ ":$build_target_name" ]
}
}
}
# }}} END strip binary and create sdk atom
# BEGIN generate Cargo.toml {{{
cargo_toml_target_name = "${target_name}_cargo"
group_deps += [ ":${cargo_toml_target_name}" ]
# Iterate through the deps collecting a list of the outputs
# of their build targets to use for calculating the dep data needed
# to produce Cargo.toml files.
dep_info_paths = []
dep_info_sources = []
if (defined(invoker.deps)) {
foreach(dep, invoker.deps) {
dep_target_name = get_label_info(dep, "name")
dep_dir = get_label_info(dep, "dir")
dep_build_target = "${dep_dir}:${dep_target_name}_build"
dep_out_dir = get_label_info(dep_build_target, "target_out_dir")
dep_info_path = "${dep_out_dir}/${dep_target_name}_info.json"
dep_info_paths += [
"--dep-data",
rebase_path(dep_info_path),
]
dep_info_sources += [ rebase_path(dep_info_path) ]
}
}
test_dep_info_paths = []
test_dep_info_sources = []
if (defined(invoker.test_deps)) {
foreach(dep, invoker.test_deps) {
dep_target_name = get_label_info(dep, "name")
dep_dir = get_label_info(dep, "dir")
dep_build_target = "${dep_dir}:${dep_target_name}_build"
dep_out_dir = get_label_info(dep_build_target, "target_out_dir")
dep_info_path = "${dep_out_dir}/${dep_target_name}_info.json"
test_dep_info_paths += [
"--test-only-dep-data",
rebase_path(dep_info_path),
]
dep_info_sources += [ rebase_path(dep_info_path) ]
}
}
action(cargo_toml_target_name) {
script = "//build/rust/write_cargo_toml.py"
forward_variables_from(invoker, [ "testonly" ])
deps = []
if (defined(invoker.deps)) {
foreach(dep, invoker.deps) {
dep_info_name = get_label_info(dep, "label_no_toolchain") + "_info"
dep_info_name += "(" + get_label_info(dep, "toolchain") + ")"
deps += [ dep_info_name ]
}
}
if (defined(invoker.test_deps)) {
foreach(test_dep, invoker.test_deps) {
dep_info_name = get_label_info(test_dep, "label_no_toolchain") + "_info"
dep_info_name += "(" + get_label_info(test_dep, "toolchain") + ")"
deps += [ dep_info_name ]
}
}
args = [
"--out-dir",
rebase_path(cargo_toml_dir),
"--source-root",
root_file,
"--package-name",
package_name,
"--crate-name",
crate_name,
"--crate-type",
type,
"--version",
version,
"--edition",
invoker.edition,
"--third-party-deps-data",
rebase_path(third_party_deps_data),
]
if (defined(invoker.features)) {
foreach(feature, invoker.features) {
args += [
"--feature",
feature,
]
}
}
if (with_lto != "none") {
args += [
"--lto",
with_lto,
]
}
# list of paths to info about crate dependencies
args += dep_info_paths
args += test_dep_info_paths
sources = dep_info_sources
sources += test_dep_info_sources
outputs = [
"${cargo_toml_dir}/Cargo.toml",
]
}
# }}} END generate Cargo.toml
# BEGIN declare test_target_name group {{{
if (with_unit_tests) {
test_target_name = "${target_name}_test"
group(test_target_name) {
testonly = true
public_deps = test_group_deps
}
if (only_unit_tests) {
group_deps += [ ":${test_target_name}" ]
}
}
# }}} END test_target_name group
group(target_name) {
forward_variables_from(invoker, [ "testonly" ])
public_deps = group_deps
}
}