• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2024 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"""Rules to convert a binary blob into a C++ library."""
15
16load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "use_cpp_toolchain")
17load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
18load(
19    "//pw_build/bazel_internal:pigweed_internal.bzl",
20    _compile_cc = "compile_cc",
21)
22
23CcBlobInfo = provider(
24    "Input to pw_cc_blob_library",
25    fields = {
26        "alignas": "If present, the byte array is aligned as specified. The " +
27                   "value of this argument is used verbatim in an alignas() " +
28                   "specifier for the blob byte array.",
29        "file_path": "The file path for the binary blob.",
30        "linker_section": "If present, places the byte array in the specified " +
31                          "linker section.",
32        "symbol_name": "The C++ symbol for the byte array.",
33    },
34)
35
36def _pw_cc_blob_info_impl(ctx):
37    return [CcBlobInfo(
38        symbol_name = ctx.attr.symbol_name,
39        file_path = ctx.file.file_path,
40        linker_section = ctx.attr.linker_section,
41        alignas = ctx.attr.alignas,
42    )]
43
44pw_cc_blob_info = rule(
45    implementation = _pw_cc_blob_info_impl,
46    attrs = {
47        "alignas": attr.string(default = ""),
48        "file_path": attr.label(allow_single_file = True),
49        "linker_section": attr.string(default = ""),
50        "symbol_name": attr.string(),
51    },
52    provides = [CcBlobInfo],
53)
54
55def _pw_cc_blob_library_impl(ctx):
56    # Python tool takes a json file with info about blobs to generate.
57    blobs = []
58    blob_paths = []
59    for blob in ctx.attr.blobs:
60        blob_info = blob[CcBlobInfo]
61        blob_paths.append(blob_info.file_path)
62        blob_dict = {
63            "file_path": blob_info.file_path.path,
64            "linker_section": blob_info.linker_section,
65            "symbol_name": blob_info.symbol_name,
66        }
67        if (blob_info.alignas):
68            blob_dict["alignas"] = blob_info.alignas
69        blobs.append(blob_dict)
70    blob_json = ctx.actions.declare_file(ctx.label.name + "_blob.json")
71    ctx.actions.write(blob_json, json.encode(blobs))
72
73    hdr = ctx.actions.declare_file(ctx.attr.out_header)
74    src = ctx.actions.declare_file(ctx.attr.out_header.removesuffix(".h") + ".cc")
75
76    if (not ctx.attr.namespace):
77        fail("namespace required for pw_cc_blob_library")
78
79    args = ctx.actions.args()
80    args.add("--blob-file={}".format(blob_json.path))
81    args.add("--namespace={}".format(ctx.attr.namespace))
82    args.add("--header-include={}".format(ctx.attr.out_header))
83    args.add("--out-header={}".format(hdr.path))
84    args.add("--out-source={}".format(src.path))
85
86    ctx.actions.run(
87        inputs = depset(direct = blob_paths + [blob_json]),
88        progress_message = "Generating cc blob library for %s" % (ctx.label.name),
89        tools = [
90            ctx.executable._generate_cc_blob_library,
91        ],
92        outputs = [hdr, src],
93        executable = ctx.executable._generate_cc_blob_library,
94        arguments = [args],
95    )
96
97    include_path = ctx.bin_dir.path
98
99    # If workspace_root is set, this target is in an external workspace, so the
100    # generated file will be located under workspace_root.
101    if ctx.label.workspace_root:
102        include_path += "/" + ctx.label.workspace_root
103
104    # If target is not in root BUILD file of repo, append package name as that's
105    # where the generated file will end up.
106    if ctx.label.package:
107        include_path += "/" + ctx.label.package
108
109    return _compile_cc(
110        ctx,
111        [src],
112        [hdr],
113        deps = ctx.attr.deps,
114        alwayslink = ctx.attr.alwayslink,
115        includes = [include_path],
116        defines = [],
117    )
118
119pw_cc_blob_library = rule(
120    implementation = _pw_cc_blob_library_impl,
121    doc = """Turns binary blobs into a C++ library of hard-coded byte arrays.
122
123    The byte arrays are constant initialized and are safe to access at any time,
124    including before main().
125
126    Args:
127        ctx: Rule context.
128        blobs: A list of CcBlobInfo where each entry corresponds to a binary
129               blob to be transformed from file to byte array. This is a
130               required field. Blob fields include:
131
132               symbol_name [required]: The C++ symbol for the byte array.
133
134               file_path [required]: The file path for the binary blob.
135
136               linker_section [optional]: If present, places the byte array
137                in the specified linker section.
138
139               alignas [optional]: If present, the byte array is aligned as
140                specified. The value of this argument is used verbatim
141                in an alignas() specifier for the blob byte array.
142
143        out_header: The header file to generate. Users will include this file
144                    exactly as it is written here to reference the byte arrays.
145
146        namespace: The C++ namespace in which to place the generated blobs.
147        alwayslink: Whether this library should always be linked.
148    """,
149    attrs = {
150        "alwayslink": attr.bool(default = False),
151        "blobs": attr.label_list(providers = [CcBlobInfo]),
152        "deps": attr.label_list(default = [Label("//pw_preprocessor")]),
153        "namespace": attr.string(),
154        "out_header": attr.string(),
155        "_generate_cc_blob_library": attr.label(
156            default = Label("//pw_build/py:generate_cc_blob_library"),
157            executable = True,
158            cfg = "exec",
159        ),
160    },
161    provides = [CcInfo],
162    fragments = ["cpp"],
163    toolchains = use_cpp_toolchain(),
164)
165