• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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