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