blob: 5ccb306d8a97aa9a935b8f502601366280c3ad50 [file] [log] [blame] [edit]
# Copyright 2024 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/zircon/c_utils.gni")
# Generate ld::testing::TestElfObject data.
#
# This defines a target to examine a linked ELF file and generate source code
# to define an ld::testing::TestElfObject C++ object for it. This target is a
# build action that generates a source code fragment into a file, but the only
# "output" of this target is its metadata. A test_elf_object() target's only
# purpose is to be reached by the $deps of test_elf_load_set() targets, which
# consume that metadata.
#
# Note that while a test_elf_object() target is not inherently testonly, the
# targets that consume its metadata are all automatically testonly.
#
# Parameters
#
# * deps
# - Required: Should reach at least one linking target. It can reach more
# than one, to generate multiple ld::testing::TestElfObject objects at
# once. But usually there's a separate test_elf_object() for each linking
# target, so it's regenerated only when that one target is relinked.
# - Type: list(label)
#
# * visibility, testonly
# - Optional: Standard GN meaning.
# - Type: bool
#
template("test_elf_object") {
main_target = target_name
rspfile_target = "$target_name.rsp"
rspfile = "$target_gen_dir/$rspfile_target"
libprefix_target = "$target_name.libprefix"
libprefix_rspfile = "$target_gen_dir/$libprefix_target"
# `inline const ld::testing::TestElfObject kTestObject_<build-id-hex> = ...;`
gen_object = "$target_gen_dir/$target_name.test-elf-object.object.inc"
gen_object_path = rebase_path(gen_object, root_build_dir)
# `, kTestObject_<build-id-hex>`
gen_entry = "$target_gen_dir/$target_name.test-elf-object.entry.inc"
gen_entry_path = rebase_path(gen_entry, root_build_dir)
toolchain_utils_action(main_target) {
forward_variables_from(invoker,
[
"visibility",
"testonly",
])
script = "//sdk/lib/ld/testing/gen-test-elf-object.py"
utils = [ "llvm-readelf" ]
sources = [
libprefix_rspfile,
rspfile,
]
depfile = "$target_gen_dir/$target_name.d"
outputs = [
gen_object,
gen_entry,
]
deps = [
":$libprefix_target",
":$rspfile_target",
]
args = [
"--rspfile=" + rebase_path(rspfile, root_build_dir),
"--libprefix-rspfile=" + rebase_path(libprefix_rspfile, root_build_dir),
"--depfile=" + rebase_path(depfile, root_build_dir),
"--object=$gen_object_path",
"--entry=$gen_entry_path",
]
metadata = {
test_elf_object_include_object = [ "#include \"$gen_object_path\"" ]
test_elf_object_include_entry = [ "#include \"$gen_entry_path\"" ]
}
}
link_output_rspfile(rspfile_target) {
visibility = [ ":$main_target" ]
forward_variables_from(invoker, [ "testonly" ])
outputs = [ rspfile ]
deps = invoker.deps
}
generated_file(libprefix_target) {
visibility = [ ":$main_target" ]
forward_variables_from(invoker, [ "testonly" ])
outputs = [ libprefix_rspfile ]
deps = invoker.deps
output_conversion = "list lines"
data_keys = [ "libprefix" ]
walk_keys = [ "link_output_barrier" ]
}
}
# Generate ld::testing::TestElfLoadSet data.
#
# This collects metadata from any test_elf_object() targets in its $deps graph,
# and assembles them into a larger source code fragment to define one named
# ld::testing::TestElfLoadSet C++ object. The underlying targets are just
# generated_file(), so there is no work done after the `gn gen` stage. These
# files are referenced only via this target's metadata, which is collected by
# test_elf_source_set() targets.
#
# test_elf_load_set() targets are inherently testonly.
#
# Parameters
#
# * deps
# - Required: Should reach test_elf_object() targets.
# - Type: list(label)
#
# * output_name
# - Required: Name for `ld::testing::TestLoadSet::FindLoadSet`.
# - Type: string
#
# * visibility
# - Optional: Standard GN meaning.
# - Type: bool
#
template("test_elf_load_set") {
main_target = target_name
set_name = invoker.output_name
set_id = string_replace(string_replace(set_name, "-", "_"), ".", "_")
gen_set = "$target_gen_dir/$main_target.load-set.cc"
gen_set_path = rebase_path(gen_set, root_build_dir)
gen_object_inc_target = "$main_target.object"
gen_object_inc = "$target_gen_dir/$main_target.object.inc"
gen_object_inc_path = rebase_path(gen_object_inc, root_build_dir)
gen_entry_inc_target = "$main_target.entry"
gen_entry_inc = "$target_gen_dir/$main_target.entry.inc"
gen_entry_inc_path = rebase_path(gen_entry_inc, root_build_dir)
label = get_label_info(":$main_target", "label_with_toolchain")
generated_file(main_target) {
forward_variables_from(invoker, [ "visibility" ])
testonly = true
outputs = [ gen_set ]
contents = [
"// Generated by $label. DO NOT EDIT!",
"namespace ld::testing {",
"using namespace elfldltl::literals;",
"#include \"$gen_object_inc_path\"",
"const TestElfLoadSet kTestElfLoadSet_$set_id{\"$set_name\"_soname,",
" kTestElfObjectList<void",
"#include \"$gen_entry_inc_path\"",
" >};",
"} // namespace ld::testing",
]
deps = [
":$gen_entry_inc_target",
":$gen_object_inc_target",
]
metadata = {
test_elf_load_set_include = [ "#include \"$gen_set_path\"" ]
# The barrier prevents another test_elf_load_set() whose deps reach this
# one from looking into this target's $deps to find the test_elf_object()
# targets that were already collected here.
test_elf_load_set_barrier = []
# These barriers prevent the deps propagating up things meant only for
# the direct users of the test ELF objects per se, not for the test
# harness code that uses the data about those ELF objects.
distribution_entries_barrier = []
link_output_barrier = []
}
}
generated_file(gen_object_inc_target) {
visibility = [ ":$main_target" ]
testonly = true
outputs = [ gen_object_inc ]
deps = invoker.deps
data_keys = [ "test_elf_object_include_object" ]
walk_keys = [ "test_elf_load_set_barrier" ]
}
generated_file(gen_entry_inc_target) {
visibility = [ ":$main_target" ]
testonly = true
outputs = [ gen_entry_inc ]
deps = invoker.deps
data_keys = [ "test_elf_object_include_entry" ]
walk_keys = [ "test_elf_load_set_barrier" ]
}
}
# Compile in ld::testing::TestElfLoadSet data.
#
# This produces a source_set() to compile the results of test_elf_load_set()
# targets into test code. Including this source_set() in a test executable
# enables `ld::testing::TestElfLoadSet::Get("$output_name")` to find the data
# for the test_elf_load_set() in $deps with that $output_name.
#
# test_elf_source_set() targets are inherently testonly.
#
# Parameters
#
# * deps
# - Required: Should reach any number of test_elf_load_set() targets.
# - Type: list(label)
#
# * visibility
# - Optional: Standard GN meaning.
# - Type: list(label)
#
template("test_elf_source_set") {
main_target = target_name
preamble_target = "$target_name.preamble"
gen_target = "$target_name.gen"
gen_file = "$target_gen_dir/$target_name.cc"
label = get_label_info(":$main_target", "label_with_toolchain")
source_set(main_target) {
forward_variables_from(invoker, [ "visibility" ])
testonly = true
sources = [ gen_file ]
include_dirs = [ root_build_dir ]
deps = [
":$gen_target",
"//sdk/lib/ld/testing",
]
}
group(preamble_target) {
visibility = [ ":$gen_target" ]
testonly = true
metadata = {
test_elf_load_set_include = [
"// Generated by $label. DO NOT EDIT!",
"#include <lib/ld/testing/test-elf-object.h>",
]
}
}
generated_file(gen_target) {
visibility = [ ":$main_target" ]
testonly = true
outputs = [ gen_file ]
deps = [ ":$preamble_target" ] + invoker.deps
data_keys = [ "test_elf_load_set_include" ]
}
}