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