Integrate bloat reports into docgen
This change updates the bloat script to output an RST version of its
report card table. Metadata is added to the bloat_report GN template
indicating its RST output, allowing it to be listed as a dependency of
pw_doc_group targets.
Change-Id: I3f098d352856a9dd8688bac44e3b60ddbb97a3a6
diff --git a/pw_bloat/BUILD.gn b/pw_bloat/BUILD.gn
index f5e97d3..2b460ef 100644
--- a/pw_bloat/BUILD.gn
+++ b/pw_bloat/BUILD.gn
@@ -13,6 +13,7 @@
# the License.
import("$dir_pw_build/pw_executable.gni")
+import("$dir_pw_docgen/docs.gni")
group("pw_bloat") {
deps = [
@@ -46,3 +47,10 @@
"base_main.cc",
]
}
+
+pw_doc_group("docs") {
+ sources = [
+ "bloat.rst",
+ ]
+ report_deps = [ "examples:simple_bloat" ]
+}
diff --git a/pw_bloat/bloat.gni b/pw_bloat/bloat.gni
index 07eb3da..95bbd6c 100644
--- a/pw_bloat/bloat.gni
+++ b/pw_bloat/bloat.gni
@@ -128,8 +128,13 @@
]
}
+ _doc_rst_output = "$target_gen_dir/${target_name}.rst"
+
# Create an action which runs the size report script on the provided targets.
pw_python_script(target_name) {
+ metadata = {
+ pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir)
+ }
script = "$dir_pw_bloat/py/bloat.py"
inputs = [
_bloaty_config,
@@ -138,7 +143,7 @@
]
outputs = [
"$target_gen_dir/${target_name}.txt",
- "$target_gen_dir/${target_name}.rst",
+ _doc_rst_output,
]
deps = _all_target_dependencies
args = _bloat_script_args + _binary_paths
diff --git a/pw_bloat/bloat.rst b/pw_bloat/bloat.rst
new file mode 100644
index 0000000..3e72a1f
--- /dev/null
+++ b/pw_bloat/bloat.rst
@@ -0,0 +1,31 @@
+.. _chapter-bloat:
+
+.. default-domain:: cpp
+
+.. highlight:: sh
+
+-----
+Bloat
+-----
+The bloat module provides tools to generate size report cards for output
+binaries.
+
+.. TODO(frolv): Explain how bloat works and how to set it up.
+
+Documentation integration
+=========================
+Bloat reports are easy to add to documentation files. All ``bloat_report``
+targets output a ``.rst`` file containing a tabular report card. This file
+can be imported directly into a documentation file using the ``include``
+directive.
+
+For example, the ``simple_bloat`` bloat report under ``//pw_bloat/examples``
+is imported into this file as follows:
+
+.. code:: rst
+
+ .. include:: examples/simple_bloat.rst
+
+Resulting in this output:
+
+.. include:: examples/simple_bloat.rst
diff --git a/pw_bloat/py/bloat.py b/pw_bloat/py/bloat.py
index 11e8c42..ad142de 100644
--- a/pw_bloat/py/bloat.py
+++ b/pw_bloat/py/bloat.py
@@ -146,18 +146,25 @@
print(f'{sys.argv[0]}: failed to run diff on {binary}',
file=sys.stderr)
+ def write_file(filename: str, contents: str) -> None:
+ path = os.path.join(args.out_dir, filename)
+ with open(path, 'w') as output_file:
+ output_file.write(contents)
+ print(f'Output written to {path}')
+
# TODO(frolv): Remove when custom output for full mode is added.
if not args.full:
out = bloat_output.TableOutput(
args.title, diffs, charset=bloat_output.LineCharset)
report.append(out.diff())
- with open(os.path.join(
- args.out_dir, f'{args.target}.txt'), 'w') as output_file:
- output_file.write('\n'.join(report))
- output_file.write('\n')
+ rst = bloat_output.RstOutput(diffs)
+ write_file(f'{args.target}.rst', rst.diff())
- print('\n'.join(report))
+ complete_output = '\n'.join(report)
+ write_file(f'{args.target}.txt', complete_output)
+ print(complete_output)
+
return 0
diff --git a/pw_bloat/py/bloat_output.py b/pw_bloat/py/bloat_output.py
index fdebe2e..35cd49d 100644
--- a/pw_bloat/py/bloat_output.py
+++ b/pw_bloat/py/bloat_output.py
@@ -16,7 +16,8 @@
import abc
import enum
-from typing import Collection, Dict, List, Optional, Tuple, Type, Union
+from typing import Callable, Collection, Dict, List, Optional, Tuple, Type
+from typing import TypeVar, Union
from binary_diff import BinaryDiff, FormattedDiff
@@ -71,6 +72,11 @@
HH = '═'
+def identity(val: str) -> str:
+ """Returns a string unmodified."""
+ return val
+
+
class TableOutput(Output):
"""Tabular output."""
@@ -80,8 +86,14 @@
title: Optional[str],
diffs: Collection[BinaryDiff] = (),
charset: Union[Type[AsciiCharset],
- Type[LineCharset]] = AsciiCharset):
+ Type[LineCharset]] = AsciiCharset,
+ preprocess: Callable[[str], str] = identity,
+ # TODO(frolv): Make this a Literal type.
+ justify: str = 'rjust'):
self._cs = charset
+ self._preprocess = preprocess
+ self._justify = justify
+
super().__init__(title, diffs)
def diff(self) -> str:
@@ -95,6 +107,7 @@
max_label = max(max_label, len(diff.label))
for segment in diff.formatted_segments():
for i, val in enumerate(segment):
+ val = self._preprocess(val)
column_widths[i] = max(column_widths[i], len(val))
separators = self._row_separators([max_label] + column_widths)
@@ -128,8 +141,9 @@
for segment in diff.formatted_segments():
subrow: List[str] = []
label = diff.label if not subrows else ''
- subrow.append(label.rjust(max_label, ' '))
- subrow.extend([val.rjust(column_widths[i], ' ')
+ subrow.append(getattr(label, self._justify)(max_label, ' '))
+ subrow.extend([getattr(self._preprocess(val),
+ self._justify)(column_widths[i], ' ')
for i, val in enumerate(segment)])
subrows.append(self._table_row(subrow))
@@ -184,4 +198,10 @@
"""Tabular output in ASCII format, which is also valid RST."""
def __init__(self, diffs: Collection[BinaryDiff] = ()):
- super().__init__(None, diffs, AsciiCharset)
+ # Use RST line blocks within table cells to force each value to appear
+ # on a new line in the HTML output.
+ def add_rst_block(val: str) -> str:
+ return f'| {val}'
+
+ super().__init__(None, diffs, AsciiCharset,
+ preprocess=add_rst_block, justify='ljust')
diff --git a/pw_docgen/py/docgen.py b/pw_docgen/py/docgen.py
index 63f844f..f1ff045 100644
--- a/pw_docgen/py/docgen.py
+++ b/pw_docgen/py/docgen.py
@@ -44,9 +44,9 @@
parser.add_argument('--sphinx-build-dir', type=str, required=True,
help='Directory in which to build docs')
parser.add_argument('--conf', type=str, required=True,
- 'Path to conf.py file for Sphinx')
+ help='Path to conf.py file for Sphinx')
parser.add_argument('--gn-root', type=str, required=True,
- 'Root of the GN build tree')
+ help='Root of the GN build tree')
parser.add_argument('--index', type=str, required=True,
help='Path to root index.rst file')
parser.add_argument('--out-dir', type=str, required=True,