| # Copyright 2025 The Pigweed Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| """A rule for calculating the code size of a binary target. |
| """ |
| |
| load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain", "use_cpp_toolchain") |
| load("@rules_cc//cc/common:cc_common.bzl", "cc_common") |
| load("//pw_build:compatibility.bzl", "incompatible_with_mcu") |
| load("//pw_kernel/tooling:system_image.bzl", "SystemImageInfo") |
| load("//pw_toolchain/action:action_names.bzl", "PW_ACTION_NAMES") |
| |
| def _code_size_impl(ctx): |
| cc_toolchain = find_cpp_toolchain(ctx) |
| feature_configuration = cc_common.configure_features( |
| ctx = ctx, |
| cc_toolchain = cc_toolchain, |
| requested_features = ctx.features, |
| unsupported_features = ctx.disabled_features, |
| ) |
| readelf = cc_common.get_tool_for_action( |
| feature_configuration = feature_configuration, |
| action_name = PW_ACTION_NAMES.readelf, |
| ) |
| |
| output = ctx.actions.declare_file(ctx.attr.name + "_code_size.csv") |
| |
| binary_files = [] |
| for binary in ctx.attr.binaries: |
| binary_files.append(binary[SystemImageInfo].elf) |
| |
| cmd = """ |
| SECTIONS_TO_CHECK=( |
| ".code" |
| ".static_init_ram" |
| ".vector_table" |
| ) |
| |
| for ELF_FILE in "$@"; do |
| TOTAL_SIZE_BYTES=0 |
| for SECTION_NAME in "${{SECTIONS_TO_CHECK[@]}}"; do |
| SECTION_SIZE_BYTES=0 |
| HEX_SIZE=$({readelf} -S "${{ELF_FILE}}" 2>/dev/null | grep -E "[[:space:]]+${{SECTION_NAME}}[[:space:]]+" | awk '{{print $7}}') |
| if [ -n "$HEX_SIZE" ]; then |
| SECTION_SIZE_BYTES=$((0x$HEX_SIZE)) |
| else |
| SECTION_SIZE_BYTES=0 |
| fi |
| |
| TOTAL_SIZE_BYTES=$((TOTAL_SIZE_BYTES + SECTION_SIZE_BYTES)) |
| done |
| echo "${{ELF_FILE}},$TOTAL_SIZE_BYTES" >> {output} |
| done |
| """.format( |
| output = output.path, |
| readelf = readelf, |
| ) |
| |
| ctx.actions.run_shell( |
| inputs = depset( |
| direct = binary_files, |
| transitive = [ |
| cc_toolchain.all_files, |
| ], |
| ), |
| outputs = [output], |
| arguments = [f.path for f in binary_files], |
| command = cmd, |
| mnemonic = "CalculateCodeSize", |
| ) |
| |
| return [DefaultInfo(files = depset([output]))] |
| |
| _code_size = rule( |
| implementation = _code_size_impl, |
| toolchains = use_cpp_toolchain(), |
| fragments = ["cpp"], |
| attrs = { |
| "binaries": attr.label_list( |
| doc = "Binary files to calculate size of", |
| mandatory = True, |
| providers = [SystemImageInfo], |
| ), |
| }, |
| doc = "Save the code size of binaries to a csv.", |
| ) |
| |
| def code_size(name, binaries, **kwargs): |
| if kwargs.get("target_compatible_with") == None: |
| kwargs["target_compatible_with"] = incompatible_with_mcu() |
| |
| _code_size( |
| name = name, |
| binaries = binaries, |
| **kwargs |
| ) |