blob: 661df320222dba7de996fa2b529b32ed5b14164b [file] [log] [blame]
Alexei Frolove2016762019-11-14 13:49:52 -08001# 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
Armando Montanezfb3d3fb2020-06-09 18:12:12 -070015import("//build_overrides/pigweed.gni")
16
Wyatt Hepler51ded742020-10-19 14:45:27 -070017import("$dir_pw_build/python_action.gni")
Wyatt Heplerd49f8fe2020-10-15 10:13:47 -070018
Alexei Frolov4c0428a2020-06-10 10:46:04 -070019declare_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 = []
Wyatt Hepler6230fa12021-05-11 18:41:51 -070037
38 # Controls whether to display size reports in the build output.
39 pw_bloat_SHOW_SIZE_REPORTS = false
Alexei Frolov4c0428a2020-06-10 10:46:04 -070040}
Alexei Frolove2016762019-11-14 13:49:52 -080041
42# Creates a target which runs a size report diff on a set of executables.
43#
44# Args:
Wyatt Hepler32d86bb2019-11-18 22:54:53 -080045# base: The default base executable target to run the diff against. May be
46# omitted if all binaries provide their own base.
Alexei Frolove2016762019-11-14 13:49:52 -080047# 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:
Alexei Frolov09447842019-11-15 15:09:05 -080058# pw_size_report("foo_bloat") {
Alexei Frolove2016762019-11-14 13:49:52 -080059# 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#
Alexei Frolov09447842019-11-15 15:09:05 -080073template("pw_size_report") {
Alexei Frolove4970e72020-06-11 13:55:29 -070074 if (pw_bloat_BLOATY_CONFIG != "") {
75 if (defined(invoker.base)) {
76 _global_base = invoker.base
77 _all_target_dependencies = [ _global_base ]
Wyatt Hepler32d86bb2019-11-18 22:54:53 -080078 } else {
Alexei Frolove4970e72020-06-11 13:55:29 -070079 _all_target_dependencies = []
Alexei Frolove2016762019-11-14 13:49:52 -080080 }
81
Alexei Frolove4970e72020-06-11 13:55:29 -070082 if (defined(invoker.title)) {
83 _title = invoker.title
Alexei Frolov3fde6b12019-12-18 16:13:38 -080084 } else {
Alexei Frolove4970e72020-06-11 13:55:29 -070085 _title = target_name
Alexei Frolov3fde6b12019-12-18 16:13:38 -080086 }
87
Alexei Frolove4970e72020-06-11 13:55:29 -070088 # 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.
Wyatt Hepler32d86bb2019-11-18 22:54:53 -080093
Alexei Frolove4970e72020-06-11 13:55:29 -070094 _binary_paths = []
95 _binary_labels = []
96 _bloaty_configs = []
Alexei Frolove2016762019-11-14 13:49:52 -080097
Alexei Frolove4970e72020-06-11 13:55:29 -070098 # 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 ]
Alexei Frolove2016762019-11-14 13:49:52 -0800104
Wyatt Hepler8224a642020-07-29 08:55:56 -0700105 _binary_path = "<TARGET_FILE(${binary.target})>"
Alexei Frolove2016762019-11-14 13:49:52 -0800106
Alexei Frolove4970e72020-06-11 13:55:29 -0700107 # 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)) {
Michael Spang1e2142c2021-06-11 15:31:08 -0400119 _bloaty_configs += [ binary.bloaty_config ]
Alexei Frolove4970e72020-06-11 13:55:29 -0700120 } else {
Michael Spang1e2142c2021-06-11 15:31:08 -0400121 _bloaty_configs += [ pw_bloat_BLOATY_CONFIG ]
Alexei Frolove4970e72020-06-11 13:55:29 -0700122 }
123
Wyatt Hepler8224a642020-07-29 08:55:56 -0700124 _binary_path += ";" + "<TARGET_FILE($_binary_base)>"
Alexei Frolove4970e72020-06-11 13:55:29 -0700125
126 _binary_paths += [ _binary_path ]
127 _binary_labels += [ binary.label ]
128 }
129
130 _bloat_script_args = [
131 "--bloaty-config",
Michael Spang1e2142c2021-06-11 15:31:08 -0400132 string_join(";", rebase_path(_bloaty_configs, root_build_dir)),
Alexei Frolove4970e72020-06-11 13:55:29 -0700133 "--out-dir",
Michael Spangc8b93902021-05-30 15:53:56 -0400134 rebase_path(target_gen_dir, root_build_dir),
Alexei Frolove4970e72020-06-11 13:55:29 -0700135 "--target",
136 target_name,
137 "--title",
138 _title,
139 "--labels",
140 string_join(";", _binary_labels),
Alexei Frolove2016762019-11-14 13:49:52 -0800141 ]
Alexei Frolove2016762019-11-14 13:49:52 -0800142
Alexei Frolove4970e72020-06-11 13:55:29 -0700143 if (defined(invoker.full_report) && invoker.full_report) {
144 _bloat_script_args += [ "--full" ]
Alexei Frolov3adcd672020-03-19 09:45:33 -0700145 }
Alexei Frolov844ff0f2020-05-06 12:15:29 -0700146
Alexei Frolove4970e72020-06-11 13:55:29 -0700147 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
Alexei Frolovd27b6742020-08-19 10:54:56 -0700156 if (host_os == "win") {
Alexei Frolove4970e72020-06-11 13:55:29 -0700157 # Bloaty is not yet packaged for Windows systems; display a message
158 # indicating this.
159 not_needed("*")
160 not_needed(invoker, "*")
161
Wyatt Heplerc8e05a42020-10-19 14:49:39 -0700162 pw_python_action(target_name) {
Alexei Frolove4970e72020-06-11 13:55:29 -0700163 metadata = {
164 pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir)
165 }
Wyatt Hepler96992c72020-10-23 08:05:33 -0700166 script = "$dir_pw_bloat/py/pw_bloat/no_bloaty.py"
Wyatt Hepler438caa02021-01-15 17:13:11 -0800167 python_deps = [ "$dir_pw_bloat/py" ]
Michael Spangc8b93902021-05-30 15:53:56 -0400168 args = [ rebase_path(_doc_rst_output, root_build_dir) ]
Alexei Frolove4970e72020-06-11 13:55:29 -0700169 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.
Wyatt Heplerc8e05a42020-10-19 14:49:39 -0700178 pw_python_action(target_name) {
Alexei Frolove4970e72020-06-11 13:55:29 -0700179 metadata = {
180 pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir)
181 }
Wyatt Hepler96992c72020-10-23 08:05:33 -0700182 script = "$dir_pw_bloat/py/pw_bloat/bloat.py"
Wyatt Hepler438caa02021-01-15 17:13:11 -0800183 python_deps = [ "$dir_pw_bloat/py" ]
Wyatt Hepler96992c72020-10-23 08:05:33 -0700184 inputs = _bloaty_configs
Alexei Frolove4970e72020-06-11 13:55:29 -0700185 outputs = [
Dennis Kormalev2462a832022-03-24 19:22:35 +0000186 "${_doc_rst_output}.txt",
Alexei Frolove4970e72020-06-11 13:55:29 -0700187 _doc_rst_output,
188 ]
Wyatt Hepler620b3e02021-01-22 09:14:45 -0800189 deps = _all_target_dependencies
Alexei Frolove4970e72020-06-11 13:55:29 -0700190 args = _bloat_script_args + _binary_paths
191
Wyatt Hepler6230fa12021-05-11 18:41:51 -0700192 # Print size reports to stdout when they are generated, if requested.
193 capture_output = !pw_bloat_SHOW_SIZE_REPORTS
Alexei Frolove4970e72020-06-11 13:55:29 -0700194 }
Alexei Frolov844ff0f2020-05-06 12:15:29 -0700195 }
Alexei Frolov3adcd672020-03-19 09:45:33 -0700196 } else {
Alexei Frolove4970e72020-06-11 13:55:29 -0700197 not_needed(invoker, "*")
198 group(target_name) {
Alexei Frolov3adcd672020-03-19 09:45:33 -0700199 }
Alexei Frolove2016762019-11-14 13:49:52 -0800200 }
201}
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800202
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
Alexei Frolov4c0428a2020-06-10 10:46:04 -0700205# pw_bloat_TOOLCHAINS.
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800206#
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.
Alexei Frolov4c0428a2020-06-10 10:46:04 -0700245 foreach(_toolchain, pw_bloat_TOOLCHAINS) {
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800246 _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)) {
Michael Spangc8b93902021-05-30 15:53:56 -0400253 ldflags =
254 [ "-T" + rebase_path(_toolchain.linker_script, root_build_dir) ]
Alexei Frolov47373492020-03-03 16:37:11 -0800255 inputs = [ _toolchain.linker_script ]
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800256 } 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
Alexei Frolov844ff0f2020-05-06 12:15:29 -0700310 # TODO(frolv): Have a way of indicating that a toolchain should build docs.
311 if (current_toolchain == default_toolchain && _size_report_binaries != []) {
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800312 # 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 {
Alexei Frolov4c0428a2020-06-10 10:46:04 -0700318 # If no toolchains are listed in pw_bloat_TOOLCHAINS, prevent GN from
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800319 # 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
Alexei Frolov725b85b2020-03-19 13:37:10 -0700324 _doc_rst_output = "$target_gen_dir/$target_name"
Wyatt Heplerc8e05a42020-10-19 14:49:39 -0700325 pw_python_action(target_name) {
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800326 metadata = {
327 pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir)
328 }
Wyatt Hepler96992c72020-10-23 08:05:33 -0700329 script = "$dir_pw_bloat/py/pw_bloat/no_toolchains.py"
Wyatt Hepler438caa02021-01-15 17:13:11 -0800330 python_deps = [ "$dir_pw_bloat/py" ]
Michael Spangc8b93902021-05-30 15:53:56 -0400331 args = [ rebase_path(_doc_rst_output, root_build_dir) ]
Rob Mohra0ba54f2020-02-27 11:43:49 -0800332 outputs = [ _doc_rst_output ]
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800333 }
334 }
335}
Alexei Frolovf8259f62020-01-09 12:35:36 -0800336
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 = {
Wyatt Heplerc5e511e2020-06-12 16:56:22 -0700341 deps = [
342 "$dir_pw_bloat:base_main",
343 "$dir_pw_bloat:bloat_this_binary",
344 ]
Alexei Frolovf8259f62020-01-09 12:35:36 -0800345}