blob: 61e83d5ab9d055244b346337c9e2b696d9d08aa8 [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
15import("$dir_pw_build/python_script.gni")
16
17# Creates a target which runs a size report diff on a set of executables.
18#
19# Args:
Wyatt Hepler32d86bb2019-11-18 22:54:53 -080020# base: The default base executable target to run the diff against. May be
21# omitted if all binaries provide their own base.
Alexei Frolove2016762019-11-14 13:49:52 -080022# binaries: List of executables to compare in the diff.
23# Each binary in the list is a scope containing up to three variables:
24# label: Descriptive name for the executable. Required.
25# target: Build target for the executable. Required.
26# base: Optional base diff target. Overrides global base argument.
27# source_filter: Optional regex to filter data source names in Bloaty.
28# title: Optional title string to display with the size report.
29# full_report: Optional boolean flag indicating whether to produce a full
30# symbol size breakdown or a summary.
31#
32# Example:
Alexei Frolov09447842019-11-15 15:09:05 -080033# pw_size_report("foo_bloat") {
Alexei Frolove2016762019-11-14 13:49:52 -080034# base = ":foo_base"
35# binaries = [
36# {
37# target = ":foo_static"
38# label = "Static"
39# },
40# {
41# target = ":foo_dynamic"
42# label = "Dynamic"
43# },
44# ]
45# title = "static vs. dynamic foo"
46# }
47#
Alexei Frolov09447842019-11-15 15:09:05 -080048template("pw_size_report") {
Wyatt Hepler32d86bb2019-11-18 22:54:53 -080049 if (defined(invoker.base)) {
50 _global_base = invoker.base
51 _all_target_dependencies = [ _global_base ]
52 } else {
53 _all_target_dependencies = []
54 }
55
Alexei Frolove2016762019-11-14 13:49:52 -080056 if (defined(invoker.title)) {
57 _title = invoker.title
58 } else {
59 _title = target_name
60 }
61
62 # This template creates an action which invokes a Python script to run a size
63 # report on each of the provided targets. Each of the targets is listed as a
64 # dependency of the action so that the report gets updated when anything is
65 # changed. Most of the code below builds the command-line arguments to pass
66 # each of the targets into the script.
67
Alexei Frolove2016762019-11-14 13:49:52 -080068 _binary_paths = []
69 _binary_labels = []
Alexei Frolov3fde6b12019-12-18 16:13:38 -080070 _bloaty_configs = []
Alexei Frolove2016762019-11-14 13:49:52 -080071
Alexei Frolove2016762019-11-14 13:49:52 -080072 # Process each of the binaries, resolving their full output paths and building
73 # them into a list of command-line arguments to the bloat script.
74 foreach(binary, invoker.binaries) {
75 assert(defined(binary.label) && defined(binary.target),
76 "Size report binaries must define 'label' and 'target' variables")
77
78 _all_target_dependencies += [ binary.target ]
79
80 _target_dir = get_label_info(binary.target, "target_out_dir")
81 _target_name = get_label_info(binary.target, "name")
82 _binary_path = get_path_info(_target_dir, "abspath") + ":$_target_name"
83
Wyatt Hepler32d86bb2019-11-18 22:54:53 -080084 # If the binary defines its own base, use that instead of the global base.
Alexei Frolove2016762019-11-14 13:49:52 -080085 if (defined(binary.base)) {
Wyatt Hepler32d86bb2019-11-18 22:54:53 -080086 _binary_base = binary.base
87 _all_target_dependencies += [ _binary_base ]
88 } else if (defined(_global_base)) {
89 _binary_base = _global_base
90 } else {
91 assert(false, "pw_size_report requires a 'base' file")
Alexei Frolove2016762019-11-14 13:49:52 -080092 }
93
Alexei Frolov3fde6b12019-12-18 16:13:38 -080094 # Allow each binary to override the global bloaty config.
95 if (defined(binary.bloaty_config)) {
96 _bloaty_configs += [ get_path_info(binary.bloaty_config, "abspath") ]
97 } else {
98 _bloaty_configs +=
99 [ get_path_info(pw_executable_config.bloaty_config_file, "abspath") ]
100 }
101
Wyatt Hepler32d86bb2019-11-18 22:54:53 -0800102 _base_dir = get_label_info(_binary_base, "target_out_dir")
103 _base_name = get_label_info(_binary_base, "name")
104 _binary_path += ";" + get_path_info(_base_dir, "abspath") + ":$_base_name"
105
Alexei Frolove2016762019-11-14 13:49:52 -0800106 _binary_paths += [ _binary_path ]
107 _binary_labels += [ binary.label ]
108 }
109
110 _bloat_script_args = [
111 "--bloaty-config",
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800112 string_join(";", _bloaty_configs),
Alexei Frolove2016762019-11-14 13:49:52 -0800113 "--out-dir",
114 target_gen_dir,
115 "--target",
116 target_name,
117 "--title",
118 _title,
Alexei Frolove2016762019-11-14 13:49:52 -0800119 "--labels",
120 string_join(";", _binary_labels),
121 ]
122
123 if (defined(invoker.full_report) && invoker.full_report) {
124 _bloat_script_args += [ "--full" ]
125 }
126
127 if (defined(invoker.source_filter)) {
128 _bloat_script_args += [
129 "--source-filter",
130 invoker.source_filter,
131 ]
132 }
133
Alexei Frolov4c035b02019-11-14 16:36:15 -0800134 _doc_rst_output = "$target_gen_dir/${target_name}.rst"
135
Alexei Frolove2016762019-11-14 13:49:52 -0800136 # Create an action which runs the size report script on the provided targets.
137 pw_python_script(target_name) {
Alexei Frolov4c035b02019-11-14 16:36:15 -0800138 metadata = {
139 pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir)
140 }
Alexei Frolove2016762019-11-14 13:49:52 -0800141 script = "$dir_pw_bloat/py/bloat.py"
142 inputs = [
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800143 "$dir_pw_bloat/py/binary_diff.py",
144 "$dir_pw_bloat/py/bloat_output.py",
145 ] + _bloaty_configs
Alexei Frolove2016762019-11-14 13:49:52 -0800146 outputs = [
147 "$target_gen_dir/${target_name}.txt",
Alexei Frolov4c035b02019-11-14 16:36:15 -0800148 _doc_rst_output,
Alexei Frolove2016762019-11-14 13:49:52 -0800149 ]
150 deps = _all_target_dependencies
151 args = _bloat_script_args + _binary_paths
152 }
153}
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800154
155# Creates a report card comparing the sizes of the same binary compiled with
156# different toolchains. The toolchains to use are listed in the build variable
157# pw_size_report_toolchains.
158#
159# Args:
160# base_executable: Scope containing a list of variables defining an executable
161# target for the size report base.
162# diff_executable: Scope containing a list of variables defining an executable
163# target for the size report comparison.
164#
165# Outputs:
166# $target_gen_dir/$target_name.txt
167# $target_gen_dir/$target_name.rst
168#
169# Example:
170#
171# pw_toolchain_size_report("my_size_report") {
172# base_executable = {
173# sources = [ "base.cc" ]
174# }
175#
176# diff_executable = {
177# sources = [ "base_with_libfoo.cc" ]
178# deps = [ ":libfoo" ]
179# }
180# }
181#
182template("pw_toolchain_size_report") {
183 assert(defined(invoker.base_executable),
184 "pw_toolchain_size_report requires a base_executable")
185 assert(defined(invoker.diff_executable),
186 "pw_toolchain_size_report requires a diff_executable")
187
188 _size_report_binaries = []
189
190 # Multiple build targets are created for each toolchain, which all need unique
191 # target names, so throw a counter in there.
192 i = 0
193
194 # Create a base and diff executable for each toolchain, adding the toolchain's
195 # linker script to the link flags for the executable, and add them all to a
196 # list of binaries for the pw_size_report template.
197 foreach(_toolchain, pw_size_report_toolchains) {
198 _prefix = "_${target_name}_${i}_pw_size"
199
200 # Create a config which adds the toolchain's linker script as a linker flag
201 # if the toolchain provides one.
202 _linker_script_target_name = "${_prefix}_linker_script"
203 config(_linker_script_target_name) {
204 if (defined(_toolchain.linker_script)) {
205 ldflags = [ "-T" + rebase_path(_toolchain.linker_script) ]
206 } else {
207 ldflags = []
208 }
209 }
210
211 # Create a group which forces the linker script config its dependents.
212 _linker_group_target_name = "${_prefix}_linker_group"
213 group(_linker_group_target_name) {
214 public_configs = [ ":$_linker_script_target_name" ]
215 }
216
217 # Define the size report base executable with the toolchain's linker script.
218 _base_target_name = "${_prefix}_base"
219 executable(_base_target_name) {
220 forward_variables_from(invoker.base_executable, "*")
221 if (!defined(deps)) {
222 deps = []
223 }
224 deps += [ ":$_linker_group_target_name" ]
225 }
226
227 # Define the size report diff executable with the toolchain's linker script.
228 _diff_target_name = "${_prefix}_diff"
229 executable(_diff_target_name) {
230 forward_variables_from(invoker.diff_executable, "*")
231 if (!defined(deps)) {
232 deps = []
233 }
234 deps += [ ":$_linker_group_target_name" ]
235 }
236
237 # Force compilation with the toolchain.
238 _base_label = get_label_info(":$_base_target_name", "label_no_toolchain")
239 _base_with_toolchain = "$_base_label(${_toolchain.target})"
240 _diff_label = get_label_info(":$_diff_target_name", "label_no_toolchain")
241 _diff_with_toolchain = "$_diff_label(${_toolchain.target})"
242
243 # Append a pw_size_report binary scope to the list comparing the toolchain's
244 # diff and base executables.
245 _size_report_binaries += [
246 {
247 base = _base_with_toolchain
248 target = _diff_with_toolchain
249 label = _toolchain.name
250
251 if (defined(_toolchain.bloaty_config)) {
252 bloaty_config = _toolchain.bloaty_config
253 }
254 },
255 ]
256
257 i += 1
258 }
259
260 if (_size_report_binaries != []) {
261 # Create the size report which runs on the binaries.
262 pw_size_report(target_name) {
263 forward_variables_from(invoker, [ "title" ])
264 binaries = _size_report_binaries
265 }
266 } else {
267 # If no toolchains are listed in pw_size_report_toolchains, prevent GN from
268 # complaining about unused variables and run a script that outputs a ReST
269 # warning to the size report file.
270 not_needed("*")
271 not_needed(invoker, "*")
272
273 _doc_rst_output = "$target_gen_dir/$target_name.rst"
274 pw_python_script(target_name) {
275 metadata = {
276 pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir)
277 }
278 script = "$dir_pw_bloat/py/no_toolchains.py"
279 args = [ _doc_rst_output ]
280 outputs = [
281 _doc_rst_output,
282 ]
283 }
284 }
285}
Alexei Frolovf8259f62020-01-09 12:35:36 -0800286
287# A base_executable for the pw_toolchain_size_report template which contains a
288# main() function that loads the bloat_this_binary library and does nothing
289# else.
290pw_bloat_empty_base = {
291 deps = [
292 "$dir_pw_bloat:bloat_this_binary",
293 ]
294 sources = [
295 "$dir_pw_bloat/base_main.cc",
296 ]
297}