blob: 661df320222dba7de996fa2b529b32ed5b14164b [file] [log] [blame]
# 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")
declare_args() {
# Path to the Bloaty configuration file that defines the memory layout and
# capacities for the target binaries.
pw_bloat_BLOATY_CONFIG = ""
# List of toolchains to use in pw_toolchain_size_report templates.
#
# Each entry is a scope containing the following variables:
#
# name: Human-readable toolchain name.
# target: GN target that defines the toolchain.
# linker_script: Optional path to a linker script file to build for the
# toolchain's target.
# bloaty_config: Optional Bloaty confirugation file defining the memory
# layout of the binaries as specified in the linker script.
#
# If this list is empty, pw_toolchain_size_report targets become no-ops.
pw_bloat_TOOLCHAINS = []
# Controls whether to display size reports in the build output.
pw_bloat_SHOW_SIZE_REPORTS = false
}
# Creates a target which runs a size report diff on a set of executables.
#
# Args:
# base: The default base executable target to run the diff against. May be
# omitted if all binaries provide their own base.
# binaries: List of executables to compare in the diff.
# Each binary in the list is a scope containing up to three variables:
# label: Descriptive name for the executable. Required.
# target: Build target for the executable. Required.
# base: Optional base diff target. Overrides global base argument.
# source_filter: Optional regex to filter data source names in Bloaty.
# title: Optional title string to display with the size report.
# full_report: Optional boolean flag indicating whether to produce a full
# symbol size breakdown or a summary.
#
# Example:
# pw_size_report("foo_bloat") {
# base = ":foo_base"
# binaries = [
# {
# target = ":foo_static"
# label = "Static"
# },
# {
# target = ":foo_dynamic"
# label = "Dynamic"
# },
# ]
# title = "static vs. dynamic foo"
# }
#
template("pw_size_report") {
if (pw_bloat_BLOATY_CONFIG != "") {
if (defined(invoker.base)) {
_global_base = invoker.base
_all_target_dependencies = [ _global_base ]
} else {
_all_target_dependencies = []
}
if (defined(invoker.title)) {
_title = invoker.title
} else {
_title = target_name
}
# This template creates an action which invokes a Python script to run a
# size report on each of the provided targets. Each of the targets is listed
# as a dependency of the action so that the report gets updated when
# anything is changed. Most of the code below builds the command-line
# arguments to pass each of the targets into the script.
_binary_paths = []
_binary_labels = []
_bloaty_configs = []
# Process each of the binaries, resolving their full output paths and
# building them into a list of command-line arguments to the bloat script.
foreach(binary, invoker.binaries) {
assert(defined(binary.label) && defined(binary.target),
"Size report binaries must define 'label' and 'target' variables")
_all_target_dependencies += [ binary.target ]
_binary_path = "<TARGET_FILE(${binary.target})>"
# If the binary defines its own base, use that instead of the global base.
if (defined(binary.base)) {
_binary_base = binary.base
_all_target_dependencies += [ _binary_base ]
} else if (defined(_global_base)) {
_binary_base = _global_base
} else {
assert(false, "pw_size_report requires a 'base' file")
}
# Allow each binary to override the global bloaty config.
if (defined(binary.bloaty_config)) {
_bloaty_configs += [ binary.bloaty_config ]
} else {
_bloaty_configs += [ pw_bloat_BLOATY_CONFIG ]
}
_binary_path += ";" + "<TARGET_FILE($_binary_base)>"
_binary_paths += [ _binary_path ]
_binary_labels += [ binary.label ]
}
_bloat_script_args = [
"--bloaty-config",
string_join(";", rebase_path(_bloaty_configs, root_build_dir)),
"--out-dir",
rebase_path(target_gen_dir, root_build_dir),
"--target",
target_name,
"--title",
_title,
"--labels",
string_join(";", _binary_labels),
]
if (defined(invoker.full_report) && invoker.full_report) {
_bloat_script_args += [ "--full" ]
}
if (defined(invoker.source_filter)) {
_bloat_script_args += [
"--source-filter",
invoker.source_filter,
]
}
_doc_rst_output = "$target_gen_dir/${target_name}"
if (host_os == "win") {
# Bloaty is not yet packaged for Windows systems; display a message
# indicating this.
not_needed("*")
not_needed(invoker, "*")
pw_python_action(target_name) {
metadata = {
pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir)
}
script = "$dir_pw_bloat/py/pw_bloat/no_bloaty.py"
python_deps = [ "$dir_pw_bloat/py" ]
args = [ rebase_path(_doc_rst_output, root_build_dir) ]
outputs = [ _doc_rst_output ]
}
group(target_name + "_UNUSED_DEPS") {
deps = _all_target_dependencies
}
} else {
# Create an action which runs the size report script on the provided
# targets.
pw_python_action(target_name) {
metadata = {
pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir)
}
script = "$dir_pw_bloat/py/pw_bloat/bloat.py"
python_deps = [ "$dir_pw_bloat/py" ]
inputs = _bloaty_configs
outputs = [
"${_doc_rst_output}.txt",
_doc_rst_output,
]
deps = _all_target_dependencies
args = _bloat_script_args + _binary_paths
# Print size reports to stdout when they are generated, if requested.
capture_output = !pw_bloat_SHOW_SIZE_REPORTS
}
}
} else {
not_needed(invoker, "*")
group(target_name) {
}
}
}
# Creates a report card comparing the sizes of the same binary compiled with
# different toolchains. The toolchains to use are listed in the build variable
# pw_bloat_TOOLCHAINS.
#
# Args:
# base_executable: Scope containing a list of variables defining an executable
# target for the size report base.
# diff_executable: Scope containing a list of variables defining an executable
# target for the size report comparison.
#
# Outputs:
# $target_gen_dir/$target_name.txt
# $target_gen_dir/$target_name.rst
#
# Example:
#
# pw_toolchain_size_report("my_size_report") {
# base_executable = {
# sources = [ "base.cc" ]
# }
#
# diff_executable = {
# sources = [ "base_with_libfoo.cc" ]
# deps = [ ":libfoo" ]
# }
# }
#
template("pw_toolchain_size_report") {
assert(defined(invoker.base_executable),
"pw_toolchain_size_report requires a base_executable")
assert(defined(invoker.diff_executable),
"pw_toolchain_size_report requires a diff_executable")
_size_report_binaries = []
# Multiple build targets are created for each toolchain, which all need unique
# target names, so throw a counter in there.
i = 0
# Create a base and diff executable for each toolchain, adding the toolchain's
# linker script to the link flags for the executable, and add them all to a
# list of binaries for the pw_size_report template.
foreach(_toolchain, pw_bloat_TOOLCHAINS) {
_prefix = "_${target_name}_${i}_pw_size"
# Create a config which adds the toolchain's linker script as a linker flag
# if the toolchain provides one.
_linker_script_target_name = "${_prefix}_linker_script"
config(_linker_script_target_name) {
if (defined(_toolchain.linker_script)) {
ldflags =
[ "-T" + rebase_path(_toolchain.linker_script, root_build_dir) ]
inputs = [ _toolchain.linker_script ]
} else {
ldflags = []
}
}
# Create a group which forces the linker script config its dependents.
_linker_group_target_name = "${_prefix}_linker_group"
group(_linker_group_target_name) {
public_configs = [ ":$_linker_script_target_name" ]
}
# Define the size report base executable with the toolchain's linker script.
_base_target_name = "${_prefix}_base"
executable(_base_target_name) {
forward_variables_from(invoker.base_executable, "*")
if (!defined(deps)) {
deps = []
}
deps += [ ":$_linker_group_target_name" ]
}
# Define the size report diff executable with the toolchain's linker script.
_diff_target_name = "${_prefix}_diff"
executable(_diff_target_name) {
forward_variables_from(invoker.diff_executable, "*")
if (!defined(deps)) {
deps = []
}
deps += [ ":$_linker_group_target_name" ]
}
# Force compilation with the toolchain.
_base_label = get_label_info(":$_base_target_name", "label_no_toolchain")
_base_with_toolchain = "$_base_label(${_toolchain.target})"
_diff_label = get_label_info(":$_diff_target_name", "label_no_toolchain")
_diff_with_toolchain = "$_diff_label(${_toolchain.target})"
# Append a pw_size_report binary scope to the list comparing the toolchain's
# diff and base executables.
_size_report_binaries += [
{
base = _base_with_toolchain
target = _diff_with_toolchain
label = _toolchain.name
if (defined(_toolchain.bloaty_config)) {
bloaty_config = _toolchain.bloaty_config
}
},
]
i += 1
}
# TODO(frolv): Have a way of indicating that a toolchain should build docs.
if (current_toolchain == default_toolchain && _size_report_binaries != []) {
# Create the size report which runs on the binaries.
pw_size_report(target_name) {
forward_variables_from(invoker, [ "title" ])
binaries = _size_report_binaries
}
} else {
# If no toolchains are listed in pw_bloat_TOOLCHAINS, prevent GN from
# complaining about unused variables and run a script that outputs a ReST
# warning to the size report file.
not_needed("*")
not_needed(invoker, "*")
_doc_rst_output = "$target_gen_dir/$target_name"
pw_python_action(target_name) {
metadata = {
pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir)
}
script = "$dir_pw_bloat/py/pw_bloat/no_toolchains.py"
python_deps = [ "$dir_pw_bloat/py" ]
args = [ rebase_path(_doc_rst_output, root_build_dir) ]
outputs = [ _doc_rst_output ]
}
}
}
# A base_executable for the pw_toolchain_size_report template which contains a
# main() function that loads the bloat_this_binary library and does nothing
# else.
pw_bloat_empty_base = {
deps = [
"$dir_pw_bloat:base_main",
"$dir_pw_bloat:bloat_this_binary",
]
}