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