pw_bloat: Output binary_sizes.json with the single report flow

Change-Id: I38af4ae83f426252c52b6e3e11b9932f69ea3697
Bug: b/242339547, b/237344277
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/126935
Reviewed-by: Chris Kenyon <chriskenyon@google.com>
Reviewed-by: Alexei Frolov <frolv@google.com>
Commit-Queue: Chris Kenyon <chriskenyon@google.com>
diff --git a/pw_bloat/bloat.gni b/pw_bloat/bloat.gni
index bb0858a..b156b09 100644
--- a/pw_bloat/bloat.gni
+++ b/pw_bloat/bloat.gni
@@ -118,6 +118,7 @@
     ]
 
     _doc_rst_output = "$target_gen_dir/${target_name}"
+    _binary_sizes_output = "$target_gen_dir/${target_name}.binary_sizes.json"
 
     if (host_os == "win") {
       # Bloaty is not yet packaged for Windows systems; display a message
@@ -153,6 +154,7 @@
         ]
         outputs = [
           "${_doc_rst_output}.txt",
+          _binary_sizes_output,
           _doc_rst_output,
         ]
         deps = _all_target_dependencies + [ ":${target_name}.evaluate" ]
diff --git a/pw_bloat/py/pw_bloat/bloat.py b/pw_bloat/py/pw_bloat/bloat.py
index e821e93..d14c1c7 100755
--- a/pw_bloat/py/pw_bloat/bloat.py
+++ b/pw_bloat/py/pw_bloat/bloat.py
@@ -28,7 +28,7 @@
 import pw_cli.log
 
 from pw_bloat.bloaty_config import generate_bloaty_config
-from pw_bloat.label import DataSourceMap
+from pw_bloat.label import DataSourceMap, Label
 from pw_bloat.label_output import (
     BloatTableOutput,
     LineCharset,
@@ -39,6 +39,7 @@
 _LOG = logging.getLogger(__name__)
 
 MAX_COL_WIDTH = 50
+BINARY_SIZES_EXTENSION = '.binary_sizes.json'
 
 
 def parse_args() -> argparse.Namespace:
@@ -165,6 +166,22 @@
     _LOG.debug('Output written to %s', path)
 
 
+def create_binary_sizes_json(binary_name: str, labels: Iterable[Label]) -> str:
+    """Creates a binary_sizes.json file content from a list of labels.
+
+    Args:
+      binary_name: the single binary name to attribute segment sizes to.
+      labels: the label.Label content to include
+
+    Returns:
+      a string of content to write to binary_sizes.json file.
+    """
+    json_content = {
+        f'{binary_name} {label.name}': label.size for label in labels
+    }
+    return json.dumps(json_content, sort_keys=True, indent=2)
+
+
 def single_target_output(
     target: str,
     bloaty_config: str,
@@ -192,8 +209,9 @@
         DataSourceMap.from_bloaty_tsv(single_tsv), MAX_COL_WIDTH, LineCharset
     )
 
+    data_source_map = DataSourceMap.from_bloaty_tsv(single_tsv)
     rst_single_report = BloatTableOutput(
-        DataSourceMap.from_bloaty_tsv(single_tsv),
+        data_source_map,
         MAX_COL_WIDTH,
         AsciiCharset,
         True,
@@ -201,9 +219,19 @@
 
     single_report_table = single_report.create_table()
 
+    # Generates contents for top level summary for binary_sizes.json
+    binary_json_content = create_binary_sizes_json(
+        target, data_source_map.labels(ds_index=0)
+    )
+
     print(single_report_table)
     write_file(target_out_file, rst_single_report.create_table(), out_dir)
     write_file(f'{target_out_file}.txt', single_report_table, out_dir)
+    write_file(
+        f'{target_out_file}{BINARY_SIZES_EXTENSION}',
+        binary_json_content,
+        out_dir,
+    )
 
     return 0