1# Copyright 2019 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14 15import("//build_overrides/pigweed.gni") 16 17import("$dir_pw_build/python_action.gni") 18 19declare_args() { 20 # Path to the Bloaty configuration file that defines the memory layout and 21 # capacities for the target binaries. 22 pw_bloat_BLOATY_CONFIG = "" 23 24 # List of toolchains to use in pw_toolchain_size_report templates. 25 # 26 # Each entry is a scope containing the following variables: 27 # 28 # name: Human-readable toolchain name. 29 # target: GN target that defines the toolchain. 30 # linker_script: Optional path to a linker script file to build for the 31 # toolchain's target. 32 # bloaty_config: Optional Bloaty confirugation file defining the memory 33 # layout of the binaries as specified in the linker script. 34 # 35 # If this list is empty, pw_toolchain_size_report targets become no-ops. 36 pw_bloat_TOOLCHAINS = [] 37} 38 39# Creates a target which runs a size report diff on a set of executables. 40# 41# Args: 42# base: The default base executable target to run the diff against. May be 43# omitted if all binaries provide their own base. 44# binaries: List of executables to compare in the diff. 45# Each binary in the list is a scope containing up to three variables: 46# label: Descriptive name for the executable. Required. 47# target: Build target for the executable. Required. 48# base: Optional base diff target. Overrides global base argument. 49# source_filter: Optional regex to filter data source names in Bloaty. 50# title: Optional title string to display with the size report. 51# full_report: Optional boolean flag indicating whether to produce a full 52# symbol size breakdown or a summary. 53# 54# Example: 55# pw_size_report("foo_bloat") { 56# base = ":foo_base" 57# binaries = [ 58# { 59# target = ":foo_static" 60# label = "Static" 61# }, 62# { 63# target = ":foo_dynamic" 64# label = "Dynamic" 65# }, 66# ] 67# title = "static vs. dynamic foo" 68# } 69# 70template("pw_size_report") { 71 if (pw_bloat_BLOATY_CONFIG != "") { 72 if (defined(invoker.base)) { 73 _global_base = invoker.base 74 _all_target_dependencies = [ _global_base ] 75 } else { 76 _all_target_dependencies = [] 77 } 78 79 if (defined(invoker.title)) { 80 _title = invoker.title 81 } else { 82 _title = target_name 83 } 84 85 # This template creates an action which invokes a Python script to run a 86 # size report on each of the provided targets. Each of the targets is listed 87 # as a dependency of the action so that the report gets updated when 88 # anything is changed. Most of the code below builds the command-line 89 # arguments to pass each of the targets into the script. 90 91 _binary_paths = [] 92 _binary_labels = [] 93 _bloaty_configs = [] 94 95 # Process each of the binaries, resolving their full output paths and 96 # building them into a list of command-line arguments to the bloat script. 97 foreach(binary, invoker.binaries) { 98 assert(defined(binary.label) && defined(binary.target), 99 "Size report binaries must define 'label' and 'target' variables") 100 _all_target_dependencies += [ binary.target ] 101 102 _binary_path = "<TARGET_FILE(${binary.target})>" 103 104 # If the binary defines its own base, use that instead of the global base. 105 if (defined(binary.base)) { 106 _binary_base = binary.base 107 _all_target_dependencies += [ _binary_base ] 108 } else if (defined(_global_base)) { 109 _binary_base = _global_base 110 } else { 111 assert(false, "pw_size_report requires a 'base' file") 112 } 113 114 # Allow each binary to override the global bloaty config. 115 if (defined(binary.bloaty_config)) { 116 _bloaty_configs += [ rebase_path(binary.bloaty_config) ] 117 } else { 118 _bloaty_configs += [ rebase_path(pw_bloat_BLOATY_CONFIG) ] 119 } 120 121 _binary_path += ";" + "<TARGET_FILE($_binary_base)>" 122 123 _binary_paths += [ _binary_path ] 124 _binary_labels += [ binary.label ] 125 } 126 127 _bloat_script_args = [ 128 "--bloaty-config", 129 string_join(";", _bloaty_configs), 130 "--out-dir", 131 rebase_path(target_gen_dir), 132 "--target", 133 target_name, 134 "--title", 135 _title, 136 "--labels", 137 string_join(";", _binary_labels), 138 ] 139 140 if (defined(invoker.full_report) && invoker.full_report) { 141 _bloat_script_args += [ "--full" ] 142 } 143 144 if (defined(invoker.source_filter)) { 145 _bloat_script_args += [ 146 "--source-filter", 147 invoker.source_filter, 148 ] 149 } 150 151 _doc_rst_output = "$target_gen_dir/${target_name}" 152 153 if (host_os == "win") { 154 # Bloaty is not yet packaged for Windows systems; display a message 155 # indicating this. 156 not_needed("*") 157 not_needed(invoker, "*") 158 159 pw_python_action(target_name) { 160 metadata = { 161 pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir) 162 } 163 script = "$dir_pw_bloat/py/pw_bloat/no_bloaty.py" 164 python_deps = [ "$dir_pw_bloat/py" ] 165 args = [ rebase_path(_doc_rst_output) ] 166 outputs = [ _doc_rst_output ] 167 } 168 169 group(target_name + "_UNUSED_DEPS") { 170 deps = _all_target_dependencies 171 } 172 } else { 173 # Create an action which runs the size report script on the provided 174 # targets. 175 pw_python_action(target_name) { 176 metadata = { 177 pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir) 178 } 179 script = "$dir_pw_bloat/py/pw_bloat/bloat.py" 180 python_deps = [ "$dir_pw_bloat/py" ] 181 inputs = _bloaty_configs 182 outputs = [ 183 "$target_gen_dir/${target_name}.txt", 184 _doc_rst_output, 185 ] 186 deps = _all_target_dependencies 187 args = _bloat_script_args + _binary_paths 188 189 # Print size reports to stdout when they are generated. 190 capture_output = false 191 } 192 } 193 } else { 194 not_needed(invoker, "*") 195 group(target_name) { 196 } 197 } 198} 199 200# Creates a report card comparing the sizes of the same binary compiled with 201# different toolchains. The toolchains to use are listed in the build variable 202# pw_bloat_TOOLCHAINS. 203# 204# Args: 205# base_executable: Scope containing a list of variables defining an executable 206# target for the size report base. 207# diff_executable: Scope containing a list of variables defining an executable 208# target for the size report comparison. 209# 210# Outputs: 211# $target_gen_dir/$target_name.txt 212# $target_gen_dir/$target_name.rst 213# 214# Example: 215# 216# pw_toolchain_size_report("my_size_report") { 217# base_executable = { 218# sources = [ "base.cc" ] 219# } 220# 221# diff_executable = { 222# sources = [ "base_with_libfoo.cc" ] 223# deps = [ ":libfoo" ] 224# } 225# } 226# 227template("pw_toolchain_size_report") { 228 assert(defined(invoker.base_executable), 229 "pw_toolchain_size_report requires a base_executable") 230 assert(defined(invoker.diff_executable), 231 "pw_toolchain_size_report requires a diff_executable") 232 233 _size_report_binaries = [] 234 235 # Multiple build targets are created for each toolchain, which all need unique 236 # target names, so throw a counter in there. 237 i = 0 238 239 # Create a base and diff executable for each toolchain, adding the toolchain's 240 # linker script to the link flags for the executable, and add them all to a 241 # list of binaries for the pw_size_report template. 242 foreach(_toolchain, pw_bloat_TOOLCHAINS) { 243 _prefix = "_${target_name}_${i}_pw_size" 244 245 # Create a config which adds the toolchain's linker script as a linker flag 246 # if the toolchain provides one. 247 _linker_script_target_name = "${_prefix}_linker_script" 248 config(_linker_script_target_name) { 249 if (defined(_toolchain.linker_script)) { 250 ldflags = [ "-T" + rebase_path(_toolchain.linker_script) ] 251 inputs = [ _toolchain.linker_script ] 252 } else { 253 ldflags = [] 254 } 255 } 256 257 # Create a group which forces the linker script config its dependents. 258 _linker_group_target_name = "${_prefix}_linker_group" 259 group(_linker_group_target_name) { 260 public_configs = [ ":$_linker_script_target_name" ] 261 } 262 263 # Define the size report base executable with the toolchain's linker script. 264 _base_target_name = "${_prefix}_base" 265 executable(_base_target_name) { 266 forward_variables_from(invoker.base_executable, "*") 267 if (!defined(deps)) { 268 deps = [] 269 } 270 deps += [ ":$_linker_group_target_name" ] 271 } 272 273 # Define the size report diff executable with the toolchain's linker script. 274 _diff_target_name = "${_prefix}_diff" 275 executable(_diff_target_name) { 276 forward_variables_from(invoker.diff_executable, "*") 277 if (!defined(deps)) { 278 deps = [] 279 } 280 deps += [ ":$_linker_group_target_name" ] 281 } 282 283 # Force compilation with the toolchain. 284 _base_label = get_label_info(":$_base_target_name", "label_no_toolchain") 285 _base_with_toolchain = "$_base_label(${_toolchain.target})" 286 _diff_label = get_label_info(":$_diff_target_name", "label_no_toolchain") 287 _diff_with_toolchain = "$_diff_label(${_toolchain.target})" 288 289 # Append a pw_size_report binary scope to the list comparing the toolchain's 290 # diff and base executables. 291 _size_report_binaries += [ 292 { 293 base = _base_with_toolchain 294 target = _diff_with_toolchain 295 label = _toolchain.name 296 297 if (defined(_toolchain.bloaty_config)) { 298 bloaty_config = _toolchain.bloaty_config 299 } 300 }, 301 ] 302 303 i += 1 304 } 305 306 # TODO(frolv): Have a way of indicating that a toolchain should build docs. 307 if (current_toolchain == default_toolchain && _size_report_binaries != []) { 308 # Create the size report which runs on the binaries. 309 pw_size_report(target_name) { 310 forward_variables_from(invoker, [ "title" ]) 311 binaries = _size_report_binaries 312 } 313 } else { 314 # If no toolchains are listed in pw_bloat_TOOLCHAINS, prevent GN from 315 # complaining about unused variables and run a script that outputs a ReST 316 # warning to the size report file. 317 not_needed("*") 318 not_needed(invoker, "*") 319 320 _doc_rst_output = "$target_gen_dir/$target_name" 321 pw_python_action(target_name) { 322 metadata = { 323 pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir) 324 } 325 script = "$dir_pw_bloat/py/pw_bloat/no_toolchains.py" 326 python_deps = [ "$dir_pw_bloat/py" ] 327 args = [ rebase_path(_doc_rst_output) ] 328 outputs = [ _doc_rst_output ] 329 } 330 } 331} 332 333# A base_executable for the pw_toolchain_size_report template which contains a 334# main() function that loads the bloat_this_binary library and does nothing 335# else. 336pw_bloat_empty_base = { 337 deps = [ 338 "$dir_pw_bloat:base_main", 339 "$dir_pw_bloat:bloat_this_binary", 340 ] 341} 342