|  | # 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. | 
|  |  | 
|  | # gn-format disable | 
|  | import("//build_overrides/pigweed.gni") | 
|  |  | 
|  | import("$dir_pw_build/python_script.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 = [] | 
|  | } | 
|  |  | 
|  | # 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 ] | 
|  |  | 
|  | _target_dir = get_label_info(binary.target, "target_out_dir") | 
|  | _target_name = get_label_info(binary.target, "name") | 
|  | _binary_path = get_path_info(_target_dir, "abspath") + ":$_target_name" | 
|  |  | 
|  | # 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 += [ get_path_info(binary.bloaty_config, "abspath") ] | 
|  | } else { | 
|  | _bloaty_configs += [ get_path_info(pw_bloat_BLOATY_CONFIG, "abspath") ] | 
|  | } | 
|  |  | 
|  | _base_dir = get_label_info(_binary_base, "target_out_dir") | 
|  | _base_name = get_label_info(_binary_base, "name") | 
|  | _binary_path += ";" + get_path_info(_base_dir, "abspath") + ":$_base_name" | 
|  |  | 
|  | _binary_paths += [ _binary_path ] | 
|  | _binary_labels += [ binary.label ] | 
|  | } | 
|  |  | 
|  | _bloat_script_args = [ | 
|  | "--bloaty-config", | 
|  | string_join(";", _bloaty_configs), | 
|  | "--out-dir", | 
|  | target_gen_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}" | 
|  |  | 
|  | # TODO(frolv): Size reports are temporarily disabled pending the toolchain | 
|  | # refactor. | 
|  | if (true || host_os == "win") { | 
|  | # Bloaty is not yet packaged for Windows systems; display a message | 
|  | # indicating this. | 
|  | not_needed("*") | 
|  | not_needed(invoker, "*") | 
|  |  | 
|  | pw_python_script(target_name) { | 
|  | metadata = { | 
|  | pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir) | 
|  | } | 
|  | script = "$dir_pw_bloat/py/no_bloaty.py" | 
|  | args = [ _doc_rst_output ] | 
|  | 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_script(target_name) { | 
|  | metadata = { | 
|  | pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir) | 
|  | } | 
|  | script = "$dir_pw_bloat/py/bloat.py" | 
|  | inputs = [ | 
|  | "$dir_pw_bloat/py/binary_diff.py", | 
|  | "$dir_pw_bloat/py/bloat_output.py", | 
|  | ] + _bloaty_configs | 
|  | outputs = [ | 
|  | "$target_gen_dir/${target_name}.txt", | 
|  | _doc_rst_output, | 
|  | ] | 
|  | deps = _all_target_dependencies | 
|  | args = _bloat_script_args + _binary_paths | 
|  |  | 
|  | # Print size reports to stdout when they are generated. | 
|  | capture_output = false | 
|  | } | 
|  | } | 
|  | } 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) ] | 
|  | 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_script(target_name) { | 
|  | metadata = { | 
|  | pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir) | 
|  | } | 
|  | script = "$dir_pw_bloat/py/no_toolchains.py" | 
|  | args = [ _doc_rst_output ] | 
|  | 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", | 
|  | ] | 
|  | } |