Skip to content
File

Blob: build/wd_cc_embed.bzl

4.8 KB
1load("@bazel_skylib//rules:write_file.bzl", "write_file")
2load("//:build/wd_cc_library.bzl", "wd_cc_library")
3 
4# Normalizes an embed name constructed from the embed path.
5def 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.
9def 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.
17def 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}};
56size_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
67extern "C" {{
68#endif
69extern const {pod_data_type} {embed_name}_begin[];
70extern 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 )