Skip to content
File

Blob: build/wd_rust_crate.bzl

6.1 KB
1load("@rules_cc//cc:cc_library.bzl", "cc_library")
2load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test", "rust_unpretty")
3 
4def rust_cxx_bridge(
5 name,
6 src,
7 hdrs = [],
8 deps = [],
9 visibility = [],
10 strip_include_prefix = None,
11 include_prefix = None,
12 tags = [],
13 local_defines = [],
14 features = []):
15 native.genrule(
16 name = "%s/generated" % name,
17 srcs = [src],
18 outs = [
19 src + ".h",
20 src + ".cc",
21 ],
22 cmd = "$(location @workerd-cxx//:codegen) $(location %s) -o $(location %s.h) -o $(location %s.cc)" % (src, src, src),
23 tools = ["@workerd-cxx//:codegen"],
24 target_compatible_with = select({
25 "@//build/config:no_build": ["@platforms//:incompatible"],
26 "//conditions:default": [],
27 }),
28 )
29 cc_library(
30 name = name,
31 srcs = [src + ".cc"],
32 hdrs = [src + ".h"] + hdrs,
33 strip_include_prefix = strip_include_prefix,
34 include_prefix = include_prefix,
35 local_defines = local_defines,
36 features = features,
37 linkstatic = select({
38 "@platforms//os:windows": True,
39 "//conditions:default": False,
40 }),
41 deps = deps,
42 visibility = visibility,
43 tags = tags,
44 target_compatible_with = select({
45 "@//build/config:no_build": ["@platforms//:incompatible"],
46 "//conditions:default": [],
47 }),
48 )
49 
50def wd_rust_crate(
51 name,
52 cxx_bridge_src = None,
53 cxx_bridge_srcs = [],
54 deps = [],
55 proc_macro_deps = [],
56 data = [],
57 test_env = {},
58 test_tags = [],
59 test_deps = [],
60 test_proc_macro_deps = [],
61 test_size = "medium",
62 cxx_bridge_deps = [],
63 cxx_bridge_tags = [],
64 cxx_bridge_local_defines = [],
65 cxx_bridge_features = [],
66 visibility = None):
67 """Define rust crate.
68 
69 Args:
70 name: crate name.
71 cxx_bridge_src: (optional) .rs source file with cxx ffi bridge definition. The rule will
72 generation additional<name>@cxx c++ library with cxx bindings if this is set.
73 deps: crate dependencies: rust crates or c/c++ libraries.
74 visibility: crate visibility.
75 data: additional data files.
76 proc_macro_deps: proc_macro dependencies.
77 rustc_env: rustc environment variables,
78 test_env: additional test environment variable.
79 test_tags: additional test tags.
80 test_deps: test-only dependencies.
81 test_proc_macro_deps: test-only proc_macro dependencies.
82 test_size: Bazel test size (default "medium"); affects test timeout. Use "large" for
83 crates with many subtests, since RUST_TEST_THREADS=1 forces serial execution.
84 cxx_bridge_deps: either a flat dependency list applied to every bridge source, or a dict of
85 bridge source => dependency list.
86 """
87 srcs = native.glob(["**/*.rs"])
88 crate_name = name.replace("-", "_")
89 
90 if cxx_bridge_src:
91 cxx_bridge_srcs = cxx_bridge_srcs + [cxx_bridge_src]
92 
93 if type(cxx_bridge_deps) == "dict":
94 invalid_bridge_srcs = [bridge_src for bridge_src in cxx_bridge_deps.keys() if bridge_src not in cxx_bridge_srcs]
95 if invalid_bridge_srcs:
96 fail("cxx_bridge_deps contains keys not present in cxx_bridge_srcs: %s" % sorted(invalid_bridge_srcs))
97 else:
98 cxx_bridge_deps = {
99 bridge_src: cxx_bridge_deps + []
100 for bridge_src in cxx_bridge_srcs
101 }
102 
103 # Add cxx dependency if there are any cxx bridges
104 if len(cxx_bridge_srcs) > 0:
105 deps = deps + [
106 "@workerd-cxx//kj-rs",
107 "@workerd-cxx//:cxx",
108 ]
109 
110 include_prefix = "workerd/" + native.package_name().removeprefix("src/")
111 
112 hdrs = native.glob(["**/*.h"], allow_empty = True)
113 for bridge_src in cxx_bridge_srcs:
114 rust_cxx_bridge(
115 name = bridge_src + "@cxx",
116 src = bridge_src,
117 hdrs = hdrs,
118 include_prefix = include_prefix,
119 strip_include_prefix = "",
120 # Not applying visibility here – if you import the cxxbridge header, you will likely
121 # also need the rust library itself to avoid linker errors.
122 deps = cxx_bridge_deps.get(bridge_src, []) + [
123 "@workerd-cxx//kj-rs",
124 "@workerd-cxx//:cxx",
125 ],
126 tags = cxx_bridge_tags,
127 local_defines = cxx_bridge_local_defines,
128 features = cxx_bridge_features,
129 )
130 
131 for bridge_src in cxx_bridge_srcs:
132 deps.append(bridge_src + "@cxx")
133 
134 crate_features = []
135 
136 rust_library(
137 name = name,
138 crate_name = crate_name,
139 srcs = srcs,
140 deps = deps + ["@workerd//deps/rust:runtime"],
141 visibility = visibility,
142 data = data,
143 proc_macro_deps = proc_macro_deps,
144 crate_features = crate_features,
145 target_compatible_with = select({
146 "@//build/config:no_build": ["@platforms//:incompatible"],
147 "//conditions:default": [],
148 }),
149 )
150 
151 rust_test(
152 name = name + "_test",
153 crate = ":" + name,
154 env = {
155 "RUST_BACKTRACE": "1",
156 # rust test runner captures stderr by default, which makes debugging tests very hard
157 "RUST_TEST_NOCAPTURE": "1",
158 # our tests are usually very heavy and do not support concurrent invocation
159 "RUST_TEST_THREADS": "1",
160 } | test_env,
161 size = test_size,
162 tags = test_tags + ["no-coverage"],
163 crate_features = crate_features,
164 deps = test_deps,
165 proc_macro_deps = test_proc_macro_deps,
166 )
167 
168 if len(proc_macro_deps) + len(cxx_bridge_srcs) > 0:
169 rust_unpretty(
170 name = name + "@expand",
171 deps = [":" + name],
172 tags = ["manual", "off-by-default"],
173 )
174 
175 if len(test_proc_macro_deps) > 0:
176 rust_unpretty(
177 name = name + "_test@expand",
178 deps = [":" + name + "_test"],
179 tags = ["manual", "off-by-default"],
180 testonly = True,
181 )