third_party.pigweed.src/pw_unit_test/test.gni
Michael Spang c8b939009b Remove absolute paths from build command lines
The GN convention is to specify paths in command lines relative to the
build directory. Unfortunately and contrary to pigweed's expectations
this is not what rebase_path(path) does; that outputs an absolute path.

Absolute paths are not desirable in most circumstances as they contain
sources of nondeterminism such as the developer's home directory. Using
them can for example reduce hit rate in build caches.

Replace rebase_path(path) with rebase_path(path, root_build_dir) which
is the correct idiom and matches GN's builtin behavior (e.g. for
sources, include_dirs, etc).

This also removes the --directory argument to python_action(). Changing
the directory during the build while using relative paths is likely to
result in confusion and should be discouraged.

There's a couple more things to do on top of this for identical
binaries between build directories / machines / developers:

- pass options to avoid embedding the working directory
- pass options to use relative paths for the vendored clang & libc++

See [1]-[2] for how to do that.

[1] https://source.chromium.org/chromium/chromium/src/+/main:build/config/compiler/BUILD.gn;l=1170-1239;drc=ab531c265c533cba1c2f6d8240cc0bf7679f605a
[2] https://cs.opensource.google/fuchsia/fuchsia/+/main:build/config/BUILD.gn;l=145-216;drc=f6d705f0937c778d5d5f807a4580113612b02f5a

Change-Id: I17708102c03d6488d68c8571b6e9343191fd47de
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/47461
Reviewed-by: Wyatt Hepler <hepler@google.com>
Commit-Queue: Michael Spang <spang@google.com>
2021-06-09 19:15:51 +00:00

332 lines
11 KiB
Plaintext

# Copyright 2019 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
import("//build_overrides/pigweed.gni")
import("$dir_pw_build/python_action.gni")
import("$dir_pw_build/target_types.gni")
declare_args() {
# Path to a test runner to automatically run unit tests after they are built.
#
# If set, the pw_test() template creates an action that invokes the test runner
# on each test executable. If unset, the pw_test() template only creates a test
# executable target.
#
# This should only be enabled for targets which support parallelized running of
# unit tests, such as desktops with multiple cores.
pw_unit_test_AUTOMATIC_RUNNER = ""
# Additional dependencies required by all unit test targets. (For example, if
# using a different test library like Googletest.)
pw_unit_test_PUBLIC_DEPS = []
# Implementation of a main function for "pw_test" unit test binaries.
pw_unit_test_MAIN = "$dir_pw_unit_test:simple_printing_main"
}
# Defines a target if enable_if is true. Otherwise, it defines that target as
# <target_name>.DISABLED and creates an empty <target_name> group. This can be
# used to conditionally create targets without having to conditionally add them
# to groups. This results in simpler BUILD.gn files.
template("_pw_disableable_target") {
assert(defined(invoker.enable_if),
"`enable_if` is required for _pw_disableable_target")
assert(defined(invoker.target_type),
"`target_type` is required for _pw_disableable_target")
if (invoker.enable_if) {
_actual_target_name = target_name
} else {
_actual_target_name = target_name + ".DISABLED"
# If the target is disabled, create an empty target in its place. Use an
# action with the original target's sources as inputs to ensure that
# the source files exist (even if they don't compile).
pw_python_action(target_name) {
script = "$dir_pw_build/py/pw_build/nop.py"
stamp = true
inputs = []
if (defined(invoker.sources)) {
inputs += invoker.sources
}
if (defined(invoker.public)) {
inputs += invoker.public
}
}
}
target(invoker.target_type, _actual_target_name) {
forward_variables_from(invoker,
"*",
[
"enable_if",
"target_type",
])
# Remove "" from dependencies. This allows disabling targets if a variable
# (e.g. a backend) is empty.
if (defined(public_deps)) {
public_deps += [ "" ]
public_deps -= [ "" ]
}
if (defined(deps)) {
deps += [ "" ]
deps -= [ "" ]
}
}
}
# Creates a library and an executable target for a unit test.
#
# <target_name>.lib contains the provided test sources as a library, which can
# then be linked into a test executable.
# <target_name> is a standalone executable which contains only the test sources
# specified in the pw_unit_test_template.
#
# If the pw_unit_test_AUTOMATIC_RUNNER variable is set, this template also creates a
# "${test_name}.run" target which runs the unit test executable after building
# it.
#
# Args:
# - enable_if: (optional) Conditionally enables or disables this test. The test
# target and *.run target do nothing when the test is disabled. The
# disabled test can still be built and run with the
# <target_name>.DISABLED and <target_name>.DISABLED.run targets.
# Defaults to true (enable_if).
# - All of the regular "executable" target args are accepted.
template("pw_test") {
# This is required in order to reference the pw_test template's target name
# within the test_metadata of the metadata group below. The group() definition
# creates a new scope where the "target_name" variable is set to its target,
# shadowing the one in this scope.
_test_target_name = target_name
_test_is_enabled = !defined(invoker.enable_if) || invoker.enable_if
# Always set the output_dir as pigweed is not compatible with shared
# bin directories for tests.
_test_output_dir = "${target_out_dir}/test"
if (defined(invoker.output_dir)) {
_test_output_dir = invoker.output_dir
}
_test_main = pw_unit_test_MAIN
if (defined(invoker.test_main)) {
_test_main = invoker.test_main
}
# The unit test code as a source_set.
_pw_disableable_target("$target_name.lib") {
target_type = "pw_source_set"
enable_if = _test_is_enabled
forward_variables_from(invoker, "*", [ "metadata" ])
if (!defined(public_deps)) {
public_deps = []
}
public_deps += pw_unit_test_PUBLIC_DEPS + [ dir_pw_unit_test ]
}
_pw_disableable_target(_test_target_name) {
target_type = "pw_executable"
enable_if = _test_is_enabled
# Metadata for this test when used as part of a pw_test_group target.
metadata = {
tests = [
{
type = "test"
test_name = _test_target_name
test_directory = rebase_path(_test_output_dir, root_build_dir)
},
]
}
deps = [ ":$_test_target_name.lib" ]
if (_test_main != "") {
deps += [ _test_main ]
}
output_dir = _test_output_dir
}
if (pw_unit_test_AUTOMATIC_RUNNER != "") {
# When the automatic runner is set, create an action which runs the unit
# test executable using the test runner script.
if (_test_is_enabled) {
_test_to_run = _test_target_name
} else {
# Create a run target for the .DISABLED version of the test.
_test_to_run = _test_target_name + ".DISABLED"
# Create a placeholder _run target for the regular version of the test.
group(_test_target_name + ".run") {
deps = [ ":$_test_target_name" ]
}
}
pw_python_action(_test_to_run + ".run") {
deps = [ ":$_test_target_name" ]
inputs = [ pw_unit_test_AUTOMATIC_RUNNER ]
script = "$dir_pw_unit_test/py/pw_unit_test/test_runner.py"
python_deps = [ "$dir_pw_cli/py" ]
args = [
"--runner",
rebase_path(pw_unit_test_AUTOMATIC_RUNNER, root_build_dir),
"--test",
"<TARGET_FILE(:$_test_to_run)>",
]
stamp = true
}
# TODO(frolv): Alias for the deprecated _run target. Remove when projects
# are migrated.
group(_test_to_run + "_run") {
public_deps = [ ":$_test_to_run.run" ]
}
} else {
group(_test_target_name + ".run") {
}
}
}
# Defines a related collection of unit tests.
#
# pw_test_group targets output a JSON metadata file for the Pigweed test runner.
#
# Args:
# - tests: List of pw_test targets for each of the tests in the group.
# - group_deps: (optional) pw_test_group targets on which this group depends.
# - enable_if: (optional) Conditionally enables or disables this test group.
# If false, an empty group is created. Defaults to true.
template("pw_test_group") {
_group_target = target_name
_group_deps_metadata = []
if (defined(invoker.tests)) {
_deps = invoker.tests
} else {
_deps = []
}
_group_is_enabled = !defined(invoker.enable_if) || invoker.enable_if
if (_group_is_enabled) {
if (defined(invoker.group_deps)) {
# If the group specified any other group dependencies, create a metadata
# entry for each of them indicating that they are another group and a
# group target to collect that metadata.
foreach(dep, invoker.group_deps) {
_group_deps_metadata += [
{
type = "dep"
group = get_label_info(dep, "label_no_toolchain")
},
]
}
_deps += invoker.group_deps
}
group(_group_target + ".lib") {
deps = []
foreach(_target, _deps) {
_dep_target = get_label_info(_target, "label_no_toolchain")
_dep_toolchain = get_label_info(_target, "toolchain")
deps += [ "$_dep_target.lib($_dep_toolchain)" ]
}
}
_metadata_group_target = "${target_name}_pw_test_group_metadata"
group(_metadata_group_target) {
metadata = {
group_deps = _group_deps_metadata
self = [
{
type = "self"
name = get_label_info(":$_group_target", "label_no_toolchain")
},
]
# Metadata from the group's own unit test targets is forwarded through
# the group dependencies group. This entry is listed as a "walk_key" in
# the generated file so that only test targets' metadata (not group
# targets) appear in the output.
if (defined(invoker.tests)) {
propagate_metadata_from = invoker.tests
}
}
deps = _deps
}
_test_group_deps = [ ":$_metadata_group_target" ]
generated_file(_group_target) {
outputs = [ "$target_out_dir/$target_name.testinfo.json" ]
data_keys = [
"group_deps",
"self",
"tests",
]
walk_keys = [ "propagate_metadata_from" ]
output_conversion = "json"
deps = _test_group_deps
}
# If automatic test running is enabled, create a *.run group that collects
# all of the individual *.run targets and groups.
if (pw_unit_test_AUTOMATIC_RUNNER != "") {
group(_group_target + ".run") {
deps = [ ":$_group_target" ]
foreach(_target, _deps) {
_dep_target = get_label_info(_target, "label_no_toolchain")
_dep_toolchain = get_label_info(_target, "toolchain")
deps += [ "$_dep_target.run($_dep_toolchain)" ]
}
}
# TODO(frolv): Remove this deprecated alias.
group(_group_target + "_run") {
deps = [ ":$_group_target.run" ]
}
}
} else { # _group_is_enabled
# Create empty groups for the tests to avoid pulling in any dependencies.
group(_group_target) {
}
group(_group_target + ".lib") {
}
if (pw_unit_test_AUTOMATIC_RUNNER != "") {
group(_group_target + ".run") {
}
# TODO(frolv): Remove this deprecated alias.
group(_group_target + "_run") {
}
}
not_needed("*")
not_needed(invoker, "*")
}
# All of the tests in this group and its dependencies bundled into a single
# test binary.
pw_test(_group_target + ".bundle") {
deps = [ ":$_group_target.lib" ]
enable_if = _group_is_enabled
}
}