File
Blob: build/wd_cc_embed.bzl
| 1 | load("@bazel_skylib//rules:write_file.bzl", "write_file") |
| 2 | load("//:build/wd_cc_library.bzl", "wd_cc_library") |
| 3 | |
| 4 | # Normalizes an embed name constructed from the embed path. |
| 5 | def normalize_embed_name(file_name): |
| 6 | return file_name.replace(".", "_").replace("-", "_").replace("/", "_").upper() |
| 7 | |
| 8 | # Converts file name or label into string of the corresponding embed name. |
| 9 | def wd_cc_embed_name(src): |
| 10 | embed_filename = native.package_relative_label(src).name |
| 11 | return normalize_embed_name(embed_filename) |
| 12 | |
| 13 | # A high-performance embed mechanism built upon C23. Prefer this over capnp embeds unless the embed is needed within capnp definitions. |
| 14 | # name: A valid C variable name. |
| 15 | # src: The name of the file to be embedded. |
| 16 | # Generates a ${name} library target and a ${base_name}.embed.h header for accessing the variable name as a const char array. |
| 17 | def wd_cc_embed(name, src, base_name = "", is_text = None, **kwargs): |
| 18 | embed_filename = native.package_relative_label(src).name |
| 19 | embed_package = native.package_relative_label(src).package |
| 20 | embed_name = normalize_embed_name(embed_filename) |
| 21 | |
| 22 | # construct a usable workspace root, e.g. external/workerd/ |
| 23 | workspace_root = native.package_relative_label(src).workspace_root |
| 24 | if (workspace_root != ""): |
| 25 | workspace_root += "/" |
| 26 | |
| 27 | # Heuristically determine if file is intended to be used as text or binary. |
| 28 | if is_text == None: |
| 29 | is_text = embed_filename.endswith(".txt") or embed_filename.endswith(".js") or embed_filename.endswith(".ts") |
| 30 | pod_data_type = "unsigned char" |
| 31 | data_type = "kj::ArrayPtr<const kj::byte>" |
| 32 | suffix = "" |
| 33 | size_adjust = "" |
| 34 | if is_text: |
| 35 | pod_data_type = "char" |
| 36 | data_type = "kj::StringPtr" |
| 37 | |
| 38 | # for text data, add terminating NUL byte, but don't count it to length so kj::StringPtr constructor works properly |
| 39 | suffix = " suffix(, 0)" |
| 40 | size_adjust = " - 1" |
| 41 | |
| 42 | # Optionally construct output file names from embed file name |
| 43 | if (base_name == ""): |
| 44 | base_name = embed_filename.replace(".", "_").replace("-", "_") |
| 45 | else: |
| 46 | embed_name = normalize_embed_name(base_name) |
| 47 | |
| 48 | write_file( |
| 49 | name = embed_filename + "@c", |
| 50 | out = base_name + ".embed.c", |
| 51 | content = ["""#include <stddef.h> |
| 52 | // Some data (including ICU embed) requires 8-byte alignment |
| 53 | __attribute__ ((aligned (8))) const {pod_data_type} {embed_name}_begin[] = {{ |
| 54 | #embed <{embed}>{suffix} |
| 55 | }}; |
| 56 | size_t {embed_name}_size = sizeof({embed_name}_begin){size_adjust}; |
| 57 | """.format(embed_name = embed_name, embed = embed_filename, pod_data_type = pod_data_type, suffix = suffix, size_adjust = size_adjust)], |
| 58 | ) |
| 59 | write_file( |
| 60 | name = embed_filename + "@h", |
| 61 | out = base_name + ".embed.h", |
| 62 | content = ["""#pragma once |
| 63 | #include <kj/common.h> |
| 64 | #include <kj/string.h> |
| 65 | #include <stddef.h> |
| 66 | #ifdef __cplusplus |
| 67 | extern "C" {{ |
| 68 | #endif |
| 69 | extern const {pod_data_type} {embed_name}_begin[]; |
| 70 | extern size_t {embed_name}_size; |
| 71 | #define {embed_name} ({data_type}({embed_name}_begin, {embed_name}_size)) |
| 72 | #ifdef __cplusplus |
| 73 | }} |
| 74 | #endif""".format(embed_name = embed_name, data_type = data_type, pod_data_type = pod_data_type)], |
| 75 | ) |
| 76 | |
| 77 | wd_cc_library( |
| 78 | name = name, |
| 79 | srcs = [base_name + ".embed.c"], |
| 80 | hdrs = [base_name + ".embed.h"], |
| 81 | additional_compiler_inputs = [src], |
| 82 | conlyopts = select({ |
| 83 | "@platforms//os:windows": [ |
| 84 | # Windows doesn't recognize C23 yet, and complains about C23 extensions even with clatest. |
| 85 | "/std:clatest", |
| 86 | "-Wno-c23-extensions", |
| 87 | "-Wno-unused-command-line-argument", |
| 88 | "/clang:--embed-dir=" + workspace_root + embed_package, |
| 89 | "/clang:--embed-dir=$(GENDIR)/" + workspace_root + embed_package, |
| 90 | ], |
| 91 | "//conditions:default": [ |
| 92 | # no need to have debug info for the embed |
| 93 | "-g0", |
| 94 | # C23 is needed for N3017 #embed. Clang also supports it in C++26 mode but it is not |
| 95 | # officially part of that standard so far; no harm in using C on a per-file basis. |
| 96 | "-std=c23", |
| 97 | # Hack: We need to provide the path to the embed file directory here. We can access |
| 98 | # the embed file easily by adding it to additional_compiler_inputs but can't easily |
| 99 | # get its directory within a macro – provide both the path of the package and its |
| 100 | # output directory to support both pre-existing and generated files. |
| 101 | # To support using the macro with external repositories, we also add the file's |
| 102 | # workspace root. |
| 103 | "--embed-dir=" + workspace_root + embed_package, |
| 104 | "--embed-dir=$(GENDIR)/" + workspace_root + embed_package, |
| 105 | ], |
| 106 | }), |
| 107 | deps = [ |
| 108 | "@capnp-cpp//src/kj", |
| 109 | ], |
| 110 | **kwargs |
| 111 | ) |