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