blob: bb6a653c736399ff609c9af638631781e0e15d10 [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 -070015# gn-format disable
16import("//build_overrides/pigweed.gni")
17
Alexei Frolove2016762019-11-14 13:49:52 -080018import("$dir_pw_build/python_script.gni")
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 = []
37}
Alexei Frolove2016762019-11-14 13:49:52 -080038
39# Creates a target which runs a size report diff on a set of executables.
40#
41# Args:
Wyatt Hepler32d86bb2019-11-18 22:54:53 -080042# base: The default base executable target to run the diff against. May be
43# omitted if all binaries provide their own base.
Alexei Frolove2016762019-11-14 13:49:52 -080044# 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:
Alexei Frolov09447842019-11-15 15:09:05 -080055# pw_size_report("foo_bloat") {
Alexei Frolove2016762019-11-14 13:49:52 -080056# 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#
Alexei Frolov09447842019-11-15 15:09:05 -080070template("pw_size_report") {
Alexei Frolove4970e72020-06-11 13:55:29 -070071 if (pw_bloat_BLOATY_CONFIG != "") {
72 if (defined(invoker.base)) {
73 _global_base = invoker.base
74 _all_target_dependencies = [ _global_base ]
Wyatt Hepler32d86bb2019-11-18 22:54:53 -080075 } else {
Alexei Frolove4970e72020-06-11 13:55:29 -070076 _all_target_dependencies = []
Alexei Frolove2016762019-11-14 13:49:52 -080077 }
78
Alexei Frolove4970e72020-06-11 13:55:29 -070079 if (defined(invoker.title)) {
80 _title = invoker.title
Alexei Frolov3fde6b12019-12-18 16:13:38 -080081 } else {
Alexei Frolove4970e72020-06-11 13:55:29 -070082 _title = target_name
Alexei Frolov3fde6b12019-12-18 16:13:38 -080083 }
84
Alexei Frolove4970e72020-06-11 13:55:29 -070085 # 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.
Wyatt Hepler32d86bb2019-11-18 22:54:53 -080090
Alexei Frolove4970e72020-06-11 13:55:29 -070091 _binary_paths = []
92 _binary_labels = []
93 _bloaty_configs = []
Alexei Frolove2016762019-11-14 13:49:52 -080094
Alexei Frolove4970e72020-06-11 13:55:29 -070095 # 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 ]
Alexei Frolove2016762019-11-14 13:49:52 -0800101
Alexei Frolove4970e72020-06-11 13:55:29 -0700102 _target_dir = get_label_info(binary.target, "target_out_dir")
103 _target_name = get_label_info(binary.target, "name")
104 _binary_path = get_path_info(_target_dir, "abspath") + ":$_target_name"
Alexei Frolove2016762019-11-14 13:49:52 -0800105
Alexei Frolove4970e72020-06-11 13:55:29 -0700106 # If the binary defines its own base, use that instead of the global base.
107 if (defined(binary.base)) {
108 _binary_base = binary.base
109 _all_target_dependencies += [ _binary_base ]
110 } else if (defined(_global_base)) {
111 _binary_base = _global_base
112 } else {
113 assert(false, "pw_size_report requires a 'base' file")
114 }
115
116 # Allow each binary to override the global bloaty config.
117 if (defined(binary.bloaty_config)) {
118 _bloaty_configs += [ get_path_info(binary.bloaty_config, "abspath") ]
119 } else {
120 _bloaty_configs += [ get_path_info(pw_bloat_BLOATY_CONFIG, "abspath") ]
121 }
122
123 _base_dir = get_label_info(_binary_base, "target_out_dir")
124 _base_name = get_label_info(_binary_base, "name")
125 _binary_path += ";" + get_path_info(_base_dir, "abspath") + ":$_base_name"
126
127 _binary_paths += [ _binary_path ]
128 _binary_labels += [ binary.label ]
129 }
130
131 _bloat_script_args = [
132 "--bloaty-config",
133 string_join(";", _bloaty_configs),
134 "--out-dir",
135 target_gen_dir,
136 "--target",
137 target_name,
138 "--title",
139 _title,
140 "--labels",
141 string_join(";", _binary_labels),
Alexei Frolove2016762019-11-14 13:49:52 -0800142 ]
Alexei Frolove2016762019-11-14 13:49:52 -0800143
Alexei Frolove4970e72020-06-11 13:55:29 -0700144 if (defined(invoker.full_report) && invoker.full_report) {
145 _bloat_script_args += [ "--full" ]
Alexei Frolov3adcd672020-03-19 09:45:33 -0700146 }
Alexei Frolov844ff0f2020-05-06 12:15:29 -0700147
Alexei Frolove4970e72020-06-11 13:55:29 -0700148 if (defined(invoker.source_filter)) {
149 _bloat_script_args += [
150 "--source-filter",
151 invoker.source_filter,
152 ]
153 }
154
155 _doc_rst_output = "$target_gen_dir/${target_name}"
156
157 # TODO(frolv): Size reports are temporarily disabled pending the toolchain
158 # refactor.
159 if (true || host_os == "win") {
160 # Bloaty is not yet packaged for Windows systems; display a message
161 # indicating this.
162 not_needed("*")
163 not_needed(invoker, "*")
164
165 pw_python_script(target_name) {
166 metadata = {
167 pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir)
168 }
169 script = "$dir_pw_bloat/py/no_bloaty.py"
170 args = [ _doc_rst_output ]
171 outputs = [ _doc_rst_output ]
172 }
173
174 group(target_name + "_UNUSED_DEPS") {
175 deps = _all_target_dependencies
176 }
177 } else {
178 # Create an action which runs the size report script on the provided
179 # targets.
180 pw_python_script(target_name) {
181 metadata = {
182 pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir)
183 }
184 script = "$dir_pw_bloat/py/bloat.py"
185 inputs = [
186 "$dir_pw_bloat/py/binary_diff.py",
187 "$dir_pw_bloat/py/bloat_output.py",
188 ] + _bloaty_configs
189 outputs = [
190 "$target_gen_dir/${target_name}.txt",
191 _doc_rst_output,
192 ]
193 deps = _all_target_dependencies
194 args = _bloat_script_args + _binary_paths
195
196 # Print size reports to stdout when they are generated.
197 capture_output = false
198 }
Alexei Frolov844ff0f2020-05-06 12:15:29 -0700199 }
Alexei Frolov3adcd672020-03-19 09:45:33 -0700200 } else {
Alexei Frolove4970e72020-06-11 13:55:29 -0700201 not_needed(invoker, "*")
202 group(target_name) {
Alexei Frolov3adcd672020-03-19 09:45:33 -0700203 }
Alexei Frolove2016762019-11-14 13:49:52 -0800204 }
205}
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800206
207# Creates a report card comparing the sizes of the same binary compiled with
208# different toolchains. The toolchains to use are listed in the build variable
Alexei Frolov4c0428a2020-06-10 10:46:04 -0700209# pw_bloat_TOOLCHAINS.
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800210#
211# Args:
212# base_executable: Scope containing a list of variables defining an executable
213# target for the size report base.
214# diff_executable: Scope containing a list of variables defining an executable
215# target for the size report comparison.
216#
217# Outputs:
218# $target_gen_dir/$target_name.txt
219# $target_gen_dir/$target_name.rst
220#
221# Example:
222#
223# pw_toolchain_size_report("my_size_report") {
224# base_executable = {
225# sources = [ "base.cc" ]
226# }
227#
228# diff_executable = {
229# sources = [ "base_with_libfoo.cc" ]
230# deps = [ ":libfoo" ]
231# }
232# }
233#
234template("pw_toolchain_size_report") {
235 assert(defined(invoker.base_executable),
236 "pw_toolchain_size_report requires a base_executable")
237 assert(defined(invoker.diff_executable),
238 "pw_toolchain_size_report requires a diff_executable")
239
240 _size_report_binaries = []
241
242 # Multiple build targets are created for each toolchain, which all need unique
243 # target names, so throw a counter in there.
244 i = 0
245
246 # Create a base and diff executable for each toolchain, adding the toolchain's
247 # linker script to the link flags for the executable, and add them all to a
248 # list of binaries for the pw_size_report template.
Alexei Frolov4c0428a2020-06-10 10:46:04 -0700249 foreach(_toolchain, pw_bloat_TOOLCHAINS) {
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800250 _prefix = "_${target_name}_${i}_pw_size"
251
252 # Create a config which adds the toolchain's linker script as a linker flag
253 # if the toolchain provides one.
254 _linker_script_target_name = "${_prefix}_linker_script"
255 config(_linker_script_target_name) {
256 if (defined(_toolchain.linker_script)) {
257 ldflags = [ "-T" + rebase_path(_toolchain.linker_script) ]
Alexei Frolov47373492020-03-03 16:37:11 -0800258 inputs = [ _toolchain.linker_script ]
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800259 } else {
260 ldflags = []
261 }
262 }
263
264 # Create a group which forces the linker script config its dependents.
265 _linker_group_target_name = "${_prefix}_linker_group"
266 group(_linker_group_target_name) {
267 public_configs = [ ":$_linker_script_target_name" ]
268 }
269
270 # Define the size report base executable with the toolchain's linker script.
271 _base_target_name = "${_prefix}_base"
272 executable(_base_target_name) {
273 forward_variables_from(invoker.base_executable, "*")
274 if (!defined(deps)) {
275 deps = []
276 }
277 deps += [ ":$_linker_group_target_name" ]
278 }
279
280 # Define the size report diff executable with the toolchain's linker script.
281 _diff_target_name = "${_prefix}_diff"
282 executable(_diff_target_name) {
283 forward_variables_from(invoker.diff_executable, "*")
284 if (!defined(deps)) {
285 deps = []
286 }
287 deps += [ ":$_linker_group_target_name" ]
288 }
289
290 # Force compilation with the toolchain.
291 _base_label = get_label_info(":$_base_target_name", "label_no_toolchain")
292 _base_with_toolchain = "$_base_label(${_toolchain.target})"
293 _diff_label = get_label_info(":$_diff_target_name", "label_no_toolchain")
294 _diff_with_toolchain = "$_diff_label(${_toolchain.target})"
295
296 # Append a pw_size_report binary scope to the list comparing the toolchain's
297 # diff and base executables.
298 _size_report_binaries += [
299 {
300 base = _base_with_toolchain
301 target = _diff_with_toolchain
302 label = _toolchain.name
303
304 if (defined(_toolchain.bloaty_config)) {
305 bloaty_config = _toolchain.bloaty_config
306 }
307 },
308 ]
309
310 i += 1
311 }
312
Alexei Frolov844ff0f2020-05-06 12:15:29 -0700313 # TODO(frolv): Have a way of indicating that a toolchain should build docs.
314 if (current_toolchain == default_toolchain && _size_report_binaries != []) {
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800315 # Create the size report which runs on the binaries.
316 pw_size_report(target_name) {
317 forward_variables_from(invoker, [ "title" ])
318 binaries = _size_report_binaries
319 }
320 } else {
Alexei Frolov4c0428a2020-06-10 10:46:04 -0700321 # If no toolchains are listed in pw_bloat_TOOLCHAINS, prevent GN from
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800322 # complaining about unused variables and run a script that outputs a ReST
323 # warning to the size report file.
324 not_needed("*")
325 not_needed(invoker, "*")
326
Alexei Frolov725b85b2020-03-19 13:37:10 -0700327 _doc_rst_output = "$target_gen_dir/$target_name"
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800328 pw_python_script(target_name) {
329 metadata = {
330 pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir)
331 }
332 script = "$dir_pw_bloat/py/no_toolchains.py"
333 args = [ _doc_rst_output ]
Rob Mohra0ba54f2020-02-27 11:43:49 -0800334 outputs = [ _doc_rst_output ]
Alexei Frolov3fde6b12019-12-18 16:13:38 -0800335 }
336 }
337}
Alexei Frolovf8259f62020-01-09 12:35:36 -0800338
339# A base_executable for the pw_toolchain_size_report template which contains a
340# main() function that loads the bloat_this_binary library and does nothing
341# else.
342pw_bloat_empty_base = {
Wyatt Heplerc5e511e2020-06-12 16:56:22 -0700343 deps = [
344 "$dir_pw_bloat:base_main",
345 "$dir_pw_bloat:bloat_this_binary",
346 ]
Alexei Frolovf8259f62020-01-09 12:35:36 -0800347}